1176 lines
32 KiB
C
1176 lines
32 KiB
C
/*******************************************************************
|
|
*
|
|
* ttfile.c (extended version) 2.1
|
|
*
|
|
* File I/O Component (body).
|
|
*
|
|
* Copyright 1996-1999 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 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 2.0 and 2.1 :
|
|
*
|
|
* - it is now possible to close a stream's file handle explicitely
|
|
* through the new API "TT_Flush_Stream". This will simply close
|
|
* a stream's file handle (useful to save system resources when
|
|
* dealing with lots of opened fonts). Of course, the function
|
|
* "TT_Use_Stream" will automatically re-open a stream's handle if
|
|
* necessary.
|
|
*
|
|
* - added "TT_Stream_Size" to replace "TT_File_Size" which wasn't
|
|
* used anyway. This one returns the size of any stream, even
|
|
* flushed one (when the previous TT_File_Size could only return
|
|
* the size of the current working stream). This is used by the
|
|
* new "Load_TrueType_Any" function in the tables loader.
|
|
*
|
|
******************************************************************/
|
|
|
|
#include "ttconfig.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "freetype.h"
|
|
#include "tttypes.h"
|
|
#include "ttdebug.h"
|
|
#include "ttengine.h"
|
|
#include "ttmutex.h"
|
|
#include "ttmemory.h"
|
|
#include "ttfile.h" /* our prototypes */
|
|
|
|
/* required by the tracing mode */
|
|
#undef TT_COMPONENT
|
|
#define TT_COMPONENT trace_file
|
|
|
|
|
|
/* For now, we don't define additional error messages in the core library */
|
|
/* to report open-on demand errors. Define these error as standard ones */
|
|
|
|
#define TT_Err_Could_Not_ReOpen_File TT_Err_Could_Not_Open_File
|
|
#define TT_Err_Could_Not_ReSeek_File TT_Err_Could_Not_Open_File
|
|
|
|
/* This definition is mandatory for each file component! */
|
|
EXPORT_FUNC
|
|
const TFileFrame TT_Null_FileFrame = { NULL, 0, 0 };
|
|
|
|
/* It has proven useful to do some bounds checks during development phase. */
|
|
/* They should probably be undefined for speed reasons in a later release. */
|
|
|
|
#if DEBUG_FILE
|
|
#define CHECK_FRAME( frame, n ) \
|
|
do { \
|
|
if ( frame.cursor + n > frame.address + frame.size ) \
|
|
Panic( "Frame boundary error!\n" ); \
|
|
} while ( 0 )
|
|
#else
|
|
#define CHECK_FRAME( frame, n ) /* nothing */
|
|
#endif
|
|
|
|
/* Because a stream can be flushed, i.e. its file handle can be */
|
|
/* closed to save system resources, we must keep the stream's file */
|
|
/* pathname to be able to re-open it on demand when it is flushed */
|
|
|
|
struct TStream_Rec_;
|
|
typedef struct TStream_Rec_ TStream_Rec;
|
|
typedef TStream_Rec* PStream_Rec;
|
|
|
|
struct TStream_Rec_
|
|
{
|
|
Bool opened; /* is the stream handle opened ? */
|
|
TT_Text* name; /* the file's pathname */
|
|
Long position; /* current position within the file */
|
|
|
|
FILE* file; /* file handle */
|
|
Long base; /* stream base in file */
|
|
Long size; /* stream size in file */
|
|
};
|
|
|
|
/* 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 */
|
|
|
|
#define STREAM2REC( x ) ( (TStream_Rec*)HANDLE_Val( x ) )
|
|
|
|
static TT_Error Stream_Activate ( PStream_Rec stream );
|
|
static TT_Error Stream_Deactivate( PStream_Rec stream );
|
|
|
|
|
|
#ifndef TT_CONFIG_OPTION_THREAD_SAFE
|
|
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/**** ****/
|
|
/**** N O N R E E N T R A N T I M P L E M E N T A T I O N ****/
|
|
/**** ****/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
|
|
/* 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 FRAME_CACHE_SIZE 2048
|
|
|
|
/* 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_
|
|
{
|
|
TMutex lock; /* used by the thread-safe build only */
|
|
Byte* frame_cache; /* frame cache */
|
|
PStream_Rec stream; /* current stream */
|
|
TFileFrame frame; /* current frame */
|
|
};
|
|
|
|
typedef struct TFile_Component_ TFile_Component;
|
|
|
|
static TFile_Component files;
|
|
|
|
#define CUR_Stream files.stream
|
|
#define CUR_Frame files.frame
|
|
|
|
#define STREAM_VARS /* void */
|
|
#define STREAM_VAR /* void */
|
|
|
|
/* 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 : Initializes the File component.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error TTFile_Init( PEngine_Instance engine )
|
|
{
|
|
TT_Error error;
|
|
|
|
|
|
MUTEX_Create( files.lock );
|
|
files.stream = NULL;
|
|
ZERO_Frame( files.frame );
|
|
|
|
if ( ALLOC( files.frame_cache, FRAME_CACHE_SIZE ) )
|
|
return error;
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TTFile_Done
|
|
*
|
|
* Description : Finalizes the File component.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error TTFile_Done( PEngine_Instance engine )
|
|
{
|
|
FREE( files.frame_cache );
|
|
MUTEX_Destroy( files.lock );
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TT_Use_Stream
|
|
*
|
|
* Description : Copies or duplicates a given stream.
|
|
*
|
|
* Input : org_stream original stream
|
|
* stream target stream (copy or duplicate)
|
|
*
|
|
* Output : Error code.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Use_Stream( TT_Stream org_stream,
|
|
TT_Stream* stream )
|
|
{
|
|
MUTEX_Lock( files.lock ); /* lock file mutex */
|
|
|
|
*stream = org_stream; /* copy the stream */
|
|
files.stream = STREAM2REC(org_stream); /* set current stream */
|
|
|
|
Stream_Activate( files.stream );
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TT_Done_Stream
|
|
*
|
|
* Description : Releases a given stream.
|
|
*
|
|
* Input : stream target stream
|
|
*
|
|
* Output : Error code.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Done_Stream( TT_Stream* stream )
|
|
{
|
|
HANDLE_Set( *stream, NULL );
|
|
MUTEX_Release( files.lock );
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* 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 `size' is
|
|
* too big in both cases).
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Access_Frame( STREAM_ARGS FRAME_ARGS Long size )
|
|
{
|
|
TT_Error error;
|
|
|
|
|
|
if ( CUR_Frame.address != NULL )
|
|
return TT_Err_Nested_Frame_Access;
|
|
|
|
if ( size <= FRAME_CACHE_SIZE )
|
|
{
|
|
/* use the cache */
|
|
CUR_Frame.address = files.frame_cache;
|
|
CUR_Frame.size = FRAME_CACHE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
if ( ALLOC( CUR_Frame.address, size ) )
|
|
return error;
|
|
CUR_Frame.size = size;
|
|
}
|
|
|
|
error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size );
|
|
if (error)
|
|
{
|
|
if ( size > FRAME_CACHE_SIZE )
|
|
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
|
|
* `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 truncates `size' 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 `size' 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.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Check_And_Access_Frame( STREAM_ARGS FRAME_ARGS Long size )
|
|
{
|
|
TT_Error error;
|
|
Long readBytes, requested;
|
|
|
|
|
|
if ( CUR_Frame.address != NULL )
|
|
return TT_Err_Nested_Frame_Access;
|
|
|
|
if ( size <= FRAME_CACHE_SIZE )
|
|
{
|
|
/* use the cache */
|
|
CUR_Frame.address = files.frame_cache;
|
|
CUR_Frame.size = FRAME_CACHE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
if ( ALLOC( CUR_Frame.address, size ) )
|
|
return error;
|
|
CUR_Frame.size = size;
|
|
}
|
|
|
|
requested = size;
|
|
readBytes = CUR_Stream->size - TT_File_Pos( STREAM_VAR );
|
|
if ( size > readBytes )
|
|
size = readBytes;
|
|
|
|
error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size );
|
|
if (error)
|
|
{
|
|
if ( requested > FRAME_CACHE_SIZE )
|
|
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.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Forget_Frame( FRAME_ARG )
|
|
{
|
|
if ( CUR_Frame.address == NULL )
|
|
return TT_Err_Nested_Frame_Access;
|
|
|
|
if ( CUR_Frame.size > FRAME_CACHE_SIZE )
|
|
FREE( CUR_Frame.address );
|
|
|
|
ZERO_Frame( CUR_Frame );
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
#else /* TT_CONFIG_OPTION_THREAD_SAFE */
|
|
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/******** ********/
|
|
/******** R E E N T R A N T I M P L E M E N T A T I O N ********/
|
|
/******** ********/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
|
|
/* a simple macro to access the file component's data */
|
|
#define files ( *((TFile_Component*)engine.file_component) )
|
|
|
|
#define CUR_Stream STREAM2REC( stream ) /* re-entrant macros */
|
|
#define CUR_Frame (*frame)
|
|
|
|
#define STREAM_VARS stream,
|
|
#define STREAM_VAR stream
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TTFile_Init
|
|
*
|
|
* Description : Initializes the File component.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error TTFile_Init( PEngine_Instance engine )
|
|
{
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TTFile_Done
|
|
*
|
|
* Description : Finalizes the File component.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error TTFile_Done( PEngine_Instance engine )
|
|
{
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TT_Use_Stream
|
|
*
|
|
* Description : Duplicates a stream for a new usage.
|
|
*
|
|
* Input : input_stream source stream to duplicate
|
|
* copy address of target duplicate stream
|
|
*
|
|
* Output : error code.
|
|
* The target stream is set to NULL in case of failure.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Use_Stream( TT_Stream input_stream,
|
|
TT_Stream* copy )
|
|
{
|
|
PStream_Rec rec = STREAM2REC( input_stream );
|
|
|
|
return TT_Open_Stream( rec->name, copy );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TT_Done_Stream
|
|
*
|
|
* Description : Releases a given stream.
|
|
*
|
|
* Input : stream target stream
|
|
*
|
|
* Output :
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Done_Stream( TT_Stream* stream )
|
|
{
|
|
return TT_Close_Stream( stream );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* 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 `size' is
|
|
* too big in both cases).
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Access_Frame( STREAM_ARGS FRAME_ARGS Long size )
|
|
{
|
|
TT_Error error;
|
|
|
|
|
|
if ( CUR_Frame.address != NULL )
|
|
return TT_Err_Nested_Frame_Access;
|
|
|
|
if ( ALLOC( CUR_Frame.address, size ) )
|
|
return error;
|
|
CUR_Frame.size = size;
|
|
|
|
error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size );
|
|
if ( error )
|
|
{
|
|
FREE( CUR_Frame.address );
|
|
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
|
|
* `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 truncates `size' 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 `size' 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.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Check_And_Access_Frame( STREAM_ARGS FRAME_ARGS Long size )
|
|
{
|
|
TT_Error error;
|
|
Long readBytes;
|
|
|
|
|
|
if ( CUR_Frame.address != NULL )
|
|
return TT_Err_Nested_Frame_Access;
|
|
|
|
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;
|
|
|
|
error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size );
|
|
if ( error )
|
|
{
|
|
FREE( CUR_Frame.address );
|
|
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.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Forget_Frame( FRAME_ARG )
|
|
{
|
|
if ( CUR_Frame.address == NULL )
|
|
return TT_Err_Nested_Frame_Access;
|
|
|
|
FREE( CUR_Frame.address );
|
|
ZERO_Frame( CUR_Frame );
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
#endif /* TT_CONFIG_OPTION_THREAD_SAFE */
|
|
|
|
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/*********** ***********/
|
|
/*********** C O M M O N I M P L E M E N T A T I O N ***********/
|
|
/*********** ***********/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : Stream_Activate
|
|
*
|
|
* Description : activates a stream, this will either:
|
|
* - open a new file handle if the stream is closed
|
|
* - move the stream to the head of the linked list
|
|
*
|
|
* Input : stream the stream to activate
|
|
*
|
|
* Output : error condition.
|
|
*
|
|
* Note : This function is also called with fresh new streams
|
|
* created by TT_Open_Stream(). They have their 'size'
|
|
* field set to -1.
|
|
*
|
|
******************************************************************/
|
|
|
|
static TT_Error Stream_Activate( PStream_Rec stream )
|
|
{
|
|
if ( !stream->opened )
|
|
{
|
|
if ( (stream->file = fopen( (TT_Text*)stream->name, "rb" )) == 0 )
|
|
return TT_Err_Could_Not_ReOpen_File;
|
|
|
|
stream->opened = TRUE;
|
|
|
|
/* A newly created stream has a size field of -1 */
|
|
if ( stream->size < 0 )
|
|
{
|
|
fseek( stream->file, 0, SEEK_END );
|
|
stream->size = ftell( stream->file );
|
|
fseek( stream->file, 0, SEEK_SET );
|
|
}
|
|
|
|
/* Reset cursor in file */
|
|
if ( stream->position )
|
|
{
|
|
if ( fseek( stream->file, stream->position, SEEK_SET ) != 0 )
|
|
{
|
|
/* error during seek */
|
|
fclose( stream->file );
|
|
stream->opened = FALSE;
|
|
return TT_Err_Could_Not_ReSeek_File;
|
|
}
|
|
}
|
|
}
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : Stream_DeActivate
|
|
*
|
|
* Description : deactivates a stream, this will :
|
|
* - close its file handle if it was opened
|
|
* - remove it from the opened list if necessary
|
|
*
|
|
* Input : stream the stream to deactivate
|
|
*
|
|
* Output : Error condition
|
|
*
|
|
* Note : the function is called whenever a stream is deleted
|
|
* (_not_ when a stream handle's is closed due to an
|
|
* activation). However, the stream record isn't
|
|
* destroyed by it..
|
|
*
|
|
******************************************************************/
|
|
|
|
static TT_Error Stream_Deactivate( PStream_Rec stream )
|
|
{
|
|
if ( stream->opened )
|
|
{
|
|
/* Save its current position within the file */
|
|
stream->position = ftell( stream->file );
|
|
fclose( stream->file );
|
|
stream->file = 0;
|
|
stream->opened = FALSE;
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TT_Stream_Size
|
|
*
|
|
* Description : Returns the length of a given stream, even if it
|
|
* is flushed.
|
|
*
|
|
* Input : stream the stream
|
|
*
|
|
* Output : Length of stream in bytes.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
Long TT_Stream_Size( TT_Stream stream )
|
|
{
|
|
PStream_Rec rec = STREAM2REC( stream );
|
|
|
|
|
|
if ( rec )
|
|
return rec->size;
|
|
else
|
|
return 0; /* invalid stream - return 0 */
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* 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.
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error TT_Open_Stream( const TT_Text* filepathname,
|
|
TT_Stream* stream )
|
|
{
|
|
Int len;
|
|
TT_Error error;
|
|
PStream_Rec stream_rec;
|
|
|
|
if ( ALLOC( *stream, sizeof ( TStream_Rec ) ) )
|
|
return error;
|
|
|
|
stream_rec = STREAM2REC( *stream );
|
|
|
|
stream_rec->file = NULL;
|
|
stream_rec->size = -1L;
|
|
stream_rec->base = 0;
|
|
stream_rec->opened = FALSE;
|
|
stream_rec->position = 0;
|
|
|
|
len = strlen( filepathname ) + 1;
|
|
if ( ALLOC( stream_rec->name, len ) )
|
|
goto Fail;
|
|
|
|
strncpy( stream_rec->name, filepathname, len );
|
|
|
|
error = Stream_Activate( stream_rec );
|
|
if ( error )
|
|
goto Fail_Activate;
|
|
|
|
#ifndef TT_CONFIG_OPTION_THREAD_SAFE
|
|
CUR_Stream = stream_rec;
|
|
#endif
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail_Activate:
|
|
FREE( stream_rec->name );
|
|
Fail:
|
|
FREE( stream_rec );
|
|
return error;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TT_Close_Stream
|
|
*
|
|
* Description : Closes a stream.
|
|
*
|
|
* Input : stream address of target TT_Stream structure
|
|
*
|
|
* Output : SUCCESS (always).
|
|
*
|
|
******************************************************************/
|
|
|
|
LOCAL_FUNC
|
|
TT_Error TT_Close_Stream( TT_Stream* stream )
|
|
{
|
|
PStream_Rec rec = STREAM2REC( *stream );
|
|
|
|
|
|
Stream_Deactivate( rec );
|
|
FREE( rec->name );
|
|
FREE( rec );
|
|
|
|
HANDLE_Set( *stream, NULL );
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TT_Flush_Stream
|
|
*
|
|
* Description : Flushes a stream, i.e., closes its file handle.
|
|
*
|
|
* Input : stream address of target TT_Stream structure
|
|
*
|
|
* Output : Error code
|
|
*
|
|
* NOTE : Never flush the current opened stream. This means that
|
|
* you should _never_ call this function between a
|
|
* TT_Use_Stream() and a TT_Done_Stream()!
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Flush_Stream( TT_Stream* stream )
|
|
{
|
|
PStream_Rec rec = STREAM2REC( *stream );
|
|
|
|
|
|
if ( rec )
|
|
{
|
|
Stream_Deactivate( rec );
|
|
return TT_Err_Ok;
|
|
}
|
|
else
|
|
return TT_Err_Invalid_Argument;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* 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.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
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()
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
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.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
TT_Error TT_Read_File( STREAM_ARGS void* buffer, Long count )
|
|
{
|
|
if ( fread( buffer, 1, count, CUR_Stream->file ) != (ULong)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.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
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 )) != TT_Err_Ok ||
|
|
(error = TT_Read_File( STREAM_VARS buffer, count )) != TT_Err_Ok )
|
|
return error;
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : TT_File_Pos
|
|
*
|
|
* Description : Returns current file seek pointer.
|
|
*
|
|
* Input : none
|
|
*
|
|
* Output : Current file position.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
Long TT_File_Pos( STREAM_ARG )
|
|
{
|
|
return ftell( CUR_Stream->file ) - CUR_Stream->base;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : GET_Byte
|
|
*
|
|
* Description : Extracts a byte from the current file frame.
|
|
*
|
|
* Input : None or current frame
|
|
*
|
|
* Output : Extracted Byte.
|
|
*
|
|
******************************************************************/
|
|
#if 0
|
|
EXPORT_FUNC
|
|
Byte TT_Get_Byte( FRAME_ARG )
|
|
{
|
|
CHECK_FRAME( CUR_Frame, 1 );
|
|
|
|
return (Byte)(*CUR_Frame.cursor++);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : GET_Char
|
|
*
|
|
* Description : Extracts a signed byte from the current file frame.
|
|
*
|
|
* Input : None or current frame
|
|
*
|
|
* Output : Extracted char.
|
|
*
|
|
******************************************************************/
|
|
EXPORT_FUNC
|
|
Char TT_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.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
Short TT_Get_Short( FRAME_ARG )
|
|
{
|
|
Short getshort;
|
|
|
|
|
|
CHECK_FRAME( CUR_Frame, 2 );
|
|
|
|
getshort = (Short)((CUR_Frame.cursor[0] << 8) |
|
|
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.
|
|
*
|
|
******************************************************************/
|
|
#if 0
|
|
EXPORT_FUNC
|
|
UShort TT_Get_UShort( FRAME_ARG )
|
|
{
|
|
UShort getshort;
|
|
|
|
|
|
CHECK_FRAME( CUR_Frame, 2 );
|
|
|
|
getshort = (UShort)((CUR_Frame.cursor[0] << 8) |
|
|
CUR_Frame.cursor[1]);
|
|
|
|
CUR_Frame.cursor += 2;
|
|
|
|
return getshort;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Function : GET_Long
|
|
*
|
|
* Description : Extracts a long from the frame.
|
|
*
|
|
* Input : None or current frame
|
|
*
|
|
* Output : Extracted long.
|
|
*
|
|
******************************************************************/
|
|
|
|
EXPORT_FUNC
|
|
Long TT_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 or current frame
|
|
*
|
|
* Output : Extracted ulong.
|
|
*
|
|
******************************************************************/
|
|
#if 0
|
|
EXPORT_FUNC
|
|
ULong TT_Get_ULong( FRAME_ARG )
|
|
{
|
|
ULong getlong;
|
|
|
|
|
|
CHECK_FRAME( CUR_Frame, 4 );
|
|
|
|
getlong = ( ((ULong)CUR_Frame.cursor[0] << 24) |
|
|
((ULong)CUR_Frame.cursor[1] << 16) |
|
|
((ULong)CUR_Frame.cursor[2] << 8 ) |
|
|
(ULong)CUR_Frame.cursor[3] );
|
|
|
|
CUR_Frame.cursor += 4;
|
|
|
|
return getlong;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* END */
|