

		     The FreeType Porting Guide

				 or

	    Everything you need to know to make FreeType
		     run on the weirdest system


	     David Turner - version 1.0 - 28 march 1998

--------------------------------------------------------------------

			Table of Contents


                Introduction
    
                I.   General design and system modules
    
                II.  Memory component API
    
                  1. The alloction function: TT_Alloc()
                  2. The release function: TT_Free()
                  3. The ALLOC() and ALLOC_ARRAY() macros
                  4. The MEM_xxxx() macros
    
                III. File component API
    
                  1. Streams and their functions
                  2. Frames and file access
                  3. Differences in thread support levels
    
                IV.  Mutex component API
    
                V.   Summary & Advanced concepts
    
                  1. Porting summary
                  2. Exotic filesystems
    
                VI.  Troubleshooting

                Conclusion


--------------------------------------------------------------------

Introduction:

The FreeType engine is portable in many ways:

- First, it can be compiled  by any ANSI C compliant compiler, which
  guarantees the widest possible uses.

- Its default  build uses a tiny  fraction of the  ANSI libc, mainly
  for memory management and I/O access, which should be available on
  most systems (i.e., malloc(), free(), fopen(), fread(), etc).

- Its design  is modular,  and allows an  implementer to  remove all
  dependencies  on a  particular runtime  environment, to  adapt the
  engine to its specific needs.

  (For example, it is possible to use memory-mapped files on systems
  which support them.)

This document explains the  engine's design, presenting the "system"
modules that need  to be changed by porters of  the library, as well
as how to do it.

Note  that  this  documentation  is  _very_ detailed,  and  you  may
DIRECTLY  JUMP to SECTION  V (Summary  and advanced  concepts) which
gives  you a  QUICK STEP-BY-STEP  GUIDE TO  PORTING  each component,
without the need to understand all the guts of the TrueType engine.

Several  issues are  discussed,  including the  use  of exotic  font
storage conventions.

--------------------------------------------------------------------

I. General design, and system modules:

  The engine's  design is intentionally highly modular.   It is made
  of several "components", each  with its own specific goals.  Three
  of these play an important role with regards to portability.  They
  are:

  - the memory component:

    Found in  the files  'ttmemory.h' and 'ttmemory.c'.   It defines
    several macros and  a few functions used by  _all_ other modules
    to allocate, release, copy, and move memory blocks.

  - the file component:

    Found  in  the  files  'ttfile.h' and  'ttfile.c'.   It  defines
    several types and abstractions  (streams, frames), that are used
    by _all_ other modules to access font files.

  - the mutex component:

    This  component compiles  to a  null object  when the  engine is
    built  in single-thread  mode.  Otherwise,  for  thread-safe and
    reentrant builds,  the macros and functions it  defines are used
    by the rest of the engine to protect shared variables.

    NOTE:

      Because   the  ANSI   libc  doesn't   provide  synchronisation
      primitives,    (synchronisation    isn't   portable    accross
      platforms), the default  implementation, found in 'ttmutex.c',
      is made  of dummy functions  which always return  a successful
      error condition.

      You _need_ to re-define this  component for your system if you
      decide to make  a thread-safe or reentrant build,  even if you
      use the ANSI libc.


  When  specializing  a  component,  i.e.,  rewriting  it  for  your
  platform, you should respect a few conventions which are explained
  in  the following  sections.  Note  also that  the system-specific
  implementations      are      usually      placed      in      the
  "freetype/lib/arch/<system>" directory.  For example:

    freetype/lib/arch/unix/ttmmap.c

      A   Unix-only  implementation   of  ttfile   which   uses  the
      memory-mapped  file API  (this greatly  improves  the engine's
      performance,  due to  the  random access  pattern typicals  of
      glyph data retrieval).

    freetype/lib/arch/os2/os2file.c

      This is  an implementation of  ttfile specific to  OS/2, which
      directly calls the system functions DosOpen(), DosRead(), etc.

      The FreeType/2 DLL (a free TrueType font driver for OS/2) also
      uses  its   own  memory  component,  which   calls  a  special
      allocation  routine required in  its runtime  environment, and
      also provides  additional statistics than can  be displayed by
      an auxiliary tool while the driver is running in the system.

