home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: SysTools
/
SysTools.zip
/
ft-beta.zip
/
freetype
/
lib
/
ttfile.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-10-06
|
26KB
|
903 lines
/*******************************************************************
*
* ttfile.c 1.3
*
* File I/O Component (body).
*
* Copyright 1996, 1997 by
* David Turner, Robert Wilhelm, and Werner Lemberg
*
* This file is part of the FreeType project, and may only be used
* modified and distributed under the terms of the FreeType project
* license, LICENSE.TXT. By continuing to use, modify or distribute
* this file you indicate that you have read the license and
* understand and accept it fully.
*
* NOTES :
*
* This implementation relies on the ANSI libc. You may wish to
* modify it to get rid of the libc and go straight to the your
* platform's stream routines.
*
* The same source code can be used for thread-safe and re-entrant
* builds of the library.
*
* Changes between 1.3 and 1.2 :
*
* - all functions report error values now
*
* - the stream semantics have also changed
*
******************************************************************/
const char _ttfile_to_satisfy_ANSI_C_[] = "";
/* ANSI C prevents the compilation of empty units. We thus introduce */
/* a dummy external to get rid of compiler warnings/errors. */
/* Note that gcc's -ansi -pedantic do not report any error here.. */
/* Watcom, VC++ or Borland C++ do however.. */
#include "ttconfig.h"
#ifndef HAVE_MMAP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "freetype.h"
#include "tttypes.h"
#include "tterror.h"
#include "ttengine.h"
#include "ttmutex.h"
#include "ttmemory.h"
#include "ttfile.h" /* our prototypes */
const TFileFrame Null_FileFrame = { NULL, 0, 0 };
/* This definition is mandatory for each file component !! */
/* It has proven useful to do some bounds checks during
development phase.
The should propably be undefined for speed reasons
in a later release. */
#define CHECK_FRAME(frame, n) \
do { \
if (frame.cursor+n > frame.address+frame.size) \
Panic("Frame boundary error!\n"); \
} while (0)
#define FRAME_CACHE_SIZE 2048
/* in non-rentrant builds, we allocate a single block where we'll */
/* place all the frames smaller than FRAME_CACHE_SIZE, rather than */
/* allocating a new block on each access. Bigger frames will be */
/* malloced normally in the heap.. */
/* */
/* See TT_Access_Frame and TT_Forget_Frame for details.. */
/* */
#define MAX_FONT_PATHNAME 128
typedef struct _TStream_Rec
{
FILE* file; /* file handle */
Long base; /* stream base in file */
Long size; /* stream size in file */
#ifdef TT_CONFIG_REENTRANT
char path[ MAX_FONT_PATHNAME ]; /* current pathname */
#endif
} TStream_Rec;
/* we support embedded TrueType files by allowing them to be */
/* inside any file, at any location, hence the 'base' argument */
/* note however that the current implementation does not allow you */
/* to specify a 'base' index when opening a file. */
/* (will come later) */
/* I still don't know if this will turn out useful ?? - DavidT */
/* note also that the only reason to keep the stream's pathname is */
/* the fact that the ANSI libc seems to lack a function to duplicate */
/* FILE structures. We need several streams to access the same font */
/* file in a re-entrant build, and we're obliged to re-open the same */
/* font file to get a new valid FILE* !! Ugly but portable.. */
typedef TStream_Rec* PStream_Rec;
#define STREAM2REC(x) ((TStream_Rec*)( (x).z ))
/* The TFile_Component structure holds all the data that was */
/* previously declared static or global in this component */
/* */
/* It is accessible through the 'engine.file_component' */
/* variable in re-entrant builds, or directly through the */
/* static 'files' variable in other builds */
/* */
struct _TFile_Component
{
TT_Error dummy_error;
#ifndef TT_CONFIG_REENTRANT
TMutex lock; /* used by the thread-safe build only */
unsigned char* frame_cache; /* frame cache */
PStream_Rec stream; /* current stream */
TFileFrame frame; /* current frame */
#endif /* reentrant */
};
typedef struct _TFile_Component TFile_Component;
#ifdef TT_CONFIG_REENTRANT
#define files (*((TFile_Component*)engine.file_component))
/* a simple macro to access the file component's data */
#define CUR_Stream STREAM2REC(stream) /* re-entrant macros */
#define CUR_Frame (*frame)
#define STREAM_VARS stream,
#define STREAM_VAR stream
#else
#define CUR_Stream files.stream /* thread-safe macros */
#define CUR_Frame files.frame
#define STREAM_VARS /* void */
#define STREAM_VAR /* void */
static TFile_Component files;
/* the 'files' variable is only defined in non-reentrant builds */
#endif /* REENTRANT */
/* The macro CUR_Stream denotes the current input stream */
/* Note that for the re-entrant version, the 'stream' name has been */
/* chosen according to the macro STREAM_ARGS */
/* The macro CUR_Frame denotes the current file frame */
/* Note that for the re-entrant version, the 'frame' name has been */
/* chosen according to the macro FRAME_ARGS */
/* The macro STREAM_VAR is used when calling public functions */
/* that need an 'optional' stream argument */
/*******************************************************************
*
* Function : TTFile_Init
*
* Description : Init the File component
*
******************************************************************/
TT_Error TTFile_Init()
{
TT_Error error;
#ifdef TT_CONFIG_REENTRANT
/* in re-entrant builds, we need to allocate the file component */
/* data on the fly before anything else.. */
if ( ALLOC( engine.file_component, sizeof(TFile_Component) ))
return error;
#else /* non re-entrant builds */
engine.file_component = (void*)&files;
MUTEX_Create( files.lock );
files.stream = NULL;
ZERO_Frame( files.frame );
if ( ALLOC( files.frame_cache, FRAME_CACHE_SIZE ) )
return error;
#endif
files.dummy_error = TT_Err_Ok;
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : TTFile_Done
*
* Description : Finalize the File component
*
******************************************************************/
TT_Error TTFile_Done()
{
#ifndef TT_CONFIG_REENTRANT
FREE( files.frame_cache );
MUTEX_Destroy( files.lock );
engine.file_component = NULL;
#else
FREE( engine.file_component );
#endif
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : TT_Open_Stream
*
* Description : opens the font file and saves the total file size.
*
* Input : error address of stream's error variable
* ( re-entrant build only ).
*
* filepathname pathname of the file to open
* stream address of target TT_Stream structure
*
*
* Output : SUCCESS on sucess, FAILURE on error.
* The target stream is set to -1 in case of failure.
*
*
******************************************************************/
TT_Error TT_Open_Stream( const char* filepathname,
TT_Stream* stream )
{
TT_Error error;
FILE* font_file;
PStream_Rec stream_rec;
if ( ALLOC( *stream, sizeof(TStream_Rec) ) )
return error;
stream_rec = STREAM2REC(*stream);
if ( !( font_file = fopen( filepathname, "rb" ) ) )
{
FREE( stream_rec );
return TT_Err_Could_Not_Open_File;
}
stream_rec->base = 0;
stream_rec->file = font_file;
fseek( font_file, 0, SEEK_END );
stream_rec->size = ftell( font_file );
fseek( font_file, 0, SEEK_SET );
#ifdef TT_CONFIG_REENTRANT
/* duplicate the font path name, as there is no 100% standard & */
/* portable way to re-open a FILE other than by re-opening it */
strncpy( stream_rec->path, filepathname, MAX_FONT_PATHNAME-1 );
stream_rec->path[ MAX_FONT_PATHNAME ] = '\0';
#else
CUR_Stream = stream_rec;
#endif
return TT_Err_Ok;
}
#ifdef TT_CONFIG_REENTRANT
/*******************************************************************
*
* Function : TT_Duplicate_Stream
*
* Description : Duplicate a stream for a new instance record
*
* Input : input_stream source stream to duplicate
* copy address of target duplicate stream
* error error of duplicate's error variable
*
* Output : SUCCESS on sucess, FAILURE on error.
* The target stream is set to -1 in case of failure.
*
* Note : This function is only available to re-entrant builds of
* the engine. Note that we kept a font pathname in the
* stream structure because we like an ANSI function to
* duplicate FILE structures conveniently.
*
******************************************************************/
TT_Error TT_Duplicate_Stream( TT_Stream input_stream,
TT_Stream* copy )
{
TT_Error error;
FILE* font_file;
PStream_Rec stream_rec;
PStream_Rec copy_rec;
stream_rec = STREAM2REC(input_stream);
if ( ALLOC( copy_rec, sizeof(TStream_Rec) ) )
return error;
HANDLE_Set( *copy, copy_rec );
if ( !( font_file = fopen( stream_rec->path, "rb" ) ) )
{
FREE( copy_rec );
return TT_Err_Could_Not_Open_File;
}
copy_rec->base = stream_rec->base;
copy_rec->size = stream_rec->size;
copy_rec->file = font_file;
strncpy( copy_rec->path, stream_rec->path, MAX_FONT_PATHNAME-1 );
stream_rec->path[ MAX_FONT_PATHNAME ] = '\0';
return TT_Err_Ok;
}
#endif /* REENTRANT */
/*******************************************************************
*
* Function : TT_Close_Stream
*
* Description : Closes a stream.
*
* Input :
*
* Output : SUCCESS (always)
*
******************************************************************/
TT_Error TT_Close_Stream( TT_Stream* stream )
{
PStream_Rec rec = STREAM2REC(*stream);
fclose( rec->file );
FREE( rec );
HANDLE_Set( *stream, NULL );
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : TT_Use_Stream
*
* Description : copy or duplicate a given stream
*
* Input : org_stream original stream
* stream target stream ( copy or duplicate )
* error target error
*
* Output :
*
******************************************************************/
TT_Error TT_Use_Stream( TT_Stream org_stream,
TT_Stream* stream )
{
#ifndef TT_CONFIG_REENTRANT
MUTEX_Lock( files.lock );
/* lock file mutex */
*stream = org_stream;
/* copy the stream */
return TT_Err_Ok;
#else
return TT_Duplicate_Stream( org_stream, stream );
#endif
}
/*******************************************************************
*
* Function : TT_Done_Stream
*
* Description : copy or duplicate a given stream
*
* Input : org_stream original stream
* stream target stream ( copy or duplicate )
* error target error
*
* Output :
*
******************************************************************/
TT_Error TT_Done_Stream( TT_Stream* stream )
{
#ifndef TT_CONFIG_REENTRANT
HANDLE_Set( *stream, NULL );
MUTEX_Release( files.lock );
return TT_Err_Ok;
#else
return TT_Close_Stream(stream);
#endif
}
/*******************************************************************
*
* Function : TT_Seek_File
*
* Description : Seeks the file cursor to a different position.
*
* Input : position new position in file
*
* Output : SUCCESS on success. FAILURE if out of range.
*
******************************************************************/
TT_Error TT_Seek_File( STREAM_ARGS long position )
{
position += CUR_Stream->base;
if ( fseek( CUR_Stream->file, position, SEEK_SET ) )
return TT_Err_Invalid_File_Offset;
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : TT_Skip_File
*
* Description : Skips forward the file cursor.
*
* Input : distance number of bytes to skip
*
* Output : see TT_Seek_File
*
******************************************************************/
TT_Error TT_Skip_File( STREAM_ARGS long distance )
{
return TT_Seek_File( STREAM_VARS ftell( CUR_Stream->file ) -
CUR_Stream->base + distance );
}
/*******************************************************************
*
* Function : TT_Read_File
*
* Description : Reads a chunk of the file and copies it to memory.
*
* Input : buffer target buffer
* count length in bytes to read
*
* Output : SUCCESS on success. FAILURE if out of range.
*
******************************************************************/
TT_Error TT_Read_File( STREAM_ARGS void* buffer, long count )
{
if ( fread( buffer, 1, count, CUR_Stream->file ) != (unsigned long)count )
return TT_Err_Invalid_File_Read;
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : TT_Read_At_File
*
* Description : Reads file at a specified position.
*
* Input : position position to seek to before read
* buffer target buffer
* count number of bytes to read
*
* Output : SUCCESS on success. FAILURE if error.
*
******************************************************************/
TT_Error TT_Read_At_File( STREAM_ARGS long position,
void* buffer,
long count )
{
TT_Error error;
if ( (error = TT_Seek_File( STREAM_VARS position )) ||
(error = TT_Read_File( STREAM_VARS buffer, count )) )
return error;
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : TT_File_Pos
*
* Description : Returns current file seek pointer.
*
* Input : none
*
* Output : current file position
*
******************************************************************/
Long TT_File_Pos( STREAM_ARG )
{
return (ftell( CUR_Stream->file ) - CUR_Stream->base);
}
/*******************************************************************
*
* Function : TT_Access_Frame
*
* Description : Notifies the component that we're going to read
* 'size' bytes from the current file position.
* This function should load/cache/map these bytes
* so that they will be addressed by the GET_xxx
* functions easily.
*
* Input : size number of bytes to access.
*
* Output : SUCCESS on success. FAILURE on error.
*
* Notes: The function fails if the byte range is not within the
* the file, or if there is not enough memory to cache
* the bytes properly (which usually means that aSize is
* too big in both cases).
*
******************************************************************/
TT_Error TT_Access_Frame( STREAM_ARGS FRAME_ARGS int size )
{
TT_Error error;
if ( CUR_Frame.address != NULL )
return TT_Err_Nested_Frame_Access;
#ifndef TT_CONFIG_REENTRANT
if ( size <= FRAME_CACHE_SIZE )
{
/* use the cache */
CUR_Frame.address = files.frame_cache;
CUR_Frame.size = FRAME_CACHE_SIZE;
}
else
#endif
{
if ( ALLOC( CUR_Frame.address, size ) )
return error;
CUR_Frame.size = size;
}
if ((error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size )))
{
#ifndef TT_CONFIG_REENTRANT
if ( size > FRAME_CACHE_SIZE )
#endif
FREE( CUR_Frame.address );
CUR_Frame.address = NULL;
CUR_Frame.size = 0;
}
CUR_Frame.cursor = CUR_Frame.address;
return error;
}
/*******************************************************************
*
* Function : TT_Check_And_Access_Frame
*
* Description : Notifies the component that we're going to read
* aSize bytes from the current file position.
* This function should load/cache/map these bytes
* so that they will be addressed by the GET_xxx
* functions easily.
*
* Input : size number of bytes to access.
*
* Output : SUCCESS on success. FAILURE on error.
*
* Notes: The function truncates aSize if the byte range is not
* within the file.
*
* It will fail if there is not enough memory to cache
* the bytes properly (which usually means that aSize is
* too big).
*
* It will fail if you make two consecutive calls
* to TT_Access_Frame(), without a TT_Forget_Frame() between
* them.
*
* The only difference with TT_Access_Frame() is that we
* check that the frame is within the current file. We
* otherwise truncate it.
*
******************************************************************/
TT_Error TT_Check_And_Access_Frame( STREAM_ARGS FRAME_ARGS int size )
{
TT_Error error;
Long readBytes;
if ( CUR_Frame.address != NULL )
return TT_Err_Nested_Frame_Access;
#ifndef TT_CONFIG_REENTRANT
if ( size <= FRAME_CACHE_SIZE )
{
/* use the cache */
CUR_Frame.address = files.frame_cache;
CUR_Frame.size = FRAME_CACHE_SIZE;
}
else
#endif
{
if ( ALLOC( CUR_Frame.address, size ) )
return error;
CUR_Frame.size = size;
}
readBytes = CUR_Stream->size - TT_File_Pos( STREAM_VAR );
if ( size > readBytes ) size = readBytes;
if ((error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size )))
{
#ifndef TT_CONFIG_REENTRANT
if ( size > FRAME_CACHE_SIZE )
#endif
FREE( CUR_Frame.address );
CUR_Frame.address = NULL;
CUR_Frame.size = 0;
}
CUR_Frame.cursor = CUR_Frame.address;
return error;
}
/*******************************************************************
*
* Function : TT_Forget_Frame
*
* Description : Releases a cached frame after reading.
*
* Input : None
*
* Output : SUCCESS on success. FAILURE on error.
*
******************************************************************/
TT_Error TT_Forget_Frame( FRAME_ARG )
{
if ( CUR_Frame.address == NULL )
return TT_Err_Nested_Frame_Access;
#ifndef TT_CONFIG_REENTRANT
if ( CUR_Frame.size > FRAME_CACHE_SIZE )
#endif
FREE( CUR_Frame.address );
CUR_Frame.address = NULL;
CUR_Frame.cursor = NULL;
CUR_Frame.size = 0;
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : GET_Byte
*
* Description : Extracts a byte from the current file frame.
*
* Input : None or current frame
*
* Output : Extracted Byte
*
* NOTES : We consider that the programmer is intelligent enough
* not to try to get a byte that is out of the frame. Hence,
* we provide no bounds check here. (A misbehaving client
* could easily page fault using this call.)
*
******************************************************************/
unsigned char Get_Byte( FRAME_ARG )
{
CHECK_FRAME(CUR_Frame, 1);
return (unsigned char)(*CUR_Frame.cursor++);
}
/*******************************************************************
*
* Function : GET_Char
*
* Description : Extracts a signed byte from the current file frame.
*
* Input : None or current frame
*
* Output : Extracted char
*
* NOTES : We consider that the programmer is intelligent enough
* not to try to get a byte that is out of the frame. Hence,
* we provide no bounds check here. (A misbehaving client
* could easily page fault using this call.)
*
******************************************************************/
char Get_Char( FRAME_ARG )
{
CHECK_FRAME(CUR_Frame, 1);
return (char)(*CUR_Frame.cursor++);
}
/*******************************************************************
*
* Function : GET_Short
*
* Description : Extracts a short from the current file frame.
*
* Input : None or current frame
*
* Output : Extracted short
*
* NOTES : We consider that the programmer is intelligent enough
* not to try to get a byte that is out of the frame. Hence,
* we provide no bounds check here. (A misbehaving client
* could easily page fault using this call.)
*
******************************************************************/
short Get_Short( FRAME_ARG )
{
short getshort;
CHECK_FRAME(CUR_Frame, 2);
getshort = ((short)CUR_Frame.cursor[0] << 8) |
(short)CUR_Frame.cursor[1];
CUR_Frame.cursor += 2;
return getshort;
}
/*******************************************************************
*
* Function : GET_UShort
*
* Description : Extracts an unsigned short from the frame.
*
* Input : None or current frame
*
* Output : Extracted ushort
*
* NOTES : We consider that the programmer is intelligent enough
* not to try to get a byte that is out of the frame. Hence,
* we provide no bounds check here. (A misbehaving client
* could easily page fault using this call.)
*
******************************************************************/
unsigned short Get_UShort( FRAME_ARG )
{
unsigned short getshort;
CHECK_FRAME(CUR_Frame, 2);
getshort = ((unsigned short)CUR_Frame.cursor[0] << 8) |
(unsigned short)CUR_Frame.cursor[1];
CUR_Frame.cursor += 2;
return getshort;
}
/*******************************************************************
*
* Function : GET_Long
*
* Description : Extracts a long from the frame.
*
* Input : None or current frame
*
* Output : Extracted long
*
* NOTES : We consider that the programmer is intelligent enough
* not to try to get a byte that is out of the frame. Hence,
* we provide no bounds check here. (A misbehaving client
* could easily page fault using this call.)
*
******************************************************************/
long Get_Long( FRAME_ARG )
{
long getlong;
CHECK_FRAME(CUR_Frame, 4);
getlong = ((long)CUR_Frame.cursor[0] << 24) |
((long)CUR_Frame.cursor[1] << 16) |
((long)CUR_Frame.cursor[2] << 8 ) |
(long)CUR_Frame.cursor[3];
CUR_Frame.cursor += 4;
return getlong;
}
/*******************************************************************
*
* Function : GET_ULong
*
* Description : Extracts an unsigned long from the frame.
*
* Input : None
*
* Output : Extracted ulong
*
* NOTES : We consider that the programmer is intelligent enough
* not to try to get a byte that is out of the frame. Hence,
* we provide no bounds check here. (A misbehaving client
* could easily page fault using this call.)
*
******************************************************************/
unsigned long Get_ULong( FRAME_ARG )
{
unsigned long getlong;
CHECK_FRAME(CUR_Frame, 4);
getlong = ( ((unsigned long)CUR_Frame.cursor[0] << 24) |
((unsigned long)CUR_Frame.cursor[1] << 16) |
((unsigned long)CUR_Frame.cursor[2] << 8 ) |
(unsigned long)CUR_Frame.cursor[3] );
CUR_Frame.cursor += 4;
return getlong;
}
#endif /* HAVE_MMAP */
/* End */