% 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.
%

% Redefine Font and CIDFont categories with FAPI-handeled fonts.

systemdict /.FAPIavailable known { //null .FAPIavailable } { //false } ifelse not {
  (%END FAPI) .skipeof
} if

languagelevel 2 .setlanguagelevel

%====================================================================
% Redefine Font category with FAPIfontmap and CIDFont with FAPIfontmap :
15 dict begin    % a temporary dictionary for local binding.

/EmbedFontObjectsQuery mark
  /.EmbedFontObjects 0
.dicttomark def

/is_device_compatible_to_FAPI    % - is_device_compatible_to_FAPI <bool>
{
  //true % removed a bogus check against EmbedFontObjectsQuery
  %
  % Temporary switch to allow override of FAPI and fallback to GS font rendering
  % to be removed at some date after FT integration is completed and released.
  %
  /DisableFAPI where {
  /DisableFAPI get not and
  }if
  % The code above assumes that only the requested parameter is rendered.
  % The commented-out code below may be useful for general case.
  % Keeping it for a while.
  % counttomark 2 idiv {
  %   exch /.EmbedFontObjects eq {
  %     counttomark 1 add 1 roll cleartomark
  %     0 eq exit
  %   } if
  % } repeat
  % dup mark eq {
  %   pop //true
  % } if
} .internalbind def

%----------------------------- Process FAPIconfig -----------------------

% The HookDiskFonts and HookEmbeddedFonts take a simple array setting normally.
% but if two or more FAPI plugins are built in, they can take a dictionary,
% whose contents are the FAPI subtype string of a given plugin as the key,
% and an array containing the (sub)set of font types the plugin should handle.
% Any repetition of font types is ill advised since the resulting bevhaviour
% will depend on the order entries are stored in the dictionary, which is
% indeterminate. An attempt to use request a font scaler/renderer which cannot
% deal with a given font type will result in the FAPI resorting to it's default
% search for a viable plugin.
%
% As an example, you might want UFST to handle Truetype/Type 42 fonts, and
% Freetype to handle others, thus:
% /HookDiskFonts << /UFST [11 42] /FreeType  [1 2 9] >>
% /HookEmbeddedFonts << /UFST [11 42] /FreeType  [1 2 9] >>

/Config
<<
  % If we've got a FAPIConfig, run it now.
  systemdict /FAPIconfig known
  {
    /FAPIconfig .systemvar .runlibfile
  } if

  % Now setup defaults for any entries not set above - these defaults are correct
  % for FAPI/Freeetype
  currentdict /FontPath known not { /FontPath (/Fonts)} if			% A default directory for FAPI-handled font files
                                                                        % path in FAPIfontmap.
  currentdict /CIDFontPath known not { /CIDFontPath (/CIDFonts)} if		% A default directory for FAPI-handled CIDfont 							   % files path in FAPIcidfmap.
  currentdict /HookDiskFonts known not { /HookDiskFonts [1 2 9 11 42] } if		% FontType values for disk PS fonts to be
                                                                        % redirected to FAPI.
  currentdict /HookEmbeddedFonts known not { /HookEmbeddedFonts [1 2 9 11 42] } if	% FontType values for embedded PS fonts to be
                                                                        % redirected to FAPI.
  /ServerOptions 2 dict
>> def
systemdict /.FAPIconfig //Config put

/UFST .FAPIavailable
{
  systemdict /UFST_SSdir known
  {
      /UFSTFONTDIR UFST_SSdir def
      systemdict /UFST_SSdir undef
  }
  {
    /UFSTROMFONTDIR (%rom%fontdata/) def

    UFSTROMFONTDIR (mtfonts/pcl45/mt3/plug__xi.fco) 
    concatstrings status
    {
       pop pop pop pop
       /UFSTFONTDIR UFSTROMFONTDIR def
    }
    {
      /UFSTFONTDIR () def
    } ifelse
  } ifelse

  systemdict /UFST_PlugIn known not
  {
    systemdict /UFST_PlugIn UFSTFONTDIR (mtfonts/pcl45/mt3/plug__xi.fco) concatstrings put
  } if

  systemdict /FCOfontfile known not
  {
    systemdict /FCOfontfile UFSTFONTDIR (mtfonts/pclps2/mt3/pclp2_xj.fco) concatstrings put
  } if

  systemdict /FCOfontfile2 known not
  {
    systemdict /FCOfontfile2 UFSTFONTDIR (mtfonts/pcl45/mt3/wd____xh.fco) concatstrings put
  } if

  systemdict /FAPIfontmap known not
  {
    systemdict /FAPIfontmap (FCOfontmap-PCLPS2) put
  } if
} if

