% Copyright (C) 2001-2023 Artifex Software, Inc.
% All Rights Reserved.
%
% This software is provided AS-IS with no warranty, either express or
% implied.
%
% This software is distributed under license and may not be copied,
% modified or distributed except as expressly authorized under the terms
% of the license contained in the file LICENSE in this distribution.
%
% Refer to licensing information at http://www.artifex.com or contact
% Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
% CA 94129, USA, for further information.
%

% Initialization file for Level 2 functions.
% When this is run, systemdict is still writable,
% but (almost) everything defined here goes into level2dict.

level2dict begin

% ------ System and user parameters ------ %

% User parameters must obey save/restore, and must also be maintained
% per-context.  We implement the former, and some of the latter, here
% with PostScript code.  NOTE: our implementation assumes that user
% parameters change only as a result of setuserparams -- that there are
% no user parameters that are ever changed dynamically by the interpreter
% (although the interpreter may adjust the value presented to setuserparams)
%
% There are two types of user parameters: those which are actually
% maintained in the interpreter, and those which exist only at the
% PostScript level.  We maintain the current state of both types in
% a read-only local dictionary named userparams, defined in systemdict.
% In a multi-context system, each context has its own copy of this
% dictionary.  In addition, there is a constant dictionary named
% psuserparams where each key is the name of a user parameter that exists
% only in PostScript and the value is a procedure to check that the value
% is legal: setuserparams uses this for checking the values.
% setuserparams updates userparams explicitly, in addition to setting
% any user parameters in the interpreter; thus we can use userparams
% to reset those parameters after a restore or a context switch.
% NOTE: the name userparams is known to the interpreter, and in fact
% the interpreter creates the userparams dictionary.

% Check parameters that are managed at the PostScript level.
/.checkparamtype {		% <newvalue> <type> .checkparamtype <bool>
  exch type eq
} .bind def
/.checksetparams {		% <newdict> <opname> <checkdict>
                                %   .checksetparams <newdict>
  2 .argindex {
                % Stack: newdict opname checkdict key newvalue
    3 copy 3 1 roll .knownget {
      exec not {
        pop pop pop load /typecheck signalerror
      } if
      dup type /stringtype eq {
        dup rcheck not {
          pop pop pop load /invalidaccess signalerror
        } if
      } if
    } {
      pop
    } ifelse pop pop
  } forall pop pop
} .bind def

% currentuser/systemparams creates and returns a dictionary in the
% current VM.  The easiest way to make this work is to copy any composite
% PostScript-level parameters to global VM.  Currently we have strings
% as well as arrays. For arrays, we also need to copy any contents that
% are in VM. Also copying string parameters insures the contents won't
% be changed. Also be careful to preserve 'executable' state.
/.copyparam {			% <value> .copyparam <value'>
  dup type /arraytype eq {
    .currentglobal //true .setglobal exch
    dup wcheck exch dup xcheck exch		% original attributes
    dup length array exch dup {	% stack: destination_array original_array original_array
      dup type /arraytype eq {
        dup 2 index ne {	% avoid recursion
          .copyparam	% recurse to handle composite array elements
        } {
          % this array self referenced, do it again (yuk!)
          pop 1 index		% get copy of destination array
        } ifelse
      } {
        dup type /stringtype eq {
          .copyparam
        } if
      }
      ifelse 3 1 roll		% keep arrays on top
    } forall pop astore
    exch { cvx } if		% set executable state
    exch not { readonly } if	% set readonly attribute as original
    exch .setglobal
  } if
  dup type /stringtype eq {
    dup wcheck exch	% save attr for setting readonly
    .currentglobal //true .setglobal
    1 index length string exch .setglobal
    copy exch not { readonly } if
  } if
} .bind odef

% Some user parameters are managed entirely at the PostScript level.
% We take care of that here.
systemdict begin
/psuserparams 48 dict def
/getuserparam {			% <name> getuserparam <value>
  /userparams .systemvar 1 .argindex get exch pop
} odef
% Fill in userparams (created by the interpreter) with current values.
mark .currentuserparams
counttomark 2 idiv {
  userparams 3 1 roll put
} repeat pop
/.definepsuserparam {		% <name> <value> .definepsuserparam -
  psuserparams 3 copy pop
  type cvlit //.checkparamtype /exec load 3 packedarray cvx put
  userparams 3 1 roll put
} .bind def
end
/currentuserparams {		% - currentuserparams <dict>
  /userparams .systemvar dup length dict .copydict
} odef
% We break out setuserparams into a separate procedure so that setvmxxx
% can use it without affecting the command in case of an error.
/.setuserparams2 {
        % Check that we will be able to set the PostScript-level
        % user parameters.
  /setuserparams /psuserparams .systemvar //.checksetparams exec
        % Set the C-level user params.  If this succeeds, we know that
        % the password check succeeded.
  dup .setuserparams
        % Now set the PostScript-level params.
        % The interpreter may have adjusted the values of some of the
        % parameters, so we have to read them back.
  dup {
    /userparams .systemvar 2 index known {
      psuserparams 2 index known not {
        pop dup .getuserparam
      } if
      .copyparam
      % special protection for the security related parameters
      [ /PermitFileReading /PermitFileWriting /PermitFileControl ]
      { 2 index eq { % force all strings to readonly but make sure the
                     % array is in the correct VM space (local/global).
        currentglobal exch dup gcheck setglobal
        dup length array exch { readonly exch } forall astore
        exch setglobal
        } if
      } forall
      % protect top level of parameters that we copied
      dup type dup /arraytype eq exch /stringtype eq or { readonly } if
      /userparams .systemvar 3 1 roll .forceput  % userparams is read-only
    } executeonly
    {
      pop pop
    } ifelse
  } executeonly forall
        % A context switch might have occurred during the above loop,
        % causing the interpreter-level parameters to be reset.
        % Set them again to the new values.  From here on, we are safe,
        % since a context switch will consult userparams.
  .setuserparams
} .bind executeonly odef % must be bound and hidden for .forceput