We ask you to respect  this directory convention.  This really needs
a minor Makefile change, and still having the ability to compile the
"default"  ttfile and  ttmemory will  help you  debug  your specific
ports by easy comparisons.


---------------------------------------------------------------------------


II. Memory component API:

  This  section  presents  the   macros  and  functions  defined  in
  ttmemory.h, and how  they should be implemented, if  you decide to
  rewrite  the  source  file  ttmemory.c from  scratch.   An  easier
  solution would be to replace the calls to malloc() and free() with
  your own functions, though.


1. Allocation routine : TT_Alloc()

  This  function is  used to  allocate blocks  of memory,  just like
  malloc(), but  defines a very different  interface.  Its prototype
  is:

        TT_Error  TT_Alloc( long  size, void**  p );

  [The FreeType source files use abstract data types like `Long' for
  all  internal  functions  and  `TT_Long'  for  externally  visible
  structures.  See tttypes.h and freetype.h, respectively.]

  We can see that:

  - The function  returns an error  code, and _not_ a  pointer.  The
    reason for  this is that  your own implementation  may perfectly
    fail  for more  than one  good  reason.  For  example, it  could
    detect  a corrupted heap,  a memory  exhaustion or  an unusually
    large block, and  have a different error code  for each of these
    cases.

    When a memory allocation error occurs in a FreeType function, it
    is always  taken into account  (of course, for  safety reasons),
    but its  code is directly sent  to the caller.   This means that
    your own applications and font servers will be able to interpret
    these errors, and let you handle them appropriately.

  - Its  second argument  is the  _address_ of  a  typeless pointer.
    This means the need to typecast it before calling this function.

    The macro MEM_Alloc() is defined in ttmemory.h to do it for you,
    as  well  as  the  "memory  extraction"  performed  by  the  '&'
    operator, so that you can write:

      {
        char*  buffer;
        MEM_Alloc( size, buffer );

 instead of

        TT_Alloc( size, (void**)&buffer );
        ...
      }

    Note  that the  engine  _never_ uses  this  macro directly,  but
    ALLOC() instead (see below) in  order to _always_ test the error
    code.

  *****************
  *** IMPORTANT ***
  *****************

  - A newly  allocated block should _always_ be  filled with zeroes!
    This is a  _very_ strong convention used within  all the engine.
    It  helps  greatly  reduce  code  size,  in  general.   If  your
    implementation of  TT_Alloc() doesn't respect  it, you're pretty
    certain  to build an  unrunnable (at  best) or  (worse) instable
    engine!  Beware.


2. Release routine: TT_Free()

  This  routine  is naturally  used  to  release  any block  created
  through TT_Alloc().  Its prototype is:

    TT_Error  TT_Free( void**  P );

  We can see that:

  - It also returns an error code.  Note, however, that the error is
    ignored  in most,  if not  all, parts  of the  engine.   This is
    because frees  usually happen when  all necessary work  has been
    finished, or when something already wrong happened.

    A later implementation of  FreeType (2.0?) may change the return
    type of this function, but this is unlikely.

  - It  takes  the address  of  a  typeless-pointer,  and _not_  the
    pointer's value itself.  This is used to set the pointer's value
    to NULL just after the block was released, which avoids dangling
    references in objects.   Of course, there is a  macro defined to
    simplify source writing.  One can use FREE() like:

      {
        char*  buffer;

        MEM_Alloc( size, buffer );

        .... work ....

        FREE( buffer );

        /* now `buffer' values to NULL, the following line will */
        /* seg-fault                                            */

        a = buffer[0];
      }

  *****************
  *** IMPORTANT ***
  *****************

  - the function TT_Free() (and thus the macro FREE()) will accept a
    NULL pointer succesfully!  This means more precisely the address
    of a pointer having the value  NULL; in this case it will return
    with a successful error code (TT_Err_Ok == 0).

    This  convention  is  also  _very_  strong in  the  engine,  and
    simplifies both code size and  style.  One of its primary origin
    is the engine's object  management which requires the ability to
    release  an object,  be it  normal or  "partial", with  the same
    code.