()
systemdict /UFST_SSdir .knownget {
  (UFST_SSdir=) exch concatstrings concatstrings
} if
systemdict /UFST_PlugIn .knownget {
  1 index length 0 ne {
    exch .filenamelistseparator concatstrings exch
  } if
  (UFST_PlugIn=) exch concatstrings concatstrings
} if
dup length 0 ne {
  //Config /ServerOptions get exch /UFST exch put
} {
  pop
} ifelse

%------------------Copy the FontEmulationProcs here : -------------------

/FontEmulationProcs /ProcSet findresource {
  def
} forall

currentdict /super.complete_instance currentdict /complete_instance get put

%-----------FAPI-specific methods for category redefinition : -----------

/RefinePath      % <FontDict> /key RefinePath <FontDict>
{ exch begin
  //Config exch get
  /Path exch
  Path //false .file_name_combine not {
    exch
    (Can't combine paths ) print print ( and ) print =
    /RefinePath cvx /configurationerror signalerror
  } if
  def
  currentdict end
} .internalbind def

/complete_instance  % <font_name> <FontDict> <Options> complete_FAPI_Font <font_name> <FontDict>
{ //super.complete_instance exec
  dup /CIDFontName known { /CIDFontPath } { /FontPath } ifelse //RefinePath exec
} .internalbind def

/IsMyRecord      % <raw_record> -> <raw_record> bool
{ dup type /dicttype eq { dup /FAPI known } { //false } ifelse
} .internalbind def

/IsActive       % <record> IsActive <bool>
{ pop //is_device_compatible_to_FAPI exec
} .internalbind def

/FontRecordVirtualMethods //RecordVirtualMethodsStub dup length 2 add dict copy begin
  /IsActive //IsActive def
  /MakeInstance   % <Name> <record> MakeInstance <Name> <Instance> <size>
  { currentglobal 3 1 roll //true setglobal
    //FontOptions //complete_instance exec
    2 copy //GetSize exec
    4 3 roll setglobal
  } .internalbind def
currentdict end def

/CIDFontRecordVirtualMethods //RecordVirtualMethodsStub dup length 3 add dict copy begin
  /GetCSI //TranslateCSI def
  /IsActive //IsActive def
  /MakeInstance   % <Name> <record> MakeInstance <Name> <Instance> <size>
  { currentglobal 3 1 roll //true setglobal
    //CIDFontOptions //complete_instance exec
    2 copy //GetSize exec
    4 3 roll setglobal
  } .internalbind def
currentdict end def

/ReadFCOfontmap:   % <path> ReadFCOfontmap: name dict ...
{
 /fontfile exch def
  {
    currentfile =string readline not {
      pop exit
    } if
    dup length 0 ne {
      0 () /SubFileDecode filter
      dup token not {
        % A comment line
        closefile
      } {
        dup /EndFCOfontmap cvx eq {
          pop closefile exit
        } if
        exch dup token not {
          /ReadFCOfontmap: cvx /rangecheck signalerror
        } if
        exch dup token not {
          /StandardEncoding
        } {
          dup type /nametype ne {
            /ReadFCOfontmap: cvx /rangecheck signalerror
          } if
        } ifelse
        findencoding
        exch dup token not {
          //null
        } {
          dup type /nametype ne {
            /ReadFCOfontmap: cvx /rangecheck signalerror
          } if
          /Decoding findresource
        } ifelse
        exch closefile                 % index name enc dec|null
        4 3 roll                       % name enc dec|null index
        << /Path fontfile
           /FontType 1
           /FAPI /UFST
           /SubfontId counttomark 2 add -1 roll
           /Decoding  counttomark 2 add -1 roll
           dup //null eq {
             pop pop
           } if
           /Encoding  counttomark 2 add -1 roll

           /FontInfo
           <<
             /UnderlineThickness 50
             /Weight (Regular)
             /version (0.00)
             /ItalicAngle 0
             /UnderlinePosition -100
             /FamilyName ()
             /Notice ()
             /FullName ()
           >>
           % although we pretend this is a Type 1, the FAPI interface currently
           % needs an identity FontMatrix
           /FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0]
           % we need a fake CharStrings dictionary to placate certain jobs
           % which try to look inside it
           /CharStrings
           <<
             /.notdef (                    )
             StandardEncoding { (                    ) } forall
           >>
           % FontBBox will get replaced with valid numbers pulled from the rendering engine
           /FontBBox [-128 -128 1024 1024] cvx
        >> % dup { exch == = } forall
      } ifelse
    } {
      pop
    } ifelse
  } loop
  currentdict /fontfile undef
} .internalbind def