/setuserparams {		% <dict> setuserparams -
    {.setuserparams2} stopped
    {/setuserparams load $error /errorname get signalerror} if
} .bind odef
% Initialize user parameters managed here.
/JobName () .definepsuserparam

% Restore must restore the user parameters.
% (Since userparams is in local VM, save takes care of saving them.)
/restore {		% <save> restore -
  //restore /userparams .systemvar .setuserparams
} .bind odef

% The pssystemparams dictionary holds some system parameters that
% are managed entirely at the PostScript level.
systemdict begin
currentdict /pssystemparams known not {
  /pssystemparams 40 dict readonly def
} if
/getsystemparam {		% <name> getsystemparam <value>
  //pssystemparams 1 .argindex .knownget { exch pop } { .getsystemparam } ifelse
} odef
end
/currentsystemparams {		% - currentsystemparams <dict>
  mark .currentsystemparams //pssystemparams { } forall .dicttomark
} odef
/setsystemparams {		% <dict> setsystemparams -
        % Check that we will be able to set the PostScript-level
        % system parameters.
   dup pop		% check # of args
   /SAFETY .systemvar /safe get {
     % SAFER mode disallows some changes
     [ /GenericResourceDir /FontResourceDir /GenericResourcePathSep ] {
       2 copy .knownget {
         exch //pssystemparams exch .knownget {
           ne { /setsystemparams cvx /invalidaccess signalerror } if
         } {
           pop
         } ifelse
       } {
         pop
       } ifelse
     } forall
   } if
   /setsystemparams //pssystemparams mark exch {
     type cvlit //.checkparamtype /exec load 3 packedarray cvx
   } forall .dicttomark //.checksetparams exec
        % Set the C-level system params.  If this succeeds, we know that
        % the password check succeeded.
   dup .setsystemparams
        % Now set the PostScript-level params.  We must copy local strings
        % into global VM.
   dup
    { //pssystemparams 2 index known
       {		% Stack: key newvalue
         .copyparam
         % protect top level parameters that we copied
         dup type dup /arraytype eq exch /stringtype eq or { readonly } if
         //pssystemparams 3 1 roll .forceput	% pssystemparams is read-only
       } executeonly
       { pop pop
       }
      ifelse
    } executeonly
   forall pop
} .bind executeonly odef

% Initialize the passwords.
% NOTE: the names StartJobPassword and SystemParamsPassword are known to
% the interpreter, and must be bound to noaccess strings.
% The length of these strings must be max_password (iutil2.h) + 1.
/StartJobPassword 65 string noaccess def
/SystemParamsPassword 65 string noaccess def

% Redefine cache parameter setting to interact properly with userparams.
/setcachelimit {
    { mark /MaxFontItem 2 .argindex .dicttomark setuserparams pop }
  stopped
    {  /setcachelimit .systemvar $error /errorname get signalerror
    } if
} .bind odef
/setcacheparams {
        % The MaxFontCache parameter is a system parameter, which we might
        % not be able to set.  Fortunately, this doesn't matter, because
        % system parameters don't have to be synchronized between this code
        % and the VM.
  counttomark 1 add copy setcacheparams
  currentcacheparams	% mark size lower upper
    3 -1 roll pop
    /MinFontCompress 3 1 roll
    /MaxFontItem exch
  .dicttomark { setuserparams cleartomark } stopped {
    /setcacheparams .systemvar $error /errorname get signalerror
  } if
} .bind odef

% Add bogus user and system parameters to satisfy badly written PostScript
% programs that incorrectly assume the existence of all the parameters
% listed in Appendix C of the Red Book.  Note that some of these may become
% real parameters later: code near the end of gs_init.ps takes care of
% removing any such parameters from ps{user,system}params.

% psuserparams
  /MaxFormItem 100000 .definepsuserparam
  /MaxPatternItem 20000 .definepsuserparam
  /MaxScreenItem 48000 .definepsuserparam
  /MaxUPathItem 0 .definepsuserparam

% File Access Permission parameters
  .currentglobal //true .setglobal
  /.checkFilePermitparams {
    type /arraytype eq {
      currentuserparams /LockFilePermissions get {
        5 { pop } repeat /setuserparams cvx /invalidaccess signalerror
      }{
        % in addition to validating the value, ensure the value is read/only
        dup { readonly exch } forall
        .currentglobal exch dup gcheck .setglobal length array exch .setglobal
        astore readonly
      }
      ifelse
    } {
      5 { pop } repeat /setuserparams cvx /typecheck signalerror
    }
    ifelse
    //true
  } .bind def
% Initialize the File Permission access control to wide open
% These will only be accessed via current/set userparams.
% Values are a string containing multiple nul terminated path strings
  /PermitFileReading dup [ (*) ] .definepsuserparam
    psuserparams exch /.checkFilePermitparams load put
  /PermitFileWriting dup [ (*) ] .definepsuserparam
    psuserparams exch /.checkFilePermitparams load put
  /PermitFileControl dup [ (*) ] .definepsuserparam
    psuserparams exch /.checkFilePermitparams load put
  .setglobal