3. The ALLOC() and ALLOC_ARRAY() macros:

  Two  macros are  also defined  to  make the  FreeType source  code
  easier  to read  and  understand.   Their role  is  to perform  an
  allocation,  while saving  the  error condition  in an  _implicit_
  local variable called "error" and returning a boolean which is set
  to true in case of error.  Their definition is

    #define ALLOC( pointer, size ) \
      ( ( error = MEM_Alloc( pointer, size ) ) != TT_Err_Ok )

  and

    #define ALLOC_ARRAY( pointer, count, type ) \
      ( ( error = MEM_Alloc( pointer, \
                             (count) * sizeof ( type ) ) ) \
      != TT_Err_Ok )

  They  are always  used  in  "if" statements,  and  can be  chained
  together.  Here is some example code:

    {
      char*     buffer1 = 0;   /* temporary buffer 1 */
      char*     buffer2 = 0;   /* temporary buffer 2 */
      TT_Error  error;
 
      ...

      if ( ALLOC_ARRAY( buffer1, n, TT_F26Dot6 ) ||
           ALLOC_ARRAY( buffer2, n, short )      )
        goto Fail;

      ... work ...

    Fail:
      FREE( buffer2 );
      FREE( buffer1 );
      return error;
    }

  Note:

    - If an error occurs during the first alloc, execution will jump
      immediately to the "Fail" label.

    - The failure code, which  releases the buffers, doesn't need to
      differentiate  whether the first  allocation succeeded  or not
      (simply   because  FREE()  accepts   null  pointers   with  no
      problems).

  The equivalent code, without macros, would be:

    {
      char*     buffer1;
      char*     buffer2;
      TT_Error  error;

      error = TT_Alloc( n * sizeof ( TT_F26Dot6 ),
                        (void**)&buffer1 );
      if ( error ) goto Fail_Buffer1;

      error = TT_Alloc( n * sizeof ( short ),
                        (void**)&buffer2 );
      if ( error ) goto Fail_Buffer2;

      .... work ....

    Fail_Buffer2:
      TT_Free( (void**)&buffer2 );

    Fail_Buffer1:
      TT_Free( (void**)&buffer1 );
    }

  Which is a lot less clear about its intents, and uses more special
  cases.


4. The MEM_xxxx() macros:

  Finally, three  macros are defined  to perform some  common memory
  block operations.  Their names are rather explicative:

  - MEM_Copy():

    Used  by the  engine to  copy  one block  of data  in memory  to
    another one.

  - MEM_Set():

    Used to set all bytes of a block of memory to a given value.

  - MEM_Move():

    Well, guess what ;-)


  These  operations  could  have  been embedded  in  functions  like
  TT_Mem_Copy(),  TT_Mem_Set(),  and  TT_Mem_Move(),  but a  lot  of
  compilers are  able to inline  directly calls to  such "intrinsic"
  functions  as memcpy()  and memmove().   Hence, macros  make sense
  here.


---------------------------------------------------------------------------


III. File Component API:


  This section  describes the file  component's API, and  the things
  that are needed to port it to a specific system.  Note that only a
  fraction of  the source code  in "ttfile.c" needs to  be rewritten
  during a port.