%----------------------------------The Redefintion---------------------

/MappedCategoryRedefiner /ProcSet findresource /Redefine get /Redefine exch def

% Redefine the /Font category :
4 dict begin
  /CategoryName /Font def
  /MapFileName systemdict /FAPIfontmap known {/FAPIfontmap .systemvar} {(FAPIfontmap)} ifelse def
  /VerifyMap  { pop } .internalbind def
  /PreprocessRecord  % <map> <Name> <raw_record> PreprocessRecord <map> <Name> <record> <bool>
  { //IsMyRecord exec dup {
      pop dup /RecordVirtualMethods //FontRecordVirtualMethods put
      //true
    } if
  } .internalbind def
currentdict end Redefine

% Redefine the /CIDFont category :
4 dict begin
  /CategoryName /CIDFont def
  /MapFileName systemdict /FAPIcidfmap known {/FAPIcidfmap .systemvar} {(FAPIcidfmap)} ifelse def
  /VerifyMap  { pop } .internalbind def
  /PreprocessRecord  % <map> <Name> <raw_record> PreprocessRecord <map> <Name> <record> <bool>
  { //IsMyRecord exec dup {
      pop dup /RecordVirtualMethods //CIDFontRecordVirtualMethods put
      //true
    } if
  } .internalbind def
currentdict end Redefine

%==================== A hook for buildfont* operators ====================

% The procedure .FAPIhook redirects PS fonts to FAPI on necessity.
% This depends on the following conditions :
%
% 1. If font dictionary has /FAPI entry, it is a font listed in FAPIconfig.FontPath,
%    and must be build with .FAPIrebuildfont, or a copy of a font, which was
%    built with .FAPIrebuildfont .
%
% 2. If the font dictionary has /PathLoad entry, and has no /FAPI entry,
%    it is an installed PS font, which is described in lib/fontmap or
%    in GS_FONTPATH. .loadfont inserts /PathLoad entry for this case
%    (see gs_fonts.ps).
%
%    Installed fonts are being loaded with GS font loader,
%    the they are passed to FAPI is same way as embedded fonts are.
%    We do so because UFST cannot read fonts, which don't
%    follow Type 1/42 file format strongly.
%
% 3. Executing .loadfont, we place /FAPI_hook_disable in the 0th
%    element of some procedure on the execution stack - see gs_fonts.ps .
%    If FAPI_hook finds /FAPI_hook_disable in there,
%    it knows that it is called for a disk font during
%    its internal definefont.
%
% 4. If font dictionary has no /FAPI entry, and has no /Path entry,
%    and if we are not in .loadfont context, it is an embedded font.
%
% 5. Two entries to be defined in lib/FAPIconfig to control the hooking of PS fonts :
%    HookDiskFonts and HookEmbeddedFonts .
%    They specify arrays of font types (integers) to be redirected with FAPI.
%    HookDiskFonts controls disk PS fonts (which fall into (2) and (3) ).
%    HookEmbeddedFonts controls fonts being embedded into documents.
%
% 7. We apply the operator .passtoFAPI for checking whether FAPI can handle a font.
%    If so, we insert /FAPI entry into the font dictionary and convert it
%    with .FAPIrebuildfont . Otherwise the font is handled with the native GS font renderer.