pssystemparams
dup /CurDisplayList 0 .forceput
dup /CurFormCache 0 .forceput
dup /CurInputDevice () .forceput
dup /CurOutlineCache 0 .forceput
dup /CurOutputDevice () .forceput
dup /CurPatternCache 0 .forceput
dup /CurUPathCache 0 .forceput
dup /CurScreenStorage 0 .forceput
dup /CurSourceList 0 .forceput
dup /DoPrintErrors //false .forceput
dup /JobTimeout 0 .forceput
dup /LicenseID (LN-001) .forceput     % bogus
dup /MaxDisplayList 140000 .forceput
dup /MaxFormCache 100000 .forceput
dup /MaxImageBuffer 524288 .forceput
dup /MaxOutlineCache 65000 .forceput
dup /MaxPatternCache 100000 .forceput
dup /MaxUPathCache 300000 .forceput
dup /MaxScreenStorage 84000 .forceput
dup /MaxSourceList 25000 .forceput
dup /PrinterName product .forceput
dup /RamSize 4194304 .forceput
    /WaitTimeout 40 .forceput

% Define the procedures for handling comment scanning.  The names
% %ProcessComment and %ProcessDSCComment are known to the interpreter.
% These procedures take the file and comment string and file as operands.
/.checkprocesscomment {
  dup //null eq {
    pop //true
  } {
    dup xcheck {
      type dup /arraytype eq exch /packedarraytype eq or
    } {
      pop //false
    } ifelse
  } ifelse
} .bind def
/ProcessComment //null .definepsuserparam
psuserparams /ProcessComment {//.checkprocesscomment exec} put
(%ProcessComment) cvn {
  /ProcessComment getuserparam
  dup //null eq { pop pop pop } { exec } ifelse
} bind def
/ProcessDSCComment //null .definepsuserparam
psuserparams /ProcessDSCComment {//.checkprocesscomment exec} put
/.loadingfont //false def
(%ProcessDSCComment) cvn {
  /ProcessDSCComment getuserparam
  dup //null eq .loadingfont or { pop pop pop } { exec } ifelse
} bind def

% ------ Miscellaneous ------ %

(<<) cvn			% - << -mark-
  /mark load def
% (>> is defined primitively.)
/languagelevel 2 def
% When running in Level 2 mode, this interpreter is supposed to be
% compatible with Adobe version 2017.
/version (2017) readonly def

% If binary tokens are supported by this interpreter,
% set an appropriate default binary object format.
/setobjectformat where
 { pop
   /RealFormat getsystemparam (IEEE) eq { 1 } { 3 } ifelse
   /ByteOrder getsystemparam { 1 add } if
   setobjectformat
 } if

% Aldus Freehand versions 2.x check for the presence of the
% setcolor operator, and if it is missing, substitute a procedure.
% Unfortunately, the procedure takes different parameters from
% the operator.  As a result, files produced by this application
% cause an error if the setcolor operator is actually defined
% and 'bind' is ever used.  Aldus fixed this bug in Freehand 3.0,
% but there are a lot of files created by the older versions
% still floating around.  Therefore, at Adobe's suggestion,
% we implement the following dreadful hack in the 'where' operator:
%      If the key is /setcolor, and
%        there is a dictionary named FreeHandDict, and
%        currentdict is that dictionary,
%      then "where" consults only that dictionary and not any other
%        dictionaries on the dictionary stack.
.wheredict /setcolor {
  /FreeHandDict .where {
    /FreeHandDict get currentdict eq {
      pop currentdict /setcolor known { currentdict //true } { //false } ifelse
    } {
      .where
    } ifelse
  } {
    .where
  } ifelse
} bind put

% ------ Virtual memory ------ %

/currentglobal			% - currentglobal <bool>
  /currentshared load def
/gcheck				% <obj> gcheck <bool>
  /scheck load def
/setglobal			% <bool> setglobal -
  /setshared load def
% We can make the global dictionaries very small, because they auto-expand.
/globaldict currentdict /shareddict .knownget not { 4 dict } if def
/GlobalFontDirectory SharedFontDirectory def

% VMReclaim and VMThreshold are user parameters.
/setvmthreshold {		% <int> setvmthreshold -
  mark /VMThreshold 2 .argindex .dicttomark {.setuserparams2} stopped
  {pop /setvmthreshold load $error /errorname get signalerror}
  {pop} ifelse
} odef
/vmreclaim {			% <int> vmreclaim -
  dup 0 gt {
    dup 2 le 1 index type /integertype eq and {
      pop    % ignore user requests for vmreclaim
             % (reclaim will still happen controlled by vmthreshold)
    }
    { .vmreclaim }    % let internal operator handle error conditions
    ifelse
  } {
    % VMReclaim userparam controls enable/disable GC
    mark /VMReclaim 2 index .dicttomark {.setuserparams2} stopped
    {pop /vmreclaim load $error /errorname get signalerror}
    {pop} ifelse
  } ifelse
} .bind executeonly odef
-1 setvmthreshold

% ------ IODevices ------ %

/.getdevparams where {
  pop /currentdevparams {	% <iodevice> currentdevparams <dict>
    .getdevparams .dicttomark
  } odef
} if
/.putdevparams where {
  pop /setdevparams {		% <iodevice> <dict> setdevparams -
    dup type /dicttype ne { /setdevparams .systemvar /typecheck signalerror } if
    mark 1 index { } forall counttomark 2 add index
    .putdevparams pop pop
  } odef
} if

% ------ Job control ------ %

serverdict begin

% We could protect the job information better, but we aren't attempting
% (currently) to protect ourselves against maliciousness.

/.jobsave //null def		% top-level save object
/.jobsavelevel 0 def		% save depth of job (0 if .jobsave is null,
                                % 1 otherwise)
/.adminjob //true def		% status of current unencapsulated job

end		% serverdict

% Because there may be objects on the e-stack created since the job save,
% we have to clear the e-stack before doing the end-of-job restore.
% We do this by executing a 2 .stop, which is caught by the 2 .stopped
% in .runexec; we leave on the o-stack a procedure to execute aftewards.
%
%**************** The definition of startjob is not complete yet, since
% it doesn't reset stdin/stdout.
/.startnewjob {			% <exit_bool> <password_level>
                                %   .startnewjob -
    serverdict /.jobsave get dup //null eq { pop } { restore } ifelse
    exch {
                        % Unencapsulated job
      serverdict /.jobsave //null put
      serverdict /.jobsavelevel 0 put
      serverdict /.adminjob 3 -1 roll 1 gt put
    } {
                        % Encapsulated job
      pop
      serverdict /.jobsave save put
      serverdict /.jobsavelevel 1 put
      .userdict /quit { stop } .bind put  % CET 28-10 requires a procedure
    } ifelse
                % Reset the interpreter state.
  clear cleardictstack
  initgraphics
  //false setglobal
} bind executeonly def
/.startjob {			% <exit_bool> <password> <finish_proc>
                                %   .startjob <ok_bool>
  vmstatus pop pop serverdict /.jobsavelevel get eq
  2 .argindex .checkpassword 0 gt and {
    exch .checkpassword exch count 3 roll count 3 sub { pop } repeat
    cleardictstack
                % Reset the e-stack back to the 2 .stopped in .runexec,
                % passing the finish_proc to be executed afterwards.
    2 .stop
  } {		% Password check failed
    pop pop pop //false
  } ifelse
} odef
/startjob {			% <exit_bool> <password> startjob <ok_bool>
        % This is a hack.  We really need some way to indicate explicitly
        % to the interpreter that we are under control of a job server.
  1 .argindex type /booleantype ne {
    /startjob .systemvar /typecheck signalerror
  } if
  { .startnewjob //true } .startjob
} odef

systemdict begin
/quit {				% - quit -
  //systemdict begin serverdict /.jobsave get //null eq
   { end //quit }
   { /quit .systemvar /invalidaccess /signalerror load end exec }
  ifelse
} bind odef
end

% We would like to define exitserver as a procedure, using the code
% that the Red Book says is equivalent to it.  However, since startjob
% resets the exec stack, we can't do this, because control would never
% proceed past the call on startjob if the exitserver is successful.
% Instead, we need to construct exitserver out of pieces of startjob.

serverdict begin

/exitserver {			% <password> exitserver -
  //true exch { .startnewjob } .startjob not {
    /exitserver cvx /invalidaccess signalerror
  } if
} bind def

end		% serverdict

% ------ Compatibility ------ %

% In Level 2 mode, the following replace the definitions that gs_statd.ps
% installs in statusdict and serverdict.
% Note that statusdict must be allocated in local VM.
% We don't bother with many of these yet.

% convenience function to make a dictionary from an object and a key
/.pair2dict { exch mark 3 1 roll .dicttomark } bind def

currentglobal //false setglobal 25 dict exch setglobal begin
currentsystemparams

% The following do not depend on the presence of setpagedevice.
/buildtime 1 index /BuildTime get def
% Also define /buildtime in systemdict because Adobe does so and some fonts use it as ID
systemdict /buildtime dup load put
/byteorder 1 index /ByteOrder get def
/checkpassword { .checkpassword 0 gt } bind def
dup /DoStartPage known
 { /dostartpage { /DoStartPage getsystemparam } bind def
   /setdostartpage { /DoStartPage //.pair2dict exec setsystemparams } bind def
 } if
dup /StartupMode known
 { /dosysstart { /StartupMode getsystemparam 0 ne } bind def
   /setdosysstart { { 1 } { 0 } ifelse /StartupMode //.pair2dict exec setsystemparams } bind def
 } if
%****** Setting jobname is supposed to set userparams.JobName, too.
/jobname { /JobName getuserparam } bind def
/jobtimeout { /JobTimeout getuserparam } bind def
/ramsize { /RamSize getsystemparam } bind def
/realformat 1 index /RealFormat get def
dup /PrinterName known
 { /setprintername { /PrinterName //.pair2dict exec setsystemparams } bind def
 } if
/printername
 { currentsystemparams /PrinterName .knownget not { () } if exch copy
 } bind def
currentuserparams /WaitTimeout known
 { /waittimeout { /WaitTimeout getuserparam } bind def
 } if

% The following do require setpagedevice.
/.setpagedevice where { pop } { (%END PAGEDEVICE) .skipeof } ifelse
/defaulttimeouts
 { currentsystemparams dup
   /JobTimeout .knownget not { 0 } if
   exch /WaitTimeout .knownget not { 0 } if
   currentpagedevice /ManualFeedTimeout .knownget not { 0 } if
 } bind def
/margins
 { currentpagedevice /Margins .knownget { exch } { [0 0] } ifelse
 } bind def
/pagemargin
 { currentpagedevice /PageOffset .knownget { 0 get } { 0 } ifelse
 } bind def
/pageparams
 { currentpagedevice
   dup /Orientation .knownget { 1 and ORIENT1 { 1 xor } if } { 0 } ifelse exch
   dup /PageSize get aload pop 3 index 0 ne { exch } if 3 2 roll
   /PageOffset .knownget { 0 get } { 0 } ifelse 4 -1 roll
 } bind def
/setdefaulttimeouts
 { exch mark /ManualFeedTimeout 3 -1 roll
   /Policies mark /ManualFeedTimeout 1 .dicttomark
   .dicttomark setpagedevice
   /WaitTimeout exch mark /JobTimeout 5 2 roll .dicttomark setsystemparams
 } bind def
/setduplexmode { /Duplex //.pair2dict exec setpagedevice } bind def
/setmargins
 { exch 2 array astore /Margins //.pair2dict exec setpagedevice
 } bind def
/setpagemargin { 0 2 array astore /PageOffset //.pair2dict exec setpagedevice } bind def
/setpageparams
 { mark /PageSize 6 -2 roll
   4 index 1 and ORIENT1 { 1 } { 0 } ifelse ne { exch } if 2 array astore
   /Orientation 5 -1 roll ORIENT1 { 1 xor } if
   /PageOffset counttomark 2 add -1 roll 0 2 array astore
   .dicttomark setpagedevice
 } bind def
/setresolution
 { count 1 lt { /setresolution cvx /stackunderflow signalerror } if
   dup type dup /integertype eq exch /realtype eq or not
   {
      /setresolution cvx /typecheck signalerror
   } if
   dup 2 array astore /HWResolution //.pair2dict exec { setpagedevice } stopped {
     pop /setresolution cvx $error /errorname get signalerror
   } if
 } bind def
%END PAGEDEVICE

% The following are not implemented yet.
%manualfeed
%manualfeedtimeout
%pagecount
%pagestackorder
%setpagestackorder

% -------- ICC manager -------- %
% All color management is going
% through ICC flow.  We need
% to have the default device
% spaces gray, RGB and CMYK
% defined by ICC profiles

//systemdict /ICCProfilesDir .knownget {
  % Set the directory sepcified by the command line option
  mark exch /ICCProfilesDir exch .dicttomark .setuserparams2
} {
  % First see if the current value is valid so we don't have to guess
  mark .currentuserparams .dicttomark /ICCProfilesDir get
  (default_gray.icc) concatstrings {status} //.internalstopped exec
  {pop //false} if
  {
    pop pop pop pop		% current value was OK. Just clean up stack
  } {
    % Search for valid (iccprofiles) directory as a sibling to (Resource)
    % and set it as a default if found.
    LIBPATH {
      (Resource) rsearch {
        exch pop exch pop (iccprofiles) concatstrings
        .file_name_separator concatstrings
        dup (default_gray.icc) concatstrings status {
          pop pop pop pop
          mark exch /ICCProfilesDir exch .dicttomark .setuserparams2
          exit
        } {
          pop
        } ifelse
      } {
        pop
      } ifelse
    } forall
  } ifelse 	% if currentuserparams ICCProfilesDir
} ifelse      % ICCProfilesDir set in systemdict (command line option)

mark	% collect dict key value pairs for anything set in systemdict (command line options)
[ /DefaultRGBProfile /DefaultGrayProfile /DefaultCMYKProfile /DeviceNProfile
  /NamedProfile /SourceObjectICC /OverrideICC
]
{ dup //systemdict exch .knownget not {
    pop		% discard keys not in systemdict
  } if
} forall
.dicttomark .setuserparams2

pop		% currentsystemparams

% Flag the current dictionary so it will be swapped when we
% change language levels.  (See zmisc2.c for more information.)
/statusdict currentdict def

currentdict end
currentdict exch /statusdict exch .forceput	% statusdict is local, systemdict is global

% The following compatibility operators are in systemdict.  They are
% defined here, rather than in gs_init.ps, because they require the
% resource machinery.

/devforall {		% <proc> <scratch> devforall -
  exch {
    1 index currentdevparams
    /Type .knownget { /FileSystem eq } { //false } ifelse
    { exec } { pop pop } ifelse
  } /exec load 3 packedarray cvx exch
  (*) 3 1 roll /IODevice resourceforall
} odef
/devstatus {		% <(%disk*%)> devstatus <searchable> <writable>
                        %   <hasNames> <mounted> <removable> <searchOrder>
                        %   <freePages> <size> true
                        % <string> devstatus false
  dup length 5 ge {
    dup 0 5 getinterval (%disk) eq {
      dup /IODevice resourcestatus {
        pop pop dup currentdevparams
        dup /Searchable get
        exch dup /Writeable get
        exch dup /HasNames get
        exch dup /Mounted get
        exch dup /Removable get
        exch dup /SearchOrder get
        exch dup /Free get
        exch /LogicalSize get
        9 -1 roll pop //true
      } {
        pop //false
      } ifelse
    } {
      pop //false
    } ifelse
  } {
    pop //false
  } ifelse
} odef

% ------ Color spaces ------ %
% gs_res.ps uses these entries in colorspacedict
% to populate the ColorSpaceFamily resource, so we need
% to add the supported spaces.
%
systemdict /colorspacedict get begin
/CIEBasedA [] def
/CIEBasedABC [] def
/DevicePixel [] def
/Indexed [] def
/Pattern [] def
/Separation [] def
end

% ------ CIE color rendering ------ %

% Define findcolorrendering and a default ColorRendering ProcSet.

/findcolorrendering {		% <intentname> findcolorrendering
                                %   <crdname> <found>
    % Adobe interpreters report /findcolorrendering (literal name), not the
    % operator itself, if an error occurs in findcolorrendering.
    /findcolorrendering {
        /ColorRendering /ProcSet findresource
        1 .argindex dup type /nametype eq { .namestring } if (.) concatstrings
        1 index /GetPageDeviceName get exec dup type /nametype eq { .namestring } if (.) concatstrings
        2 index /GetHalftoneName get exec dup type /nametype eq { .namestring } if
        concatstrings concatstrings cvn	% stack: intentname procset crdname
        dup /ColorRendering resourcestatus {
            pop pop exch pop exch pop //true
        } {
            pop /GetSubstituteCRD get exec //false
        } ifelse
    } .errorexec
} odef

5 dict dup begin

/GetPageDeviceName {		% - GetPageDeviceName <name>
  currentpagedevice dup /PageDeviceName .knownget {
    exch pop dup //null eq { pop /none } if
  } {
    pop /none
  } ifelse
} bind def

/GetHalftoneName {		% - GetHalftoneName <name>
  currenthalftone /HalftoneName .knownget not { /none } if
} bind def

/GetSubstituteCRD {		% <intentname> GetSubstituteCRD <crdname>
  pop /DefaultColorRendering
} bind def

end
% The resource machinery hasn't been activated, so just save the ProcSet
% and let .fixresources finish the installation process.
/ColorRendering exch def

% Define setcolorrendering.

/.colorrenderingtypes 5 dict def

/setcolorrendering {		% <crd> setcolorrendering -
  dup /ColorRenderingType get
  dup type /integertype ne {
    /setcolorrendering .systemvar /typecheck signalerror
  } if
  //.colorrenderingtypes exch .knownget {
     exec
  } {
    /setcolorrendering .systemvar /rangecheck signalerror
  } ifelse
} odef

/.setcolorrendering1 where { pop } { (%END CRD) .skipeof } ifelse

.colorrenderingtypes 1 {
  % Adobe ProcSet "Adobe_AGM_Core 2.0 0" places an /Intent key into CRD's
  dup /Intent .knownget {
    //.renderingintentdict exch .knownget { .setrenderingintent } if
  } if
  dup .buildcolorrendering1 .setcolorrendering1
} .bind put

% Note: the value 101 in the next line must be the same as the value of
% GX_DEVICE_CRD1_TYPE in gscrdp.h.
.colorrenderingtypes 101 {
  dup .builddevicecolorrendering1 .setdevicecolorrendering1
} .bind put

% sRGB output CRD, D65 white point
mark
/ColorRenderingType 1
/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ] readonly

% Bradford Cone Space
/MatrixPQR [ 0.8951 -0.7502  0.0389
             0.2664  1.7135 -0.0685
            -0.1614  0.0367  1.0296] readonly

/MatrixLMN [ 3.240449 -0.969265  0.055643
            -1.537136  1.876011 -0.204026
            -0.498531  0.041556  1.057229 ] readonly

% Inverse sRGB gamma transform
/EncodeABC [ { dup 0.00304 le
                { 12.92321 mul }
                { 1 2.4 div exp 1.055 mul 0.055 sub }
               ifelse
             } bind dup dup
           ] readonly

/WhitePoint [ 0.9505 1 1.0890 ] readonly % D65
/BlackPoint [ 0 0 0 ] readonly

% VonKries-like transform in Bradford Cone Space
   /TransformPQR
     % The implementations have been moved to C for performance.
     [ { .TransformPQR_scale_WB0 } bind
       { .TransformPQR_scale_WB1 } bind
       { .TransformPQR_scale_WB2 } bind
     ] readonly
.dicttomark setcolorrendering

%END CRD

% Initialize a CIEBased color space for sRGB.
/CIEsRGB [ /CIEBasedABC
  mark
    /DecodeLMN [ {
      dup 0.03928 le { 12.92321 div } { 0.055 add 1.055 div 2.4 exp } ifelse
    } bind dup dup ] readonly
    /MatrixLMN [
      0.412457 0.212673 0.019334
      0.357576 0.715152 0.119192
      0.180437 0.072175 0.950301
    ] readonly
    /WhitePoint [0.9505 1.0 1.0890] readonly
  .dicttomark readonly
] readonly def

% Special type to install
% sRGB ICC profile color space
/CIEsRGBICC [ /ICCBased
  mark
    /N 3
    /DataSource (srgb)
    /Alternate [/DeviceRGB]
    /Name (srgb)
  .dicttomark
] def

% Special type to install
% sGray ICC profile color space
/CIEsGRAYICC [ /ICCBased
  mark
    /N 1
    /DataSource (sgray)
    /Alternate [/DeviceGray]
    /Name (sgray)
  .dicttomark
] def

% Special type to install
% e-sRGB ICC profile color space
/CIEesRGBICC [ /ICCBased
  mark
    /N 3
    /DataSource (esrgb)
    /Alternate [/DeviceRGB]
    /Name (esrgb)
  .dicttomark
] def

% Special type to install
% rommRGB ICC profile color space
/CIErommRGBICC [ /ICCBased
  mark
    /N 3
    /DataSource (rommrgb)
    /Alternate [/DeviceRGB]
    /Name (rommrgb)
  .dicttomark
] def

% ------ Painting ------ %

% A straightforward definition of execform that doesn't actually
% do any caching.
/.execform1 {
        % This is a separate operator so that the stacks will be restored
        % properly if an error occurs.
  %% High level forms need the CTM before the Form Matrix is applied
  /UNROLLFORMS where {/UNROLLFORMS get}{//false}ifelse not
  {matrix currentmatrix exch} if
  dup /Matrix get concat
  dup /BBox get aload pop
  exch 3 index sub exch 2 index sub rectclip
  dup /PaintProc get
  1 index /Implementation known not {
    1 index dup /Implementation //null .forceput readonly pop
  } executeonly if
  /UNROLLFORMS where {/UNROLLFORMS get}{//false}ifelse not
  %% [CTM] <<Form>> PaintProc .beginform -
  {
    %% First,, check to see if we have no /Implementation already defined (see above)
    1 index /Implementation get //null eq
    {
      %% We don't, so copy the form dictionary
      1 index 4 1 roll
      %% tell devices we're starting a form, run the PaintProc, and then tell the devices we've finished
      3 -1 roll 2 index
      .beginform exec

      %% This is all horrible code to deal with illegal forms which leave junk on the operand stack
      %% Starting from 1 (not 0, we don't want to look at the loop count) and going up to the
      %% number of objects on the stack, check each object to see if its the Form dict (it is
      %% *supposed* to be the top object after executing the PaintProc). I don't currently check to see if
      %% the PaintProc ate the dict, but if there are extra objects, remove them and tell
      %% the user.
      %%
      count 1 1 3 -1 roll {
        dup index
        dup type /dicttype eq {
          /Implementation known {
            2 sub 0 1 3 -1 roll {
              QUIET not {
                 (\n  WARNING - Form PaintProc left operands on the stack after execution.\n        This is technically illegal and these have been removed, \n        if output is incorrect run again with -dUNROLLFORMS.\n) =
              } if
              pop pop
            } for
            exit
          }if
        } {
          pop pop
        }ifelse
      } for

      .endform
      %% Ask devices if they have cached the form, and what ID to use if so
      %% returning -1 means 'no ID'
      .get_form_id dup -1 eq
      {pop pop}
      {
        %% The form is cached with a specific ID. Make a dictionary (which we'll store in the
        %% Form dictioanry using the /Implementation key).
        1 dict dup /FormID 4 -1 roll put
        1 index exch /Implementation exch .forceput readonly pop
      } executeonly
      ifelse
    }
    {
      %% We have a (non-null) Implementation, get the dictionary and pull the
      %% FormID key from it, then tell the device to use the stored form with the
      %% specified key.
      pop dup /Implementation get /FormID get .repeatform
    }ifelse
  }
  {exec} ifelse
} .bind odef	% must bind .forceput

/.formtypes 5 dict
  dup 1 /.execform1 load put
def

/execform {			% <form> execform -
  gsave {
    dup /FormType get //.formtypes exch get exec
  } stopped grestore { stop } if
} odef

/.patterntypes 5 dict
  dup 1 /.buildpattern1 load put
def

/makepattern {			% <proto_dict> <matrix> makepattern <pattern>
  dup type /dicttype eq {
     % "<dict> makepattern" reports /typecheck on Adobe
     /makepattern .systemvar /typecheck signalerror
  } if
  //.patterntypes 2 .argindex /PatternType get .knownget not {
      /makepattern .systemvar /rangecheck signalerror
  } if
  .currentglobal //false .setglobal exch
                % Stack: proto matrix global buildproc
  3 index dup length 1 add dict .copydict
                % Stack: proto matrix global buildproc newdict
  3 index 3 -1 roll exec
                % Stack: proto matrix global newdict instance
  % Create an 'Implementation' entry for the pattern dict.  The PRLM 3rd says
  % this about the contents of Implementation:  "The type and value of this
  % entry are implementation-dependent."  The CET (page 2 of 18-02f) expects
  % that this entry be an array and that the second element of the array be a
  % gstate. We put our pattern instance struct into the first element of the
  % array.
  1 index /Implementation 3 -1 roll
  .getCPSImode { gstate } { //null } ifelse 2 array astore
  put				% put Implementation into the pattern dict.
                % Stack: proto matrix global newdict
  readonly exch .setglobal exch pop exch pop
} odef

/setpattern {			% [<comp1> ...] <pattern> setpattern -
  { currentcolorspace 0 get /Pattern ne {
      [ /Pattern currentcolorspace ] setcolorspace
    } if setcolor
  } stopped {
    /setpattern .systemvar $error /errorname get signalerror
  } if
} odef

% The following functions emulate the actions of findcmykcustomcolor and
% setcustomcolor.  These functions are described in Adobe's TN 5044.  That
% same document also says "The following "operators" are not defined in the
% PostScript Language Reference Manual, but should be used as pseudo-operators
% in your PostScript language output. Separation applications from Adobe
% Systems and other vendors will redefine these convention operators to
% separate your documents.  Your application should conditionally define
% procedures with these special names, as shown later in this document."
%
% We are providing these functions because we have found files created by
% "QuarkXPress: pictwpstops filter 1.0" which produce bad shading dictionaries
% if these operators are not defined.

% Also we add a findrgbcustomcolor that was discovered in Adobe Illustrator
% AI5 (Adobe Illustrator (R) Version 7.0 Full Prolog) ProcSet that allows
% us to create Separation colorspace with a /DeviceRGB tint transform.

% Conditionally disable the TN 5044 psuedo-ops if NO_TN5044 specified
/NO_TN5044 where { pop (%END TN 5044 psuedo-ops) .skipeof } if

% TN 5044 does not define the contents of the array.  We are simply putting
% the values given into an array.  This is consistent with what we see when
% testing with Adobe Distiller 6.0.
%   <cyan> <magenta> <yellow> <black> <key> findcmykcustomcolor <array>
/findcmykcustomcolor { 5 array astore } bind executeonly def
% The following isn't documented by Adobe, but was found in Adobe Illustrator (R)
% Version 7.0 Full Prolog
/findrgbcustomcolor { 4 array astore } bind executeonly def

% Build a tint transform function for use by setcustomcolor.  This function
% is for a Separation color space which has either a DeviceCMYK base color space
% (i.e. 1 input and 4 outputs) or a DeviceRGB colorspace (1 in, 3 out).
% The input to buildcustomtinttransform is the array created by findcmykcustomcolor
% and the length of the array is used to determine the alternate colorspace.
% The resulting function for CMYK is:
%   { dup cyan mul exch dup magenta mul exch dup yellow mul exch black mul }
%   Where cyan, magenta, yellow, and black are values from the array.
% For RGB, since the resulting function is:
%    { dup red mul exch dup green mul exch blue mul }
/buildcustomtinttransform	% <array> buildcustomtinttransform <function>
{
  dup length 5 eq {
  % CMYK
    [ /dup load 2 index 0 get /mul load
      /exch load /dup load 6 index 1 get /mul load
      /exch load /dup load 10 index 2 get /mul load
      /exch load 13 index 3 get /mul load
    ]
  } {
    % RGB is assumed
    [ /dup load 2 index 0 get /mul load
      /exch load /dup load 6 index 1 get /mul load
      /exch load 9 index 2 get /mul load
    ]
  } ifelse
  cvx bind exch pop		%  Make executable and remove the input array
} bind executeonly def

% Construct the colorspace array to be used by setcolorspace from the array
% result of either findcmykcustomcolor or findrgbcustomcolor.
/buildcolorspacearray	% <array> buildcustomtinttransform <colorspace_array>
{ % as with buildcustomtinttransform, the length of the array is used to
  % determine the alternate colorspace
  dup length 5 eq {
    % Start building Separation colorspace with CMYK alternate
    [ /Separation 2 index 4 get	% Get separation name from array's key
      /DeviceCMYK
      4 index //buildcustomtinttransform exec % build the tint transform function
    ]
  } {
    [ /Separation 2 index 3 get	% Get separation name from array's key
      /DeviceRGB
      4 index //buildcustomtinttransform exec % build the tint transform function
    ]
  } ifelse
  exch pop		% remove the input array
} bind executeonly def

% Set a custom color based upon a tint and array which describes the custom
% color.  See findcmykcustomcolor.  First we create and then set a Separation
% colorspace.  Then we set the specified color.
% Note that older Adobe ProcSets apparently allow for 'null' as the tint
% for some reason, so an alternate operational mode is tolerated:
% 					    null setcustomcolor -
/setcustomcolor			% <array> <tint> setcustomcolor -
{
  dup //null eq {
    pop pop
  }{
    % Check that the tint is a number between 0 and 1
    dup type dup /integertype eq exch /realtype eq or not {
      /setcustomcolor cvx /typecheck signalerror
    } if
    % Bug 703869. Apparently Adobe (at least Acrobat Pro) silently clamps values
    dup 1 le not {
      pop 1
    } if
    dup 0 ge not {
      pop 0
    } if

    % The array is supposed to be the result of fundcmykcustomcolor. Our
    % implementation just pushes all the arguments into the array and that's
    % what buildcolorspacearray expects. So check that now.
    % Starting with the first N-1 elemenst which must be numbers where 0 <= x <= 1
    1 index
    0 1 2 index length 2 sub
    {
      1 index exch get dup
      type dup /integertype eq exch /realtype eq or not {
        /setcustomcolor cvx /typecheck signalerror
      } if
      dup
      1 le not {
        /setcustomcolor cvx /rangecheck signalerror
      } if
      0 ge not {
        /setcustomcolor cvx /rangecheck signalerror
      } if
    } for

    % Finally, check the last element of the array, which must be a string.
    dup length 1 sub get type /stringtype eq not {
      /setcustomcolor cvx /typecheck signalerror
    } if

    exch //buildcolorspacearray exec
    setcolorspace			% Set the Separation color space as current
    setcolor			% Set the tint as the current color
  } ifelse
} bind executeonly def

% This proc is supposed to implement a version of overprinting. TN 5044 says
% that this proc is not used by any shipping host-based application. We have
% only found it being used in a proc set in files by Canvas from Deneba Systems.
% Even their proc set does not actually do any overprinting.  However their
% files crash if this is not defined.  Thus we have a copy of this proc but
% we are simply checking for inputs being -1 and if so then we set the value
% to 0.
/setcmykoverprint {
  4 { dup -1 eq { pop 0 } if 4 1 roll } repeat setcmykcolor
} bind def

/separation_all [/Separation /All /DeviceCMYK { dup dup dup } bind ] readonly def

% Collect the arguments into the image dictionary
% <width> <height> <bits/sample> <matrix> <proc> args2dict <dict>
/args2dict {
  10 dict begin
  {1 0} 1
  { /ImageType /Decode /DataSource /ImageMatrix /BitsPerComponent /Height /Width
  } { exch def } forall
  currentdict end
} bind def

% Prints (1-gray) on all separations.
% <gray> setseparationgray -
/setseparationgray {
  //separation_all setcolorspace
  1 exch sub setcolor
} bind def

% Renders an image whose sample values specify the amount of the custom color.
% <width> <height> <bits/sample> <matrix> <proc> <array> customcolorimage -
/customcolorimage {
  gsave
  //buildcolorspacearray exec setcolorspace
  //args2dict exec image
  grestore
} bind def

% Renders an image on all process and custom color plates.
% <width> <height> <bits/sample> <matrix> <proc>
/separationimage {
  gsave
  //separation_all setcolorspace
  //args2dict exec image
  grestore
} bind def

{ /buildcustomtinttransform /buildcolorspacearray /separation_all /args2dict }
{ currentdict exch undef } forall

%END TN 5044 psuedo-ops

end				% level2dict

% undefine things defined in this file and not referenced elsewhere
[
    /.checkprocesscomment
    /.pair2dict
    /.setcolorrendering1
    /.checkparamtype
    /.checksetparams
] dup level2dict .undefinternalnames
systemdict .undefinternalnames
