blob: 3844d8b34b398496ff04658ccd491f250296320c [file] [log] [blame]
/****************************************************************************
*
* pkdrivr.c
*
* FreeType font driver for TeX's PK FONT files.
*
* Copyright 1996-2018 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.
*
*/
#include <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_OBJECTS_H
#include FT_TRUETYPE_IDS_H
#include FT_INTERNAL_TFM_H
#include FT_SERVICE_PK_H
#include FT_SERVICE_FONT_FORMAT_H
#include "pk.h"
#include "pkdrivr.h"
#include "pkerror.h"
/**************************************************************************
*
* The macro FT_COMPONENT is used in trace mode. It is an implicit
* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
* messages during execution.
*/
#undef FT_COMPONENT
#define FT_COMPONENT trace_pkdriver
typedef struct PK_CMapRec_
{
FT_CMapRec cmap;
FT_UInt32 bc; /* Beginning Character */
FT_UInt32 ec; /* End Character */
} PK_CMapRec, *PK_CMap;
FT_CALLBACK_DEF( FT_Error )
pk_cmap_init( FT_CMap pkcmap,
FT_Pointer init_data )
{
PK_CMap cmap = (PK_CMap)pkcmap;
PK_Face face = (PK_Face)FT_CMAP_FACE( cmap );
FT_UNUSED( init_data );
cmap->bc = face->pk_glyph->code_min;
cmap->ec = face->pk_glyph->code_max;
return FT_Err_Ok;
}
FT_CALLBACK_DEF( void )
pk_cmap_done( FT_CMap pkcmap )
{
PK_CMap cmap = (PK_CMap)pkcmap;
cmap->bc = 0;
cmap->ec = -1;
}
FT_CALLBACK_DEF( FT_UInt )
pk_cmap_char_index( FT_CMap pkcmap,
FT_UInt32 char_code )
{
FT_UInt gindex = 0;
PK_CMap cmap = (PK_CMap)pkcmap;
char_code -= cmap->bc;
if ( char_code < cmap->ec - cmap->bc + 1 )
gindex = (FT_UInt)( char_code );
return gindex;
}
FT_CALLBACK_DEF( FT_UInt )
pk_cmap_char_next( FT_CMap pkcmap,
FT_UInt32 *achar_code )
{
PK_CMap cmap = (PK_CMap)pkcmap;
FT_UInt gindex = 0;
FT_UInt32 result = 0;
FT_UInt32 char_code = *achar_code + 1;
if ( char_code <= cmap->bc )
{
result = cmap->bc;
gindex = 1;
}
else
{
char_code -= cmap->bc;
if ( char_code < cmap->ec - cmap->bc + 1 )
{
result = char_code;
gindex = (FT_UInt)( char_code );
}
}
*achar_code = result;
return gindex;
}
static
const FT_CMap_ClassRec pk_cmap_class =
{
sizeof ( PK_CMapRec ),
pk_cmap_init,
pk_cmap_done,
pk_cmap_char_index,
pk_cmap_char_next,
NULL, NULL, NULL, NULL, NULL
};
FT_CALLBACK_DEF( void )
PK_Face_Done( FT_Face pkface ) /* PK_Face */
{
PK_Face face = (PK_Face)pkface;
FT_Memory memory;
if ( !face )
return;
memory = FT_FACE_MEMORY( face );
pk_free_font( face );
FT_FREE( pkface->available_sizes );
}
FT_CALLBACK_DEF( FT_Error )
PK_Face_Init( FT_Stream stream,
FT_Face pkface, /* PK_Face */
FT_Int face_index,
FT_Int num_params,
FT_Parameter* params )
{
PK_Face face = (PK_Face)pkface;
FT_Error error = FT_Err_Ok;
FT_Memory memory = FT_FACE_MEMORY( face );
PK_Glyph go=NULL;
FT_UInt16 i,count;
TFM_Service tfm;
FT_UNUSED( num_params );
FT_UNUSED( params );
face->tfm = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ),
"tfm" );
tfm = (TFM_Service)face->tfm;
if ( !tfm )
{
FT_ERROR(( "GF_Face_Init: cannot access `tfm' module\n" ));
error = FT_THROW( Missing_Module );
goto Exit;
}
FT_TRACE2(( "PK driver\n" ));
/* load font */
error = pk_load_font( stream, memory, &go );
if ( FT_ERR_EQ( error, Unknown_File_Format ) )
{
FT_TRACE2(( " not a PK file\n" ));
goto Fail;
}
else if ( error )
goto Exit;
/* we have a pk font: let's construct the face object */
face->pk_glyph = go ;
/* sanity check */
if ( !face->pk_glyph->bm_table )
{
FT_TRACE2(( "glyph bitmaps not allocated\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
/* PK cannot have multiple faces in a single font file.
* XXX: non-zero face_index is already invalid argument, but
* Type1, Type42 driver has a convention to return
* an invalid argument error when the font could be
* opened by the specified driver.
*/
if ( face_index > 0 && ( face_index & 0xFFFF ) > 0 )
{
FT_ERROR(( "PK_Face_Init: invalid face index\n" ));
PK_Face_Done( pkface );
return FT_THROW( Invalid_Argument );
}
/* we now need to fill the root FT_Face fields */
/* with relevant information */
pkface->num_faces = 1;
pkface->face_index = 0;
pkface->face_flags |= FT_FACE_FLAG_FIXED_SIZES |
FT_FACE_FLAG_HORIZONTAL ;
/*
* XXX: TO-DO: pkface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
* XXX: I have to check for this.
*/
pkface->family_name = NULL;
count=0;
for (i = 0; i < 256; i++)
{
if(go->bm_table[i].bitmap != NULL)
count++;
}
pkface->num_glyphs = (FT_Long)count;
FT_TRACE4(( " number of glyphs: allocated %d\n",pkface->num_glyphs ));
if ( pkface->num_glyphs <= 0 )
{
FT_ERROR(( "PK_Face_Init: glyphs not allocated\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
pkface->num_fixed_sizes = 1;
if ( FT_NEW_ARRAY( pkface->available_sizes, 1 ) )
goto Exit;
{
FT_Bitmap_Size* bsize = pkface->available_sizes;
FT_UShort x_res, y_res;
bsize->height = (FT_Short) face->pk_glyph->font_bbx_h ;
bsize->width = (FT_Short) face->pk_glyph->font_bbx_w ;
bsize->size = (FT_Pos) FT_MulDiv( FT_ABS( face->pk_glyph->ds ),
64 * 7200,
72270L );
x_res = toint( face->pk_glyph->hppp * 72.27 );
y_res = toint( face->pk_glyph->vppp * 72.27 );
bsize->y_ppem = (FT_Pos) toint((face->pk_glyph->ds * y_res)/ 72.27) << 6 ;
bsize->x_ppem = (FT_Pos)FT_MulDiv( bsize->y_ppem,
x_res,
y_res ); ;
}
/* Charmaps */
{
FT_CharMapRec charmap;
/* Unicode Charmap */
charmap.encoding = FT_ENCODING_UNICODE;
charmap.platform_id = TT_PLATFORM_MICROSOFT;
charmap.encoding_id = TT_MS_ID_UNICODE_CS;
charmap.face = FT_FACE( face );
error = FT_CMap_New( &pk_cmap_class, NULL, &charmap, NULL );
if ( error )
goto Exit;
}
if ( go->code_max < go->code_min )
{
FT_TRACE2(( "invalid number of glyphs\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
Exit:
return error;
Fail:
PK_Face_Done( pkface );
return FT_THROW( Unknown_File_Format );
}
FT_CALLBACK_DEF( FT_Error )
PK_Size_Select( FT_Size size,
FT_ULong strike_index )
{
PK_Face face = (PK_Face)size->face;
PK_Glyph go = face->pk_glyph;
FT_UNUSED( strike_index );
FT_Select_Metrics( size->face, 0 );
size->metrics.ascender = (go->font_bbx_h - go->font_bbx_yoff) * 64;
size->metrics.descender = -go->font_bbx_yoff * 64;
size->metrics.max_advance = go->font_bbx_w * 64;
return FT_Err_Ok;
}
FT_CALLBACK_DEF( FT_Error )
PK_Size_Request( FT_Size size,
FT_Size_Request req )
{
PK_Face face = (PK_Face)size->face;
FT_Bitmap_Size* bsize = size->face->available_sizes;
FT_Error error = FT_ERR( Invalid_Pixel_Size );
FT_Long height;
height = FT_REQUEST_HEIGHT( req );
height = ( height + 32 ) >> 6;
switch ( req->type )
{
case FT_SIZE_REQUEST_TYPE_NOMINAL:
if ( height == ( ( bsize->y_ppem + 32 ) >> 6 ) )
error = FT_Err_Ok;
break;
case FT_SIZE_REQUEST_TYPE_REAL_DIM:
if ( height == face->pk_glyph->font_bbx_h )
error = FT_Err_Ok;
break;
default:
error = FT_THROW( Unimplemented_Feature );
break;
}
if ( error )
return error;
else
return PK_Size_Select( size, 0 );
}
FT_CALLBACK_DEF( FT_Error )
PK_Glyph_Load( FT_GlyphSlot slot,
FT_Size size,
FT_UInt glyph_index,
FT_Int32 load_flags )
{
PK_Face pk = (PK_Face)FT_SIZE_FACE( size );
FT_Face face = FT_FACE( pk );
FT_Error error = FT_Err_Ok;
FT_Bitmap* bitmap = &slot->bitmap;
PK_BitmapRec bm;
PK_Glyph go;
go = pk->pk_glyph;
FT_UNUSED( load_flags );
if ( !face )
{
error = FT_THROW( Invalid_Face_Handle );
goto Exit;
}
if ( !go ||
glyph_index >= (FT_UInt)( face->num_glyphs ) )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
FT_TRACE1(( "PK_Glyph_Load: glyph index %d\n", glyph_index ));
if ( glyph_index < 0 )
glyph_index = 0;
if ((glyph_index < go->code_min) || (go->code_max < glyph_index))
{
FT_TRACE2(( "invalid glyph index\n" ));
error = FT_THROW( Invalid_Argument );
goto Exit;
}
if ( !go->bm_table )
{
FT_TRACE2(( "invalid bitmap table\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
/* slot, bitmap => freetype, bm => pklib */
bm = pk->pk_glyph->bm_table[glyph_index];
bitmap->rows = bm.bbx_height;
bitmap->width = bm.bbx_width;
bitmap->pixel_mode = FT_PIXEL_MODE_MONO;
if ( !bm.raster )
{
FT_TRACE2(( "invalid bitmap width\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
bitmap->pitch = (int)bm.raster ;
/* note: we don't allocate a new array to hold the bitmap; */
/* we can simply point to it */
ft_glyphslot_set_bitmap( slot, bm.bitmap );
slot->format = FT_GLYPH_FORMAT_BITMAP;
slot->bitmap_left = bm.off_x ;
slot->bitmap_top = bm.off_y ;
slot->metrics.horiAdvance = (FT_Pos) (bm.mv_x ) * 64;
slot->metrics.horiBearingX = (FT_Pos) (bm.off_x ) * 64;
slot->metrics.horiBearingY = (FT_Pos) (bm.bbx_height) * 64;
slot->metrics.width = (FT_Pos) ( bitmap->width * 64 );
slot->metrics.height = (FT_Pos) ( bitmap->rows * 64 );
ft_synthesize_vertical_metrics( &slot->metrics, bm.bbx_height * 64 );
Exit:
return error;
}
FT_LOCAL_DEF( void )
TFM_Done_Metrics( FT_Memory memory,
TFM_FontInfo fi )
{
FT_FREE(fi->width);
FT_FREE(fi->height);
FT_FREE(fi->depth);
FT_FREE( fi );
}
/* parse a TFM metrics file */
FT_LOCAL_DEF( FT_Error )
TFM_Read_Metrics( FT_Face pk_face,
FT_Stream stream )
{
TFM_Service tfm;
FT_Memory memory = stream->memory;
TFM_ParserRec parser;
TFM_FontInfo fi = NULL;
FT_Error error = FT_ERR( Unknown_File_Format );
PK_Face face = (PK_Face)pk_face;
PK_Glyph pk_glyph= face->pk_glyph;
if ( face->tfm_data )
{
FT_TRACE1(( "TFM_Read_Metrics:"
" Freeing previously attached metrics data.\n" ));
TFM_Done_Metrics( memory, (TFM_FontInfo)face->tfm_data );
face->tfm_data = NULL;
}
if ( FT_NEW( fi ) )
goto Exit;
FT_TRACE4(( "TFM_Read_Metrics: Invoking TFM_Service.\n" ));
tfm = (TFM_Service)face->tfm;
if ( tfm->tfm_parser_funcs )
{
/* Initialise TFM Service */
error = tfm->tfm_parser_funcs->init( &parser,
memory,
stream );
if ( !error )
{
FT_TRACE4(( "TFM_Read_Metrics: Initialised tfm metric data.\n" ));
parser.FontInfo = fi;
parser.user_data = pk_glyph;
error = tfm->tfm_parser_funcs->parse_metrics( &parser );
if( !error )
FT_TRACE4(( "TFM_Read_Metrics: parsing TFM metric information done.\n" ));
FT_TRACE6(( "TFM_Read_Metrics: TFM Metric Information:\n"
" Check Sum : %ld\n"
" Design Size: %ld\n"
" Begin Char : %d\n"
" End Char : %d\n"
" font_bbx_w : %d\n"
" font_bbx_h : %d\n"
" slant : %d\n", parser.FontInfo->cs, parser.FontInfo->design_size, parser.FontInfo->begin_char,
parser.FontInfo->end_char, parser.FontInfo->font_bbx_w,
parser.FontInfo->font_bbx_h, parser.FontInfo->slant ));
tfm->tfm_parser_funcs->done( &parser );
}
}
if ( !error )
{
/* Modify PK_Glyph data according to TFM metric values */
/*face->pk_glyph->font_bbx_w = fi->font_bbx_w;
face->pk_glyph->font_bbx_h = fi->font_bbx_h;
*/
face->tfm_data = fi;
}
Exit:
if ( fi )
TFM_Done_Metrics( memory, fi );
return error;
}
/*
*
* SERVICES LIST
*
*/
static const FT_ServiceDescRec pk_services[] =
{
{ FT_SERVICE_ID_PK, NULL },
{ FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_PK },
{ NULL, NULL }
};
FT_CALLBACK_DEF( FT_Module_Interface )
pk_driver_requester( FT_Module module,
const char* name )
{
FT_UNUSED( module );
return ft_service_list_lookup( pk_services, name );
}
FT_CALLBACK_TABLE_DEF
const FT_Driver_ClassRec pk_driver_class =
{
{
FT_MODULE_FONT_DRIVER |
FT_MODULE_DRIVER_NO_OUTLINES,
sizeof ( FT_DriverRec ),
"pk",
0x10000L,
0x20000L,
NULL, /* module-specific interface */
NULL, /* FT_Module_Constructor module_init */
NULL, /* FT_Module_Destructor module_done */
pk_driver_requester /* FT_Module_Requester get_interface */
},
sizeof ( PK_FaceRec ),
sizeof ( FT_SizeRec ),
sizeof ( FT_GlyphSlotRec ),
PK_Face_Init, /* FT_Face_InitFunc init_face */
PK_Face_Done, /* FT_Face_DoneFunc done_face */
NULL, /* FT_Size_InitFunc init_size */
NULL, /* FT_Size_DoneFunc done_size */
NULL, /* FT_Slot_InitFunc init_slot */
NULL, /* FT_Slot_DoneFunc done_slot */
PK_Glyph_Load, /* FT_Slot_LoadFunc load_glyph */
NULL, /* FT_Face_GetKerningFunc get_kerning */
TFM_Read_Metrics, /* FT_Face_AttachFunc attach_file */
NULL, /* FT_Face_GetAdvancesFunc get_advances */
PK_Size_Request, /* FT_Size_RequestFunc request_size */
PK_Size_Select /* FT_Size_SelectFunc select_size */
};
/* END */