/FAPI_hook_debug   % <proc> FAPI_hook_debug -
FAPIDEBUG { {exec} } { {pop} } ifelse
bind def

/FAPI_hook_warn   % <proc> FAPI_hook_debug -
QUIET { {pop} } { {exec} } ifelse
bind def

/FAPI_is_hook_disabled     % - FAPI_is_hook_disabled <bool>
{ % checks whether execution stack contains packedarray started with /FAPI_hook_disable .
  /FAPI_hook_disable /MappedCategoryRedefiner /ProcSet findresource /execstack_lookup get exec
  //null ne
} .internalbind def

/FAPIhook_aux  %      <string|name> <font_dict> .FAPIhook <string|name> <font>
{                                                     % name <<font>>
  { (\nFAPIhook ) print 1 index = flush } //FAPI_hook_debug exec
  dup /FAPI known {
    { //PrintFontRef exec ( is mapped to FAPI=) print dup /FAPI get = flush } //FAPI_hook_debug exec
    //true .FAPIrebuildfont //ChooseDecoding exec
  } {
    dup /PathLoad known dup {
      { (PathLoad known for the font ) print //PrintFontRef exec (.\n) print flush} //FAPI_hook_debug exec
    } {
      pop //FAPI_is_hook_disabled exec dup
      { pop
        { (FAPIhook is in .loadfont context for the font ) print //PrintFontRef exec (.\n) print flush } //FAPI_hook_debug exec
        //true
      } if
    } ifelse
    { /HookDiskFonts } { /HookEmbeddedFonts } ifelse
    //Config exch get                                  % name <<font>> [types]

    dup type /dicttype eq
    {
      //false exch
      {
        3 index //GetFontType exec //FindInArray exec
        {
          2 index exch /FAPIPlugInReq exch put
          pop //true exit
        }
        { pop } ifelse
      } forall
    }
    {
        1 index //GetFontType exec //FindInArray exec      % name <<font>> bHook
    } ifelse

    { { (Trying to render the font ) print //PrintFontRef exec ( with FAPI...\n) print flush } //FAPI_hook_debug exec
      .FAPIpassfont {
        { //PrintFontRef exec ( is being rendered with FAPI=) print dup /FAPI get = flush } //FAPI_hook_debug exec
        //false .FAPIrebuildfont //ChooseDecoding exec
      } {
        { (Can't render ) print //PrintFontRef exec ( with FAPI, will do with native GS renderer.\n) print flush } //FAPI_hook_warn exec
      } ifelse
    } {
      { (The font ) print //PrintFontRef exec ( doesn't need to render with FAPI.\n) print flush } //FAPI_hook_debug exec
    } ifelse

    % Remove the plugin request from the font dictionary
    dup /FAPIPlugInReq undef
  } ifelse
} .internalbind def

/FAPIhook  %      <string|name> <font_dict> .FAPIhook <string|name> <font>
{ //is_device_compatible_to_FAPI exec
  { //FAPIhook_aux exec
  } {
    { (FAPIhook is disabled for the current device.\n) print flush } //FAPI_hook_debug exec
  } ifelse
} .internalbind def

% ------------------ Redefine .buildfont* with FAPI : -----------------------

/.buildfont1
{ //.buildfont1 exec //FAPIhook exec
} .internalbind % 'odef' is below.

/.buildfont2
{ //.buildfont2 exec //FAPIhook exec
} .internalbind % 'odef' is below.

/.buildfont42
{ //.buildfont42 exec //FAPIhook exec
} .internalbind % 'odef' is below.

/.buildfont9
{ //.buildfont9 exec //FAPIhook exec
} .internalbind % 'odef' is below.

/.buildfont10
{ //.buildfont10 exec //FAPIhook exec
} .internalbind % 'odef' is below.

/.buildfont11
{ //.buildfont11 exec //FAPIhook exec
} .internalbind % 'odef' is below.

end % the temporary dictionary for local binding.
odef odef odef odef odef odef

% Undef these, not needed outside this file
[ /.FAPIavailable /.FAPIpassfont /.FAPIrebuildfont ] systemdict .undefinternalnames

.setlanguagelevel

%END FAPI