1. Streams and their functions:

  A  stream  in  FreeType   (1.0  and  1.1)  encapsulates  both  the
  location/naming of  a file,  and its access.   This is due  to the
  fact that  they were  originally designed to  embed a  simple ANSI
  "FILE*" file pointer.

  This means several things:

  - A  stream  is  created   and  opened  via  the  TT_Open_Stream()
    function.  It  takes, in the  default build, a font  pathname of
    type `char*' that it uses when calling fopen().

  - It can be released/closed via the function TT_Close_Stream().

  - It embeds a "current file  position", just like an ordinary file
    descriptor.    It  is  thus   seekable,  through   the  function
    TT_Seek_File().

  - Raw data  can be extracted from a  stream through TT_Read_File()
    and TT_Read_At_File().

  However, it has certain properties that differ from a libc FILE*:

  - Because each  face object has  its own stream, and  because most
    operating systems limit the number of opened system resources in
    each process,  it is more than  helpful to be able  to "flush" a
    stream.

    A  stream is  said to  be flushed  when the  system  resource it
    contains  (like a  file descriptor)  has been  closed.  However,
    this resource is re-opened automatically when needed.

    The function TT_Flush_Stream() is  used to flush a stream.  When
    a stream has been flushed, it is also said to be "asleep".

  - The engine calls TT_Use_Stream()  before each new stream access.
    With it,  the file  component is able  to awake  (or 'activate')
    streams that are flushed, if needed.

  - Consequently,  the  engine calls  TT_Done_Stream()  when it  has
    performed all  I/O access.  These two  APIs (TT_Use_Stream() and
    TT_Done_Stream()) let  the file  component track and  manage the
    engine's access patterns, and  allows it to cache opened streams
    more cleverly.

    For example, one  could implement an LRU list  used to track the
    "oldest" streams, and only activate the 10 "freshest" ones, thus
    limiting the total number of system stream resources used by the
    library, independently  of the total  number of opened  faces in
    the engine.


2. Frames and file access

  In  order to resolve  endianess and  alignment issues,  the engine
  uses  the concept  of "frames"  to  extract data  from a  TrueType
  table.  It works like:

  - A frame is  simply a sequence of successive  bytes, taken from a
    stream from its current position.  A frame can only exist within
    a stream.

  - The  function  TT_Access_Frame() (ideally)  reads  its data  and
    places it  in an  intermediate buffer, which  is later  used for
    parsing.  This  function also checks  that the whole  frame fits
    within the original file.  For  example, it will return an error
    when detecting "over-reads" in the file (which can happen if the
    font file is broken).

    Note  that the  intermediate buffer  disappears in  the  case of
    memory-mapped files.

  - Each frame has an internal  cursor, which is set to its buffer's
    base by  the previous function.  Note, however,  that it differs
    from the stream's current position, which has been advanced once
    TT_Acess_Frame() is completed.

  - Data is extracted  from the frame through calls  to functions of
    the form:

      TT_Get_<IntegerType>();

    where <IntegerType>  can be any  of: Byte (unsigned  char), Char
    (signed char), Short, UShort, Long, or ULong.

    Each  function  returns  the  integer below  the  current  frame
    cursor, and advances the latter in the buffer.

  - Finally,  when  the frame  access  ends,  the  engine calls  the
    TT_Forget_Frame() function, which  will release the intermediate
    buffer and set the cursor to NULL.

  Here is a typical frame read sequence:

    {
      /* first - read the next 12-bytes frame in memory */
      error = TT_Access_Frame( 12 );
      if ( error )
        return error;

      /* now, extract all data */
      object->field1 = TT_Get_Short();
      object->field2 = TT_Get_Long();
      object->field3 = TT_Get_Char();
      object->filed4 = TT_Get_Long();
      object->field5 = TT_Get_Byte();

      /* done - now release the frame */
      TT_Forget_Frame();

      /* now perform some checks */
      if ( object->field1 == -1 )
        return Error_1;

      if ( object->field2 > object->field4 )
        return Error_2;

      ....
    }


  *****************
  *** IMPORTANT ***
  *****************

  A few  things need  to be noticed  by porters when  they implement
  frame loading (i.e., the TT_Access_Frame() function):

  - The   functions   that   need    to   be   ported   are   really
    TT_Access_Frame()   and  TT_Forget_Frame().   The  TT_Get_XXXX()
    functions should be left as is.

  - A  frame  has  state,  and  must _always_  be  released  through
    TT_Forget_Frame()  in case  of an  error.  This  means  that the
    engine will _never_ use code like the following:

      {
        ...

        error = TT_Access_Frame( 12 );
        if ( error )
           goto Fail;
    
        object->field1 = TT_Get_Short();
    
        /* now check error and return immediately -- */
        /* WITHOUT RELEASING FRAME !                 */
        if ( object->field1 == -1 )
          goto Fail;
    
        object->field2 = TT_Get_Long();
        object->field3 = TT_Get_Char();
        object->field4 = TT_Get_Long();
    
        /* check for error, return immediately -- */
        /* WITHOUT RELEASING FRAME!               */
        if ( object->field2 > object->field4 )
          goto Fail;
    
        /* now release frame */
        TT_Forget_Frame();
      }

    This   means  more   simply   that  EACH   successful  call   to
    TT_Access_Frame()  will   ALWAYS  be  followed  by   a  call  to
    TT_Forget_Frame()!

   - As a consequence  of the first rule, and also  in order to keep
     things  simple,  NESTING FRAME  ACCESSES  aren't allowed.   For
     example, the following code will produce an error:

       {
         ...

         /* First frame access */
         error = TT_Access_Frame( 8 );
         if ( error )
           goto Fail;

         /* read a file offset */
         offset = TT_Get_Long();

         /* seek and load another frame */
         error = TT_File_Seek( stream, offset );
         if ( error )
           goto Fail;

         error = TT_Access_Frame( 4 );
         /* The function TT_Access_Frame detects nested calls */
         /* and ALWAYS returns TT_Err_Nested_Frame_Access!    */
         if ( error )
           goto Fail;

         data1 = TT_Get_Long();

        /* release second frame */
        TT_Forget_Frame();

        /* read next integer from the first frame */
        data2 = TT_Get_Long();

        /* release first frame */
        TT_Forget_Frame();
      }

    This simplifies the  work that needs to be  done wen porting the
    TT_Access_Frame() and TT_Forget_Frame() functions.


3. Differences in thread support levels

  The FreeType library can be built to three distinct thread-support
  levels.  This section  will present each other, and  show how this
  translates within the ttfile.c source code.

  a. Levels:

    The three levels are:

    - single thread:

      No synchronization  primitive is used  to protect the  data in
      the file component.  Hence, there is only one "current" stream
      at any one time.  Note, however, that in some cases, more than
      one stream  may be "active" (or "awakened");  e.g., when using
      memory-mapped files,  each opened  face needs a  valid mapping
      before it can be used/parsed by the engine.

    - thread-safe:

      The thread  safe mode synchronizes concurrent  accesses to the
      renderer's component through mutexes.  For the file component,
      this  means a  single mutex  which is  "locked" by  a  call to
      TT_Use_Stream(), and "released" by TT_Done_Stream().

      As a consequence, there  is only one possible "current" stream
      when the engine reads files, like in the single thread case.

    - re-entrant:

      In  this  mode,  concurrent  accesses  are  possible  on  many
      components,   including   ttfile.    This  means   that   each
      TT_Use_Stream()  must  _really_  create  its  own  system/ANSI
      stream for a single file, and that the file component _cannot_
      have any  state (only stream  objects have!), like  a "current
      stream" and "current frame".

      This mode must use mutexes to protect all shared variables and
      lists from concurrent changes/reads.  The only component which
      is still  serialized in this  mode is the  scan-line converter
      (a.k.a. ttraster).

  b. Implementation differences:

    Because  the  TrueType engine  serves  more  as  a "font  format
    driver" than a general and high-level text-rendering library, it
    has  been decided  to keep  its code  as simple  and  compact as
    possible.
    
    This implies  some implementation differences  between the three
    thread modes, which are briefly explained below:
    
    - Single-thread  and thread-safe  mode can  have a  state, which
      means for ttfile.c, a "current stream" and "current frame".
    
    - In  the  reentrant mode,  the  'state'  must  be stored  in  a
      thread-local  place, which  means  the stack  (or more  simply
      local function variables).
    
    What follows is  that some ttfile functions won't  take the same
    number of arguments depending on the thread-support mode.  Let's
    take the example of frame access and parsing:

    In  single-thread and  thread-safe  mode, the  current frame  is
    automatically set by  the TT_Access_Frame() function, which only
    takes a 'size' argument to determine the run of bytes to extract
    from the _current_stream_ within ttfile's state.
    
    Moreover,  the  TT_Get_XXXX() functions  extract  data from  the
    current  frame, and need  no arguments.   A simple  frame access
    then looks like this:
    
      {
        /* read the next 12-bytes frame from the _current_stream_ */
        error = TT_Access_Frame( 12 );
        if ( error )
          return error;
    
        /* now, extract all data from the _current_frame_ */
        object->field1 = TT_Get_Short();
        object->field2 = TT_Get_Long();
        object->field3 = TT_Get_Char();
        object->filed4 = TT_Get_Long();
        object->field5 = TT_Get_Byte();
    
        /* done - now release the current frame */
        TT_Forget_Frame();
      }
    
    In  reentrant mode,  things are  a bit  different.   The current
    stream and current  frame must be passed as  parameters, and the
    code looks like this (notice the new function parameters!):
    
      {
        TT_Frame  frame;  /* define a local variable to handle */
                          /* the current frame                 */
    
        ....   /* we suppose we already have a stream variable */
        ....   /* named 'stream' (how surprising ;-)           */
    
        /* read the next 12-bytes frame from a given stream */
        error = TT_Access_Frame( stream, 12, &frame );
        if ( error )
          return error;
    
        /* now, extract all data from the given frame */
        object->field1 = TT_Get_Short( frame );
        object->field2 = TT_Get_Long ( frame );
        object->field3 = TT_Get_Char ( frame );
        object->filed4 = TT_Get_Long ( frame );
        object->field5 = TT_Get_Byte ( frame );
    
        /* done - now release the current frame */
        TT_Forget_Frame( frame );
      }

    The differences between these  two schemes are striking.  Though
    an 'easy' solution  would have been to only  write the engine in
    reentrant-mode, it would have  resulted in a larger and slightly
    slower code, as well as the  source a bit more obscure about its
    intents  and  thus  harder  to maintain.   Also,  the  reentrant
    version is  only needed in  rare cases and environments,  and it
    wasn't thought  as a  good idea to  complexify _source_  code in
    order to comply with rare uses.
    
    The problem is  solved within the engine by the use  of a set of
    carefully  selected macros,  which help  generate  both versions
    from a _single_ source file.
    
    Moreover, as the macros copy the non-reentrant syntax (i.e., the
    use of  the "stream" and  "frame" parameters is implicit  to the
    macros), the source  is kept clear and easy  to understand, even
    when compiled in re-entrant mode.
    
    The code looks then like the following in the engine:
    
      {
        ...
    
        /* read the next 12-bytes frame from the current stream  */
        /* assignement of the error code in the local 'error'    */
        /* variable is also implicit to the ACCESS_Frame macro,  */
        /* and its result is always a boolean (no ANSI warnings) */
    
        if ( ACCESS_Frame( 12 ) )
          goto Fail;
    
        /* Now, extract all data from the current frame          */
        /* the macros GET_xxxxx use an implicit local 'frame'    */
        /* variable in reentrant mode.                           */

        object->field1 = GET_Short();
        object->field2 = GET_Long();
        object->field3 = GET_Char();
        object->filed4 = GET_Long();
        object->field5 = GET_Byte();

        /* done - now release the current frame */

        FORGET_Frame();
      }

    What's also good  is that the above code  is "expressive" in the
    sense that it really describes what's happening during the frame
    load, hiding the boring  but necessary details required by error
    checking and  reentrancy.  And when the error  checks are within
    the macros, we're sure we won't forget them because they're "too
    boring to  code" (one of  the reason why 'exceptions'  caught so
    quickly in C++ and Java).

  c. Consequences on ttfile:

    Of   course,  the   macros   only  hide   real  differences   in
    implementation which must be reflected in ttfile.h and ttfile.c.
    In order  to ease this task,  some other macros  are used, which
    use is reserved for these two files.  There are:

      STREAM_ARG
      STREAM_ARGS
      FRAME_ARG
      FRAME_ARGS     in ttfile.h

      CUR_Stream
      STREAM_VAR
      STREAM_VARS
      FRAME_VAR
      FRAME_VARS     in ttfile.c

    All of  these macros (with the exception  of CUR_Stream) default
    to nothing (i.e., a  void macro) in single-thread and re-entrant
    mode, which only differ from the use of a mutex lock and release
    in   the   functions   TT_Use_Stream()   and   TT_Done_Stream().
    CUR_Stream  defaults  to the  file  component's current  stream,
    found in its internal state (as you can guess, it designates the
    "current stream").

    On the opposite, the macros are used to define additional function
    parameters (when a function is called) and arguments (when calling
    in a function).  For example, the following (fictional) code:

      /* return the size of a given stream */
      long  Stream_Size( STREAM_ARG )
      {
        return CUR_Stream.size;
      }

    expands to:

      long  Stream_Size()
      {
        return file_component.current_stream.size;
      }

    in non-reentrant mode, and to:

      long  Stream_Size( TT_Stream  stream )
      {
        return (*stream).size;
      }

    otherwise.

    Thus, we can see the following reentrant expansions:

      STREAM_ARG     -->    TT_Stream   stream
      STREAM_ARGS    -->    TT_Stream   stream, (note the comma)

      FRAME_ARG      -->    TT_Frame    frame
      FRAME_ARGS     -->    TT_Frame    frame,  (note the comme too)

      STREAM_VAR     -->    stream
      STREAM_VARS    -->    stream,

      FRAME_VAR      -->    frame
      FRAME_VARS     -->    frame,

    They follow these simple rules:

     - An XXXX_ARG is used to  define a single optional parameter in
       a function  _prototype_.  The parameter is said  to be single
       when it is not followed by anything, e.g.

         long  Stream_Size( STREAM_ARG )


     - An XXXX_ARGS is the same, followed a comma, in order to place
       other (non-optional) parameters behind, e.g.

         TT_Error  Stream_Seek( STREAM_ARGS  long pos )

     - An  XXXX_VAR is  used, _only_  within ttfile.c,  to  _call_ a
       function  having an  XXXX_ARG or  XXXX_ARGS in  its prototype
       resp. declaration.

         long  Stream_Left( STREAM_ARG )
         {
           return ( Stream_Size( STREAM_VAR ) -
                    Stream_Pos( STREAM_VAR ) );
         }

     - An XXXX_VARS is the same, but can be followed by non-optional
       parameters.

    The macros  allow you  to write some  code independently  of the
    thread level within ttfile.c.

    *****************
    *** IMPORTANT ***
    *****************

    In general,  porters should  not be concerned  about the  use of
    these macros.  One easy way to  port is to take the ANSI code in
    ttfile.c and modify only the parts that really access the system
    (like fopen, fread, fseek, etc).

    These details are explained here  to make you understand how the
    code works, in case you're interested in more elaborate ports.


---------------------------------------------------------------------------


IV. Mutex Component API:

  As said before, the default library source code uses the ANSI libc
  only,  and  the  source  code  in ttmutex.c  only  contains  dummy
  functions which return a succesful error condition in all cases.

  You  thus NEED to  specialize it  in order  to successfully  use a
  thread-safe or  reentrant build.  Here is  explained what's really
  important:


1. the TMutex type:

  The engine uses  the "TMutex" type defined in  ttmutex.h to handle
  mutexes.  It is only a typedef  of a void* and should be kept that
  way for the fastest porting.

  Your job will  most probably be to store  a system mutex/semaphore
  handle or pointer in it.


2. the mutex macros and functions:

  The interface file ttmutex.h  defines several macros that are used
  within  the engine to  protect all  shared variables  (like lists)
  from concurrent accesses.  All macros default to void (nothing) in
  single thread mode, and  to calls to the TT_Mutex_XXXX() functions
  in multi-threaded modes.

  These functions are:

  o TT_Mutex_Create()
    
    Which takes a TMutex address  as an argument.  It should place a
    NULL pointer in this output variable in case of failure.

  o TT_Mutex_Lock()

    It also takes  the address of a TMutex as  an argument.  Used to
    lock the mutex/semaphore, of course.

  o TT_Mutex_Release()

    Guess what ;-) Same interface.

  o TT_Mutex_Delete()

    Destroys a mutex/semaphore.


3. Redefining the TMutex type:

  You can also get rid  of the TT_Mutex_xxxx() functions if you want
  to use your system's synchronization API.  This can be done in two
  simple steps:

  a. Redefine the TMutex type to suit your system's handle types.

  b. Redefine the MUTEX_xxxx() macros in order to call directly your
     API in the case of multi-threaded builds.

  Both  methods   (specializing  the  TT_Mutex_xxx()   functions  or
  redefining the macros) are possible.


--------------------------------------------------------------------------


V. Summary and Advanced Concepts:

1. Quick step-by-step guide to porting the system components:

  a. Port the memory component:

    o Look   at  the  "ttmemory.h"  file  and   change   the  macros
      MEM_Copy(), MEM_Move(), and MEM_Set() to reflect your system's
      API providing  the equivalent functionality.   The reason that
      macros  instead  of functions  are  used  there  is that  many
      compilers are  able to inline directly  these functions within
      your code.

    o Look  at the "ttmemory.c"  file.  Replace the  single malloc()
      call with  your own allocation routine, and  the single free()
      with your  own release routine.   If your allocator  uses more
      sophisticated functions, you'll  probably have to rewrite more
      parts of this file.  See section I above.

  b. Port the mutex component:

    o Look  at the file  "ttmutex.c" and specialize each  routine to
      have it use your system's synchronization API.

    o For a  more advanced port, you can  also directly redefine the
      definition of  the TMutex  type in ttmutex.h,  as well  as the
      macro definitions (like MUTEX_Create(), MUTEX_Lock(), etc.) to
      use directly your system's  API.  The TT_Mutex_xxxx() won't be
      necessary then.

  c. Port the file component:

    o For a quick port,  look at the following functions and replace
      the ANSI libc calls (like fopen(), fclose(), fread(), etc.):

        Stream_Activate(), Stream_Deactivate(), TT_Open_Stream(),
        TT_Done_Stream(), TT_Seek_File(), TT_Skip_File(),
        TT_Read_File(), and TT_File_Pos()

    o If you plan to use memory-mapped files, you can have a look at
      the       Unix       file       component       found       in
      "freetype/lib/arch/unix/ttmmap.c".   It  should  give  you  an
      indication of what to do.


2. Exotic file systems and font resources:
    
1. Other file naming conventions:

  The high-level library uses the "TT_Text*" type to define the type
  of characters  used for  a font file's  pathname.  By  default, it
  equals the  "char*" type,  which allow you  to open a  face object
  with the following call:

    error = TT_Open_Face( engine, "c:\fonts\times.ttf", &face );

  The implementation of  TT_Open_Face() passes directly the pathname
  pointer to the internal  TT_Open_Stream() function, located in the
  file component, which really opens the file.

  Some  filesystems use  different naming  conventions,  like UTF-16
  code, where each  character is coded in 16-bits.  In order to help
  them use FreeType, all you need to do is the following:

  - Define the macro TT_HAVE_TT_TEXT.

  - Define the type "TT_Text" to the character type you need, like
    'wchar_t' for Unicode.

  Note that  this should apply when compiling  the FreeType library,
  as  well   as  WHEN  INCLUDING  THE  FILE   "freetype.h"  IN  YOUR
  APPLICATIONS.

  When the  configuration macro TT_HAVE_TT_TEXT is  not defined, the
  file "freetype.h"  defines TT_Text as  "char*".  You can  read its
  source  code to see  it more  explicitly (look  at the  very first
  lines of code).

  You can also use TT_Text as a pointer to more specific files, like
  a simple memory address when the font is located in ROM, etc.

  Just synchronize the definition of TT_Text with the implementation
  in ttfile.c!


2. Font Resources:

  FreeType  2.0 will  feature many  architectural changes  that will
  help  make porting  easier, especially  with regards  of  the file
  component.
    
  To do  this, it will separate  the concepts of  a "font resource",
  i.e.  a file seen as a  storage, from a "font stream", i.e. a file
  seen as  a stream  of data.  Only  the resource-wise code  will be
  visible  to porters,  and  it will  be  much easier  to port  (for
  example, nearly  all thread-support levels issues  will be treated
  internally in the rest of the engine, and will be invisible to the
  resource component).

  We're sorry  for the current  design and state, but  the TT_Stream
  started  as a  simple  encapsulation of  an  ANSI FILE*  variable,
  before font-specific  access patterns  made them become  what they
  now are.

  FreeType 2.0  will be a good  reason to re-design  I/O access more
  clearly,  and fortunately  with more  power and  flexibility (like
  using easily files  from different type, ROM-based, memory-mapped,
  disk-based, in a single engine).

  However, all of this doesn't  mean than the current design doesn't
  work.  It does, so don't hesitate to use it :-)



VI. Troubleshooting:

  To be written



Conclusion

  To be written


--- end of porting.txt ---
