| /**************************************************************************** |
| * |
| * gfdrivr.c |
| * |
| * FreeType font driver for METAFONT GF 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_GF_H |
| #include FT_SERVICE_FONT_FORMAT_H |
| |
| #include "gf.h" |
| #include "gfdrivr.h" |
| #include "gferror.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_gfdriver |
| |
| |
| typedef struct GF_CMapRec_ |
| { |
| FT_CMapRec cmap; |
| FT_ULong num_encodings; |
| GF_Encoding encodings; |
| } GF_CMapRec, *GF_CMap; |
| |
| |
| FT_CALLBACK_DEF( FT_Error ) |
| gf_cmap_init( FT_CMap gfcmap, |
| FT_Pointer init_data ) |
| { |
| GF_CMap cmap = (GF_CMap)gfcmap; |
| GF_Face face = (GF_Face)FT_CMAP_FACE( cmap ); |
| FT_UNUSED( init_data ); |
| |
| cmap->num_encodings = face->gf_glyph->nencodings; |
| cmap->encodings = face->gf_glyph->encodings; |
| |
| return FT_Err_Ok; |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| gf_cmap_done( FT_CMap gfcmap ) |
| { |
| GF_CMap cmap = (GF_CMap)gfcmap; |
| |
| cmap->encodings = NULL; |
| cmap->num_encodings = 0; |
| |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_UInt ) |
| gf_cmap_char_index( FT_CMap gfcmap, |
| FT_UInt32 charcode ) |
| { |
| GF_CMap cmap = (GF_CMap)gfcmap; |
| GF_Encoding encodings = cmap->encodings; |
| FT_ULong min, max, mid; |
| FT_UInt result = 0; |
| |
| min = 0; |
| max = cmap->num_encodings; |
| |
| while ( min < max ) |
| { |
| FT_ULong code; |
| |
| |
| mid = ( min + max ) >> 1; |
| code = (FT_ULong)encodings[mid].enc; |
| |
| if ( charcode == code ) |
| { |
| result = encodings[mid].glyph; |
| break; |
| } |
| |
| if ( charcode < code ) |
| max = mid; |
| else |
| min = mid + 1; |
| } |
| |
| return result; |
| } |
| |
| FT_CALLBACK_DEF( FT_UInt ) |
| gf_cmap_char_next( FT_CMap gfcmap, |
| FT_UInt32 *acharcode ) |
| { |
| GF_CMap cmap = (GF_CMap)gfcmap; |
| GF_Encoding encodings = cmap->encodings; |
| FT_ULong min, max, mid; |
| FT_ULong charcode = *acharcode + 1; |
| FT_UInt result = 0; |
| |
| |
| min = 0; |
| max = cmap->num_encodings; |
| |
| while ( min < max ) |
| { |
| FT_ULong code; |
| |
| |
| mid = ( min + max ) >> 1; |
| code = (FT_ULong)encodings[mid].enc; |
| |
| if ( charcode == code ) |
| { |
| result = encodings[mid].glyph + 1; |
| goto Exit; |
| } |
| |
| if ( charcode < code ) |
| max = mid; |
| else |
| min = mid + 1; |
| } |
| |
| charcode = 0; |
| if ( min < cmap->num_encodings ) |
| { |
| charcode = (FT_ULong)encodings[min].enc; |
| result = encodings[min].glyph ; |
| } |
| |
| Exit: |
| if ( charcode > 0xFFFFFFFFUL ) |
| { |
| FT_TRACE1(( "gf_cmap_char_next: charcode 0x%x > 32bit API" )); |
| *acharcode = 0; |
| /* XXX: result should be changed to indicate an overflow error */ |
| } |
| else |
| *acharcode = (FT_UInt32)charcode; |
| return result; |
| } |
| |
| |
| static |
| const FT_CMap_ClassRec gf_cmap_class = |
| { |
| sizeof ( GF_CMapRec ), |
| gf_cmap_init, |
| gf_cmap_done, |
| gf_cmap_char_index, |
| gf_cmap_char_next, |
| |
| NULL, NULL, NULL, NULL, NULL |
| }; |
| |
| |
| FT_CALLBACK_DEF( void ) |
| GF_Face_Done( FT_Face gfface ) /* GF_Face */ |
| { |
| GF_Face face = (GF_Face)gfface; |
| FT_Memory memory; |
| |
| |
| if ( !face ) |
| return; |
| |
| memory = FT_FACE_MEMORY( face ); |
| |
| FT_FREE( gfface->available_sizes ); |
| FT_FREE( face->gf_glyph->encodings ); |
| FT_FREE( face->gf_glyph->metrics ); |
| gf_free_font( face ); |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_Error ) |
| GF_Face_Init( FT_Stream stream, |
| FT_Face gfface, /* GF_Face */ |
| FT_Int face_index, |
| FT_Int num_params, |
| FT_Parameter* params ) |
| { |
| GF_Face face = (GF_Face)gfface; |
| FT_Error error = FT_Err_Ok; |
| FT_Memory memory = FT_FACE_MEMORY( face ); |
| GF_Glyph go=NULL; |
| |
| 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(( "GF driver\n" )); |
| |
| /* load font */ |
| error = gf_load_font( stream, memory, &go ); |
| if ( FT_ERR_EQ( error, Unknown_File_Format ) ) |
| { |
| FT_TRACE2(( " not a GF file\n" )); |
| goto Fail; |
| } |
| else if ( error ) |
| goto Exit; |
| |
| /* we have a gf font: let's construct the face object */ |
| face->gf_glyph = go ; |
| |
| /* sanity check */ |
| if ( !face->gf_glyph->metrics ) |
| { |
| FT_TRACE2(( "glyph metrics missing\n" )); |
| error = FT_THROW( Invalid_File_Format ); |
| goto Exit; |
| } |
| |
| /* GF 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(( "GF_Face_Init: invalid face index\n" )); |
| GF_Face_Done( gfface ); |
| return FT_THROW( Invalid_Argument ); |
| } |
| |
| /* we now need to fill the root FT_Face fields */ |
| /* with relevant information */ |
| |
| gfface->num_faces = 1; |
| gfface->face_index = 0; |
| gfface->face_flags |= FT_FACE_FLAG_FIXED_SIZES | |
| FT_FACE_FLAG_HORIZONTAL ; |
| /* |
| * XXX: TO-DO: gfface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; |
| * XXX: I have to check for this. |
| */ |
| |
| gfface->family_name = NULL; |
| gfface->num_glyphs = (FT_Long)face->gf_glyph->nglyphs; |
| |
| FT_TRACE4(( " number of glyphs: allocated %d\n",gfface->num_glyphs )); |
| |
| if ( gfface->num_glyphs <= 0 ) |
| { |
| FT_ERROR(( "GF_Face_Init: glyphs not allocated\n" )); |
| error = FT_THROW( Invalid_File_Format ); |
| goto Exit; |
| } |
| |
| gfface->num_fixed_sizes = 1; |
| if ( FT_NEW_ARRAY( gfface->available_sizes, 1 ) ) |
| goto Exit; |
| |
| { |
| FT_Bitmap_Size* bsize = gfface->available_sizes; |
| FT_UShort x_res, y_res; |
| |
| bsize->height = (FT_Short) face->gf_glyph->font_bbx_h ; |
| bsize->width = (FT_Short) face->gf_glyph->font_bbx_w ; |
| bsize->size = (FT_Pos) FT_MulDiv( FT_ABS( face->gf_glyph->ds ), |
| 64 * 7200, |
| 72270L ); |
| |
| x_res = toint( go->hppp * 72.27 ); |
| y_res = toint( go->vppp * 72.27 ); |
| |
| bsize->y_ppem = (FT_Pos) toint((face->gf_glyph->ds * y_res)/ 72.27) << 6 ; |
| bsize->x_ppem = (FT_Pos)FT_MulDiv( bsize->y_ppem, |
| x_res, |
| y_res ); ; |
| } |
| |
| /* set up charmap */ |
| { |
| /* FT_Bool unicode_charmap ; */ |
| |
| /* XXX: TO-DO |
| Currently the unicode_charmap is set to `0' |
| The functionality of extracting coding scheme |
| from `xxx' and `yyy' commands will be used to |
| set the unicode_charmap. |
| */ |
| } |
| |
| /* Charmaps */ |
| { |
| FT_CharMapRec charmap; |
| FT_Bool unicode_charmap = 0; |
| |
| charmap.face = FT_FACE( face ); |
| charmap.encoding = FT_ENCODING_NONE; |
| /* initial platform/encoding should indicate unset status? */ |
| charmap.platform_id = TT_PLATFORM_APPLE_UNICODE; |
| charmap.encoding_id = TT_APPLE_ID_DEFAULT; |
| |
| if( unicode_charmap ) |
| { |
| /* Unicode Charmap */ |
| charmap.encoding = FT_ENCODING_UNICODE; |
| charmap.platform_id = TT_PLATFORM_MICROSOFT; |
| charmap.encoding_id = TT_MS_ID_UNICODE_CS; |
| } |
| |
| error = FT_CMap_New( &gf_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: |
| GF_Face_Done( gfface ); |
| return FT_THROW( Unknown_File_Format ); |
| } |
| |
| FT_CALLBACK_DEF( FT_Error ) |
| GF_Size_Select( FT_Size size, |
| FT_ULong strike_index ) |
| { |
| GF_Face face = (GF_Face)size->face; |
| GF_Glyph go = face->gf_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 ) |
| GF_Size_Request( FT_Size size, |
| FT_Size_Request req ) |
| { |
| GF_Face face = (GF_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->gf_glyph->font_bbx_h ) |
| error = FT_Err_Ok; |
| break; |
| |
| default: |
| error = FT_THROW( Unimplemented_Feature ); |
| break; |
| } |
| |
| if ( error ) |
| return error; |
| else |
| return GF_Size_Select( size, 0 ); |
| } |
| |
| |
| |
| FT_CALLBACK_DEF( FT_Error ) |
| GF_Glyph_Load( FT_GlyphSlot slot, |
| FT_Size size, |
| FT_UInt glyph_index, |
| FT_Int32 load_flags ) |
| { |
| GF_Face gf = (GF_Face)FT_SIZE_FACE( size ); |
| FT_Face face = FT_FACE( gf ); |
| FT_Stream stream; |
| FT_Error error = FT_Err_Ok; |
| FT_Bitmap* bitmap = &slot->bitmap; |
| GF_MetricRec metric; |
| GF_Glyph go; |
| FT_ULong bytes; |
| |
| go = gf->gf_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(( "GF_Glyph_Load: glyph index %d and charcode is %d\n", glyph_index, go->metrics[glyph_index].code )); |
| |
| if ( (FT_Int)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; |
| } |
| |
| stream = gf->root.stream; |
| metric = gf->gf_glyph->metrics[glyph_index]; |
| |
| error = gf_read_glyph( stream, |
| &metric ); |
| |
| bitmap->rows = metric.bbx_height; |
| bitmap->width = metric.bbx_width; |
| bitmap->pixel_mode = FT_PIXEL_MODE_MONO; |
| |
| bitmap->pitch = (int)( ( bitmap->width + 7 ) >> 3 ); |
| |
| slot->format = FT_GLYPH_FORMAT_BITMAP; |
| slot->bitmap_left = metric.off_x ; |
| slot->bitmap_top = metric.off_y ; |
| |
| slot->metrics.horiAdvance = (FT_Pos) ( metric.mv_x ) * 64; |
| slot->metrics.horiBearingX = (FT_Pos) ( metric.off_x ) * 64; |
| slot->metrics.horiBearingY = (FT_Pos) ( metric.bbx_height) * 64; |
| slot->metrics.width = (FT_Pos) ( bitmap->width * 64 ); |
| slot->metrics.height = (FT_Pos) ( bitmap->rows * 64 ); |
| |
| FT_TRACE2(( "Glyph metric values are: metric.bbx_height is %ld\n" |
| " metric.bbx_width is %ld\n" |
| " metric.off_x is %ld\n" |
| " metric.off_y is %ld\n" |
| " metric.mv_x is %ld\n" |
| " metric.mv_y is %ld\n", metric.bbx_height, metric.bbx_width, |
| metric.off_x, metric.off_y, metric.mv_x, |
| metric.mv_y )); |
| |
| ft_synthesize_vertical_metrics( &slot->metrics, metric.bbx_height * 64 ); |
| |
| if ( load_flags & FT_LOAD_BITMAP_METRICS_ONLY ) |
| goto Exit; |
| |
| bytes = (FT_ULong)bitmap->pitch * bitmap->rows; |
| |
| error = ft_glyphslot_alloc_bitmap( slot, (FT_ULong)bytes ); |
| memset( slot->bitmap.buffer, 0, bytes ); |
| |
| 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 gf_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 ); |
| GF_Face face = (GF_Face)gf_face; |
| GF_Glyph gf_glyph= face->gf_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 = gf_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 GF_Glyph data according to TFM metric values */ |
| |
| /*face->gf_glyph->font_bbx_w = fi->font_bbx_w; |
| face->gf_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 gf_services[] = |
| { |
| { FT_SERVICE_ID_GF, NULL }, |
| { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_GF }, |
| { NULL, NULL } |
| }; |
| |
| FT_CALLBACK_DEF( FT_Module_Interface ) |
| gf_driver_requester( FT_Module module, |
| const char* name ) |
| { |
| FT_UNUSED( module ); |
| |
| return ft_service_list_lookup( gf_services, name ); |
| } |
| |
| |
| FT_CALLBACK_TABLE_DEF |
| const FT_Driver_ClassRec gf_driver_class = |
| { |
| { |
| FT_MODULE_FONT_DRIVER | |
| FT_MODULE_DRIVER_NO_OUTLINES, |
| sizeof ( FT_DriverRec ), |
| |
| "gf", |
| 0x10000L, |
| 0x20000L, |
| |
| NULL, /* module-specific interface */ |
| |
| NULL, /* FT_Module_Constructor module_init */ |
| NULL, /* FT_Module_Destructor module_done */ |
| gf_driver_requester /* FT_Module_Requester get_interface */ |
| }, |
| |
| sizeof ( GF_FaceRec ), |
| sizeof ( FT_SizeRec ), |
| sizeof ( FT_GlyphSlotRec ), |
| |
| GF_Face_Init, /* FT_Face_InitFunc init_face */ |
| GF_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 */ |
| |
| GF_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 */ |
| |
| GF_Size_Request, /* FT_Size_RequestFunc request_size */ |
| GF_Size_Select /* FT_Size_SelectFunc select_size */ |
| }; |
| |
| |
| /* END */ |