Source code
Revision control
Copy as Markdown
Other Tools
/****************************************************************************
*
* ttsbit.c
*
* TrueType and OpenType embedded bitmap support (body).
*
* Copyright (C) 2005-2024 by
* David Turner, Robert Wilhelm, and Werner Lemberg.
*
* Copyright 2013 by Google, Inc.
* Google Author(s): Behdad Esfahbod.
*
* 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 <freetype/internal/ftdebug.h>
#include <freetype/internal/ftstream.h>
#include <freetype/tttags.h>
#include <freetype/ftbitmap.h>
#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
#include "ttsbit.h"
#include "sferrors.h"
#include "ttmtx.h"
#include "pngshim.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 ttsbit
FT_LOCAL_DEF( FT_Error )
tt_face_load_sbit( TT_Face face,
FT_Stream stream )
{
FT_Error error;
FT_ULong table_size;
FT_ULong table_start;
face->sbit_table = NULL;
face->sbit_table_size = 0;
face->sbit_table_type = TT_SBIT_TABLE_TYPE_NONE;
face->sbit_num_strikes = 0;
error = face->goto_table( face, TTAG_CBLC, stream, &table_size );
if ( !error )
face->sbit_table_type = TT_SBIT_TABLE_TYPE_CBLC;
else
{
error = face->goto_table( face, TTAG_EBLC, stream, &table_size );
if ( error )
error = face->goto_table( face, TTAG_bloc, stream, &table_size );
if ( !error )
face->sbit_table_type = TT_SBIT_TABLE_TYPE_EBLC;
}
if ( error )
{
error = face->goto_table( face, TTAG_sbix, stream, &table_size );
if ( !error )
face->sbit_table_type = TT_SBIT_TABLE_TYPE_SBIX;
}
if ( error )
goto Exit;
if ( table_size < 8 )
{
FT_ERROR(( "tt_face_load_sbit_strikes: table too short\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
table_start = FT_STREAM_POS();
switch ( (FT_UInt)face->sbit_table_type )
{
case TT_SBIT_TABLE_TYPE_EBLC:
case TT_SBIT_TABLE_TYPE_CBLC:
{
FT_Byte* p;
FT_Fixed version;
FT_ULong num_strikes;
FT_UInt count;
if ( FT_FRAME_EXTRACT( table_size, face->sbit_table ) )
goto Exit;
face->sbit_table_size = table_size;
p = face->sbit_table;
version = FT_NEXT_LONG( p );
num_strikes = FT_NEXT_ULONG( p );
/* there's at least one font (FZShuSong-Z01, version 3) */
/* that uses the wrong byte order for the `version' field */
if ( ( (FT_ULong)version & 0xFFFF0000UL ) != 0x00020000UL &&
( (FT_ULong)version & 0x0000FFFFUL ) != 0x00000200UL &&
( (FT_ULong)version & 0xFFFF0000UL ) != 0x00030000UL &&
( (FT_ULong)version & 0x0000FFFFUL ) != 0x00000300UL )
{
error = FT_THROW( Unknown_File_Format );
goto Exit;
}
if ( num_strikes >= 0x10000UL )
{
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
/*
* Count the number of strikes available in the table. We are a bit
* paranoid there and don't trust the data.
*/
count = (FT_UInt)num_strikes;
if ( 8 + 48UL * count > table_size )
count = (FT_UInt)( ( table_size - 8 ) / 48 );
face->sbit_num_strikes = count;
}
break;
case TT_SBIT_TABLE_TYPE_SBIX:
{
FT_UShort version;
FT_UShort flags;
FT_ULong num_strikes;
FT_UInt count;
if ( FT_FRAME_ENTER( 8 ) )
goto Exit;
version = FT_GET_USHORT();
flags = FT_GET_USHORT();
num_strikes = FT_GET_ULONG();
FT_FRAME_EXIT();
if ( version < 1 )
{
error = FT_THROW( Unknown_File_Format );
goto Exit;
}
/* Bit 0 must always be `1'. */
/* Bit 1 controls the overlay of bitmaps with outlines. */
/* All other bits should be zero. */
if ( !( flags == 1 || flags == 3 ) ||
num_strikes >= 0x10000UL )
{
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
if ( flags == 3 )
face->root.face_flags |= FT_FACE_FLAG_SBIX_OVERLAY;
/*
* Count the number of strikes available in the table. We are a bit
* paranoid there and don't trust the data.
*/
count = (FT_UInt)num_strikes;
if ( 8 + 4UL * count > table_size )
count = (FT_UInt)( ( table_size - 8 ) / 4 );
if ( FT_STREAM_SEEK( FT_STREAM_POS() - 8 ) )
goto Exit;
face->sbit_table_size = 8 + count * 4;
if ( FT_FRAME_EXTRACT( face->sbit_table_size, face->sbit_table ) )
goto Exit;
face->sbit_num_strikes = count;
}
break;
default:
/* we ignore unknown table formats */
error = FT_THROW( Unknown_File_Format );
break;
}
if ( !error )
FT_TRACE3(( "tt_face_load_sbit_strikes: found %u strikes\n",
face->sbit_num_strikes ));
face->ebdt_start = 0;
face->ebdt_size = 0;
if ( face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX )
{
/* the `sbix' table is self-contained; */
/* it has no associated data table */
face->ebdt_start = table_start;
face->ebdt_size = table_size;
}
else if ( face->sbit_table_type != TT_SBIT_TABLE_TYPE_NONE )
{
FT_ULong ebdt_size;
error = face->goto_table( face, TTAG_CBDT, stream, &ebdt_size );
if ( error )
error = face->goto_table( face, TTAG_EBDT, stream, &ebdt_size );
if ( error )
error = face->goto_table( face, TTAG_bdat, stream, &ebdt_size );
if ( !error )
{
face->ebdt_start = FT_STREAM_POS();
face->ebdt_size = ebdt_size;
}
}
if ( !face->ebdt_size )
{
FT_TRACE2(( "tt_face_load_sbit_strikes:"
" no embedded bitmap data table found;\n" ));
FT_TRACE2(( " "
" resetting number of strikes to zero\n" ));
face->sbit_num_strikes = 0;
}
return FT_Err_Ok;
Exit:
if ( error )
{
if ( face->sbit_table )
FT_FRAME_RELEASE( face->sbit_table );
face->sbit_table_size = 0;
face->sbit_table_type = TT_SBIT_TABLE_TYPE_NONE;
}
return error;
}
FT_LOCAL_DEF( void )
tt_face_free_sbit( TT_Face face )
{
FT_Stream stream = face->root.stream;
FT_FRAME_RELEASE( face->sbit_table );
face->sbit_table_size = 0;
face->sbit_table_type = TT_SBIT_TABLE_TYPE_NONE;
face->sbit_num_strikes = 0;
}
FT_LOCAL_DEF( FT_Error )
tt_face_set_sbit_strike( TT_Face face,
FT_Size_Request req,
FT_ULong* astrike_index )
{
return FT_Match_Size( (FT_Face)face, req, 0, astrike_index );
}
FT_LOCAL_DEF( FT_Error )
tt_face_load_strike_metrics( TT_Face face,
FT_ULong strike_index,
FT_Size_Metrics* metrics )
{
/* we have to test for the existence of `sbit_strike_map' */
/* because the function gets also used at the very beginning */
/* to construct `sbit_strike_map' itself */
if ( face->sbit_strike_map )
{
if ( strike_index >= (FT_ULong)face->root.num_fixed_sizes )
return FT_THROW( Invalid_Argument );
/* map to real index */
strike_index = face->sbit_strike_map[strike_index];
}
else
{
if ( strike_index >= (FT_ULong)face->sbit_num_strikes )
return FT_THROW( Invalid_Argument );
}
switch ( (FT_UInt)face->sbit_table_type )
{
case TT_SBIT_TABLE_TYPE_EBLC:
case TT_SBIT_TABLE_TYPE_CBLC:
{
FT_Byte* strike;
FT_Char max_before_bl;
FT_Char min_after_bl;
strike = face->sbit_table + 8 + strike_index * 48;
metrics->x_ppem = (FT_UShort)strike[44];
metrics->y_ppem = (FT_UShort)strike[45];
metrics->ascender = (FT_Char)strike[16] * 64; /* hori.ascender */
metrics->descender = (FT_Char)strike[17] * 64; /* hori.descender */
/* Due to fuzzy wording in the EBLC documentation, we find both */
/* positive and negative values for `descender'. Additionally, */
/* many fonts have both `ascender' and `descender' set to zero */
/* (which is definitely wrong). MS Windows simply ignores all */
/* those values... For these reasons we apply some heuristics */
/* to get a reasonable, non-zero value for the height. */
max_before_bl = (FT_Char)strike[24];
min_after_bl = (FT_Char)strike[25];
if ( metrics->descender > 0 )
{
/* compare sign of descender with `min_after_bl' */
if ( min_after_bl < 0 )
metrics->descender = -metrics->descender;
}
else if ( metrics->descender == 0 )
{
if ( metrics->ascender == 0 )
{
FT_TRACE2(( "tt_face_load_strike_metrics:"
" sanitizing invalid ascender and descender\n" ));
FT_TRACE2(( " "
" values for strike %ld (%dppem, %dppem)\n",
strike_index,
metrics->x_ppem, metrics->y_ppem ));
/* sanitize buggy ascender and descender values */
if ( max_before_bl || min_after_bl )
{
metrics->ascender = max_before_bl * 64;
metrics->descender = min_after_bl * 64;
}
else
{
metrics->ascender = metrics->y_ppem * 64;
metrics->descender = 0;
}
}
}
#if 0
else
; /* if we have a negative descender, simply use it */
#endif
metrics->height = metrics->ascender - metrics->descender;
if ( metrics->height == 0 )
{
FT_TRACE2(( "tt_face_load_strike_metrics:"
" sanitizing invalid height value\n" ));
FT_TRACE2(( " "
" for strike (%d, %d)\n",
metrics->x_ppem, metrics->y_ppem ));
metrics->height = metrics->y_ppem * 64;
metrics->descender = metrics->ascender - metrics->height;
}
/* Is this correct? */
metrics->max_advance = ( (FT_Char)strike[22] + /* min_origin_SB */
strike[18] + /* max_width */
(FT_Char)strike[23] /* min_advance_SB */
) * 64;
/* set the scale values (in 16.16 units) so advances */
/* from the hmtx and vmtx table are scaled correctly */
metrics->x_scale = FT_DivFix( metrics->x_ppem * 64,
face->header.Units_Per_EM );
metrics->y_scale = FT_DivFix( metrics->y_ppem * 64,
face->header.Units_Per_EM );
return FT_Err_Ok;
}
case TT_SBIT_TABLE_TYPE_SBIX:
{
FT_Stream stream = face->root.stream;
FT_UInt offset;
FT_UShort ppem, resolution;
TT_HoriHeader *hori;
FT_Fixed scale;
FT_Error error;
FT_Byte* p;
p = face->sbit_table + 8 + 4 * strike_index;
offset = FT_NEXT_ULONG( p );
if ( offset + 4 > face->ebdt_size )
return FT_THROW( Invalid_File_Format );
if ( FT_STREAM_SEEK( face->ebdt_start + offset ) ||
FT_FRAME_ENTER( 4 ) )
return error;
ppem = FT_GET_USHORT();
resolution = FT_GET_USHORT();
FT_UNUSED( resolution ); /* What to do with this? */
FT_FRAME_EXIT();
metrics->x_ppem = ppem;
metrics->y_ppem = ppem;
scale = FT_DivFix( ppem * 64, face->header.Units_Per_EM );
hori = &face->horizontal;
metrics->ascender = FT_MulFix( hori->Ascender, scale );
metrics->descender = FT_MulFix( hori->Descender, scale );
metrics->height =
FT_MulFix( hori->Ascender - hori->Descender + hori->Line_Gap,
scale );
metrics->max_advance = FT_MulFix( hori->advance_Width_Max, scale );
/* set the scale values (in 16.16 units) so advances */
/* from the hmtx and vmtx table are scaled correctly */
metrics->x_scale = scale;
metrics->y_scale = scale;
return error;
}
default:
return FT_THROW( Unknown_File_Format );
}
}
typedef struct TT_SBitDecoderRec_
{
TT_Face face;
FT_Stream stream;
FT_Bitmap* bitmap;
TT_SBit_Metrics metrics;
FT_Bool metrics_loaded;
FT_Bool bitmap_allocated;
FT_Byte bit_depth;
FT_ULong ebdt_start;
FT_ULong ebdt_size;
FT_ULong strike_index_array;
FT_ULong strike_index_count;
FT_Byte* eblc_base;
FT_Byte* eblc_limit;
} TT_SBitDecoderRec, *TT_SBitDecoder;
static FT_Error
tt_sbit_decoder_init( TT_SBitDecoder decoder,
TT_Face face,
FT_ULong strike_index,
TT_SBit_MetricsRec* metrics )
{
FT_Error error = FT_ERR( Table_Missing );
FT_Stream stream = face->root.stream;
strike_index = face->sbit_strike_map[strike_index];
if ( !face->ebdt_size )
goto Exit;
if ( FT_STREAM_SEEK( face->ebdt_start ) )
goto Exit;
decoder->face = face;
decoder->stream = stream;
decoder->bitmap = &face->root.glyph->bitmap;
decoder->metrics = metrics;
decoder->metrics_loaded = 0;
decoder->bitmap_allocated = 0;
decoder->ebdt_start = face->ebdt_start;
decoder->ebdt_size = face->ebdt_size;
decoder->eblc_base = face->sbit_table;
decoder->eblc_limit = face->sbit_table + face->sbit_table_size;
/* now find the strike corresponding to the index */
{
FT_Byte* p;
if ( 8 + 48 * strike_index + 3 * 4 + 34 + 1 > face->sbit_table_size )
{
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
p = decoder->eblc_base + 8 + 48 * strike_index;
decoder->strike_index_array = FT_NEXT_ULONG( p );
p += 4;
decoder->strike_index_count = FT_NEXT_ULONG( p );
p += 34;
decoder->bit_depth = *p;
/* decoder->strike_index_array + */
/* 8 * decoder->strike_index_count > face->sbit_table_size ? */
if ( decoder->strike_index_array > face->sbit_table_size ||
decoder->strike_index_count >
( face->sbit_table_size - decoder->strike_index_array ) / 8 )
error = FT_THROW( Invalid_File_Format );
}
Exit:
return error;
}
static void
tt_sbit_decoder_done( TT_SBitDecoder decoder )
{
FT_UNUSED( decoder );
}
static FT_Error
tt_sbit_decoder_alloc_bitmap( TT_SBitDecoder decoder,
FT_Bool metrics_only )
{
FT_Error error = FT_Err_Ok;
FT_UInt width, height;
FT_Bitmap* map = decoder->bitmap;
FT_ULong size;
if ( !decoder->metrics_loaded )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
width = decoder->metrics->width;
height = decoder->metrics->height;
map->width = width;
map->rows = height;
switch ( decoder->bit_depth )
{
case 1:
map->pixel_mode = FT_PIXEL_MODE_MONO;
map->pitch = (int)( ( map->width + 7 ) >> 3 );
map->num_grays = 2;
break;
case 2:
map->pixel_mode = FT_PIXEL_MODE_GRAY2;
map->pitch = (int)( ( map->width + 3 ) >> 2 );
map->num_grays = 4;
break;
case 4:
map->pixel_mode = FT_PIXEL_MODE_GRAY4;
map->pitch = (int)( ( map->width + 1 ) >> 1 );
map->num_grays = 16;
break;
case 8:
map->pixel_mode = FT_PIXEL_MODE_GRAY;
map->pitch = (int)( map->width );
map->num_grays = 256;
break;
case 32:
map->pixel_mode = FT_PIXEL_MODE_BGRA;
map->pitch = (int)( map->width * 4 );
map->num_grays = 256;
break;
default:
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
size = map->rows * (FT_ULong)map->pitch;
/* check that there is no empty image */
if ( size == 0 )
goto Exit; /* exit successfully! */
if ( metrics_only )
goto Exit; /* only metrics are requested */
error = ft_glyphslot_alloc_bitmap( decoder->face->root.glyph, size );
if ( error )
goto Exit;
decoder->bitmap_allocated = 1;
Exit:
return error;
}
static FT_Error
tt_sbit_decoder_load_metrics( TT_SBitDecoder decoder,
FT_Byte* *pp,
FT_Byte* limit,
FT_Bool big )
{
FT_Byte* p = *pp;
TT_SBit_Metrics metrics = decoder->metrics;
if ( p + 5 > limit )
goto Fail;
metrics->height = p[0];
metrics->width = p[1];
metrics->horiBearingX = (FT_Char)p[2];
metrics->horiBearingY = (FT_Char)p[3];
metrics->horiAdvance = p[4];
p += 5;
if ( big )
{
if ( p + 3 > limit )
goto Fail;
metrics->vertBearingX = (FT_Char)p[0];
metrics->vertBearingY = (FT_Char)p[1];
metrics->vertAdvance = p[2];
p += 3;
}
else
{
/* avoid uninitialized data in case there is no vertical info -- */
metrics->vertBearingX = 0;
metrics->vertBearingY = 0;
metrics->vertAdvance = 0;
}
decoder->metrics_loaded = 1;
*pp = p;
return FT_Err_Ok;
Fail:
FT_TRACE1(( "tt_sbit_decoder_load_metrics: broken table\n" ));
return FT_THROW( Invalid_Argument );
}
/* forward declaration */
static FT_Error
tt_sbit_decoder_load_image( TT_SBitDecoder decoder,
FT_UInt glyph_index,
FT_Int x_pos,
FT_Int y_pos,
FT_UInt recurse_count,
FT_Bool metrics_only );
typedef FT_Error (*TT_SBitDecoder_LoadFunc)(
TT_SBitDecoder decoder,
FT_Byte* p,
FT_Byte* plimit,
FT_Int x_pos,
FT_Int y_pos,
FT_UInt recurse_count );
static FT_Error
tt_sbit_decoder_load_byte_aligned( TT_SBitDecoder decoder,
FT_Byte* p,
FT_Byte* limit,
FT_Int x_pos,
FT_Int y_pos,
FT_UInt recurse_count )
{
FT_Error error = FT_Err_Ok;
FT_Byte* line;
FT_Int pitch, width, height, line_bits, h;
FT_UInt bit_height, bit_width;
FT_Bitmap* bitmap;
FT_UNUSED( recurse_count );
/* check that we can write the glyph into the bitmap */
bitmap = decoder->bitmap;
bit_width = bitmap->width;
bit_height = bitmap->rows;
pitch = bitmap->pitch;
line = bitmap->buffer;
if ( !line )
goto Exit;
width = decoder->metrics->width;
height = decoder->metrics->height;
line_bits = width * decoder->bit_depth;
if ( x_pos < 0 || (FT_UInt)( x_pos + width ) > bit_width ||
y_pos < 0 || (FT_UInt)( y_pos + height ) > bit_height )
{
FT_TRACE1(( "tt_sbit_decoder_load_byte_aligned:"
" invalid bitmap dimensions\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
if ( p + ( ( line_bits + 7 ) >> 3 ) * height > limit )
{
FT_TRACE1(( "tt_sbit_decoder_load_byte_aligned: broken bitmap\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
/* now do the blit */
line += y_pos * pitch + ( x_pos >> 3 );
x_pos &= 7;
if ( x_pos == 0 ) /* the easy one */
{
for ( h = height; h > 0; h--, line += pitch )
{
FT_Byte* pwrite = line;
FT_Int w;
for ( w = line_bits; w >= 8; w -= 8 )
{
pwrite[0] = (FT_Byte)( pwrite[0] | *p++ );
pwrite += 1;
}
if ( w > 0 )
pwrite[0] = (FT_Byte)( pwrite[0] | ( *p++ & ( 0xFF00U >> w ) ) );
}
}
else /* x_pos > 0 */
{
for ( h = height; h > 0; h--, line += pitch )
{
FT_Byte* pwrite = line;
FT_Int w;
FT_UInt wval = 0;
for ( w = line_bits; w >= 8; w -= 8 )
{
wval = (FT_UInt)( wval | *p++ );
pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) );
pwrite += 1;
wval <<= 8;
}
if ( w > 0 )
wval = (FT_UInt)( wval | ( *p++ & ( 0xFF00U >> w ) ) );
/* all bits read and there are `x_pos + w' bits to be written */
pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) );
if ( x_pos + w > 8 )
{
pwrite++;
wval <<= 8;
pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) );
}
}
}
Exit:
if ( !error )
FT_TRACE3(( "tt_sbit_decoder_load_byte_aligned: loaded\n" ));
return error;
}
/*
* Load a bit-aligned bitmap (with pointer `p') into a line-aligned bitmap
* (with pointer `pwrite'). In the example below, the width is 3 pixel,
* and `x_pos' is 1 pixel.
*
* p p+1
* | | |
* | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |...
* | | |
* +-------+ +-------+ +-------+ ...
* . . .
* . . .
* v . .
* +-------+ . .
* | | .
* | 7 6 5 4 3 2 1 0 | .
* | | .
* pwrite . .
* . .
* v .
* +-------+ .
* | |
* | 7 6 5 4 3 2 1 0 |
* | |
* pwrite+1 .
* .
* v
* +-------+
* | |
* | 7 6 5 4 3 2 1 0 |
* | |
* pwrite+2
*
*/
static FT_Error
tt_sbit_decoder_load_bit_aligned( TT_SBitDecoder decoder,
FT_Byte* p,
FT_Byte* limit,
FT_Int x_pos,
FT_Int y_pos,
FT_UInt recurse_count )
{
FT_Error error = FT_Err_Ok;
FT_Byte* line;
FT_Int pitch, width, height, line_bits, h, nbits;
FT_UInt bit_height, bit_width;
FT_Bitmap* bitmap;
FT_UShort rval;
FT_UNUSED( recurse_count );
/* check that we can write the glyph into the bitmap */
bitmap = decoder->bitmap;
bit_width = bitmap->width;
bit_height = bitmap->rows;
pitch = bitmap->pitch;
line = bitmap->buffer;
width = decoder->metrics->width;
height = decoder->metrics->height;
line_bits = width * decoder->bit_depth;
if ( x_pos < 0 || (FT_UInt)( x_pos + width ) > bit_width ||
y_pos < 0 || (FT_UInt)( y_pos + height ) > bit_height )
{
FT_TRACE1(( "tt_sbit_decoder_load_bit_aligned:"
" invalid bitmap dimensions\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
if ( p + ( ( line_bits * height + 7 ) >> 3 ) > limit )
{
FT_TRACE1(( "tt_sbit_decoder_load_bit_aligned: broken bitmap\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
if ( !line_bits || !height )
{
/* nothing to do */
goto Exit;
}
/* now do the blit */
/* adjust `line' to point to the first byte of the bitmap */
line += y_pos * pitch + ( x_pos >> 3 );
x_pos &= 7;
/* the higher byte of `rval' is used as a buffer */
rval = 0;
nbits = 0;
for ( h = height; h > 0; h--, line += pitch )
{
FT_Byte* pwrite = line;
FT_Int w = line_bits;
/* handle initial byte (in target bitmap) specially if necessary */
if ( x_pos )
{
w = ( line_bits < 8 - x_pos ) ? line_bits : 8 - x_pos;
if ( h == height )
{
rval = *p++;
nbits = x_pos;
}
else if ( nbits < w )
{
if ( p < limit )
rval |= *p++;
nbits += 8 - w;
}
else
{
rval >>= 8;
nbits -= w;
}
*pwrite++ |= ( ( rval >> nbits ) & 0xFF ) &
( ~( 0xFFU << w ) << ( 8 - w - x_pos ) );
rval <<= 8;
w = line_bits - w;
}
/* handle medial bytes */
for ( ; w >= 8; w -= 8 )
{
rval |= *p++;
*pwrite++ |= ( rval >> nbits ) & 0xFF;
rval <<= 8;
}
/* handle final byte if necessary */
if ( w > 0 )
{
if ( nbits < w )
{
if ( p < limit )
rval |= *p++;
*pwrite |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w );
nbits += 8 - w;
rval <<= 8;
}
else
{
*pwrite |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w );
nbits -= w;
}
}
}
Exit:
if ( !error )
FT_TRACE3(( "tt_sbit_decoder_load_bit_aligned: loaded\n" ));
return error;
}
static FT_Error
tt_sbit_decoder_load_compound( TT_SBitDecoder decoder,
FT_Byte* p,
FT_Byte* limit,
FT_Int x_pos,
FT_Int y_pos,
FT_UInt recurse_count )
{
FT_Error error = FT_Err_Ok;
FT_UInt num_components, nn;
FT_Char horiBearingX = (FT_Char)decoder->metrics->horiBearingX;
FT_Char horiBearingY = (FT_Char)decoder->metrics->horiBearingY;
FT_Byte horiAdvance = (FT_Byte)decoder->metrics->horiAdvance;
FT_Char vertBearingX = (FT_Char)decoder->metrics->vertBearingX;
FT_Char vertBearingY = (FT_Char)decoder->metrics->vertBearingY;
FT_Byte vertAdvance = (FT_Byte)decoder->metrics->vertAdvance;
if ( p + 2 > limit )
goto Fail;
num_components = FT_NEXT_USHORT( p );
if ( p + 4 * num_components > limit )
{
FT_TRACE1(( "tt_sbit_decoder_load_compound: broken table\n" ));
goto Fail;
}
FT_TRACE3(( "tt_sbit_decoder_load_compound: loading %d component%s\n",
num_components,
num_components == 1 ? "" : "s" ));
for ( nn = 0; nn < num_components; nn++ )
{
FT_UInt gindex = FT_NEXT_USHORT( p );
FT_Char dx = FT_NEXT_CHAR( p );
FT_Char dy = FT_NEXT_CHAR( p );
/* NB: a recursive call */
error = tt_sbit_decoder_load_image( decoder,
gindex,
x_pos + dx,
y_pos + dy,
recurse_count + 1,
/* request full bitmap image */
FALSE );
if ( error )
break;
}
FT_TRACE3(( "tt_sbit_decoder_load_compound: done\n" ));
decoder->metrics->horiBearingX = horiBearingX;
decoder->metrics->horiBearingY = horiBearingY;
decoder->metrics->horiAdvance = horiAdvance;
decoder->metrics->vertBearingX = vertBearingX;
decoder->metrics->vertBearingY = vertBearingY;
decoder->metrics->vertAdvance = vertAdvance;
decoder->metrics->width = (FT_Byte)decoder->bitmap->width;
decoder->metrics->height = (FT_Byte)decoder->bitmap->rows;
Exit:
return error;
Fail:
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
#ifdef FT_CONFIG_OPTION_USE_PNG
static FT_Error
tt_sbit_decoder_load_png( TT_SBitDecoder decoder,
FT_Byte* p,
FT_Byte* limit,
FT_Int x_pos,
FT_Int y_pos,
FT_UInt recurse_count )
{
FT_Error error = FT_Err_Ok;
FT_ULong png_len;
FT_UNUSED( recurse_count );
if ( limit - p < 4 )
{
FT_TRACE1(( "tt_sbit_decoder_load_png: broken bitmap\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
png_len = FT_NEXT_ULONG( p );
if ( (FT_ULong)( limit - p ) < png_len )
{
FT_TRACE1(( "tt_sbit_decoder_load_png: broken bitmap\n" ));
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
error = Load_SBit_Png( decoder->face->root.glyph,
x_pos,
y_pos,
decoder->bit_depth,
decoder->metrics,
decoder->stream->memory,
p,
png_len,
FALSE,
FALSE );
Exit:
if ( !error )
FT_TRACE3(( "tt_sbit_decoder_load_png: loaded\n" ));
return error;
}
#endif /* FT_CONFIG_OPTION_USE_PNG */
static FT_Error
tt_sbit_decoder_load_bitmap( TT_SBitDecoder decoder,
FT_UInt glyph_format,
FT_ULong glyph_start,
FT_ULong glyph_size,
FT_Int x_pos,
FT_Int y_pos,
FT_UInt recurse_count,
FT_Bool metrics_only )
{
FT_Error error;
FT_Stream stream = decoder->stream;
FT_Byte* p;
FT_Byte* p_limit;
FT_Byte* data;
/* seek into the EBDT table now */
if ( !glyph_size ||
glyph_start + glyph_size > decoder->ebdt_size )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
if ( FT_STREAM_SEEK( decoder->ebdt_start + glyph_start ) ||
FT_FRAME_EXTRACT( glyph_size, data ) )
goto Exit;
p = data;
p_limit = p + glyph_size;
/* read the data, depending on the glyph format */
switch ( glyph_format )
{
case 1:
case 2:
case 8:
case 17:
error = tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 0 );
break;
case 6:
case 7:
case 9:
case 18:
error = tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 );
break;
default:
error = FT_Err_Ok;
}
if ( error )
goto Fail;
{
TT_SBitDecoder_LoadFunc loader;
switch ( glyph_format )
{
case 1:
case 6:
loader = tt_sbit_decoder_load_byte_aligned;
break;
case 2:
case 7:
{
/* Don't trust `glyph_format'. For example, Apple's main Korean */
/* system font, `AppleMyungJo.ttf' (version 7.0d2e6), uses glyph */
/* format 7, but the data is format 6. We check whether we have */
/* an excessive number of bytes in the image: If it is equal to */
/* the value for a byte-aligned glyph, use the other loading */
/* routine. */
/* */
/* Note that for some (width,height) combinations, where the */
/* width is not a multiple of 8, the sizes for bit- and */
/* byte-aligned data are equal, for example (7,7) or (15,6). We */
/* then prefer what `glyph_format' specifies. */
FT_UInt width = decoder->metrics->width;
FT_UInt height = decoder->metrics->height;
FT_UInt bit_size = ( width * height + 7 ) >> 3;
FT_UInt byte_size = height * ( ( width + 7 ) >> 3 );
if ( bit_size < byte_size &&
byte_size == (FT_UInt)( p_limit - p ) )
loader = tt_sbit_decoder_load_byte_aligned;
else
loader = tt_sbit_decoder_load_bit_aligned;
}
break;
case 5:
loader = tt_sbit_decoder_load_bit_aligned;
break;
case 8:
if ( p + 1 > p_limit )
goto Fail;
p += 1; /* skip padding */
FALL_THROUGH;
case 9:
loader = tt_sbit_decoder_load_compound;
break;
case 17: /* small metrics, PNG image data */
case 18: /* big metrics, PNG image data */
case 19: /* metrics in EBLC, PNG image data */
#ifdef FT_CONFIG_OPTION_USE_PNG
loader = tt_sbit_decoder_load_png;
break;
#else
error = FT_THROW( Unimplemented_Feature );
goto Fail;
#endif /* FT_CONFIG_OPTION_USE_PNG */
default:
error = FT_THROW( Invalid_Table );
goto Fail;
}
if ( !decoder->bitmap_allocated )
{
error = tt_sbit_decoder_alloc_bitmap( decoder, metrics_only );
if ( error )
goto Fail;
}
if ( metrics_only )
goto Fail; /* this is not an error */
error = loader( decoder, p, p_limit, x_pos, y_pos, recurse_count );
}
Fail:
FT_FRAME_RELEASE( data );
Exit:
return error;
}
static FT_Error
tt_sbit_decoder_load_image( TT_SBitDecoder decoder,
FT_UInt glyph_index,
FT_Int x_pos,
FT_Int y_pos,
FT_UInt recurse_count,
FT_Bool metrics_only )
{
FT_Byte* p = decoder->eblc_base + decoder->strike_index_array;
FT_Byte* p_limit = decoder->eblc_limit;
FT_ULong num_ranges = decoder->strike_index_count;
FT_UInt start, end, index_format, image_format;
FT_ULong image_start = 0, image_end = 0, image_offset;
/* arbitrary recursion limit */
if ( recurse_count > 100 )
{
FT_TRACE4(( "tt_sbit_decoder_load_image:"
" recursion depth exceeded\n" ));
goto Failure;
}
/* First, we find the correct strike range that applies to this */
/* glyph index. */
for ( ; num_ranges > 0; num_ranges-- )
{
start = FT_NEXT_USHORT( p );
end = FT_NEXT_USHORT( p );
if ( glyph_index >= start && glyph_index <= end )
goto FoundRange;
p += 4; /* ignore index offset */
}
goto NoBitmap;
FoundRange:
image_offset = FT_NEXT_ULONG( p );
/* overflow check */
p = decoder->eblc_base + decoder->strike_index_array;
if ( image_offset > (FT_ULong)( p_limit - p ) )
goto Failure;
p += image_offset;
if ( p + 8 > p_limit )
goto NoBitmap;
/* now find the glyph's location and extend within the ebdt table */
index_format = FT_NEXT_USHORT( p );
image_format = FT_NEXT_USHORT( p );
image_offset = FT_NEXT_ULONG ( p );
switch ( index_format )
{
case 1: /* 4-byte offsets relative to `image_offset' */
p += 4 * ( glyph_index - start );
if ( p + 8 > p_limit )
goto NoBitmap;
image_start = FT_NEXT_ULONG( p );
image_end = FT_NEXT_ULONG( p );
if ( image_start == image_end ) /* missing glyph */
goto NoBitmap;
break;
case 2: /* big metrics, constant image size */
{
FT_ULong image_size;
if ( p + 12 > p_limit )
goto NoBitmap;
image_size = FT_NEXT_ULONG( p );
if ( tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 ) )
goto NoBitmap;
image_start = image_size * ( glyph_index - start );
image_end = image_start + image_size;
}
break;
case 3: /* 2-byte offsets relative to 'image_offset' */
p += 2 * ( glyph_index - start );
if ( p + 4 > p_limit )
goto NoBitmap;
image_start = FT_NEXT_USHORT( p );
image_end = FT_NEXT_USHORT( p );
if ( image_start == image_end ) /* missing glyph */
goto NoBitmap;
break;
case 4: /* sparse glyph array with (glyph,offset) pairs */
{
FT_ULong mm, num_glyphs;
if ( p + 4 > p_limit )
goto NoBitmap;
num_glyphs = FT_NEXT_ULONG( p );
/* overflow check for p + ( num_glyphs + 1 ) * 4 */
if ( p + 4 > p_limit ||
num_glyphs > (FT_ULong)( ( ( p_limit - p ) >> 2 ) - 1 ) )
goto NoBitmap;
for ( mm = 0; mm < num_glyphs; mm++ )
{
FT_UInt gindex = FT_NEXT_USHORT( p );
if ( gindex == glyph_index )
{
image_start = FT_NEXT_USHORT( p );
p += 2;
image_end = FT_PEEK_USHORT( p );
break;
}
p += 2;
}
if ( mm >= num_glyphs )
goto NoBitmap;
}
break;
case 5: /* constant metrics with sparse glyph codes */
case 19:
{
FT_ULong image_size, mm, num_glyphs;
if ( p + 16 > p_limit )
goto NoBitmap;
image_size = FT_NEXT_ULONG( p );
if ( tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 ) )
goto NoBitmap;
num_glyphs = FT_NEXT_ULONG( p );
/* overflow check for p + 2 * num_glyphs */
if ( num_glyphs > (FT_ULong)( ( p_limit - p ) >> 1 ) )
goto NoBitmap;
for ( mm = 0; mm < num_glyphs; mm++ )
{
FT_UInt gindex = FT_NEXT_USHORT( p );
if ( gindex == glyph_index )
break;
}
if ( mm >= num_glyphs )
goto NoBitmap;
image_start = image_size * mm;
image_end = image_start + image_size;
}
break;
default:
goto NoBitmap;
}
if ( image_start > image_end )
goto NoBitmap;
image_end -= image_start;
image_start = image_offset + image_start;
FT_TRACE3(( "tt_sbit_decoder_load_image:"
" found sbit (format %d) for glyph index %d\n",
image_format, glyph_index ));
return tt_sbit_decoder_load_bitmap( decoder,
image_format,
image_start,
image_end,
x_pos,
y_pos,
recurse_count,
metrics_only );
Failure:
return FT_THROW( Invalid_Table );
NoBitmap:
if ( recurse_count )
{
FT_TRACE4(( "tt_sbit_decoder_load_image:"
" missing subglyph sbit with glyph index %d\n",
glyph_index ));
return FT_THROW( Invalid_Composite );
}
FT_TRACE4(( "tt_sbit_decoder_load_image:"
" no sbit found for glyph index %d\n", glyph_index ));
return FT_THROW( Missing_Bitmap );
}
static FT_Error
tt_face_load_sbix_image( TT_Face face,
FT_ULong strike_index,
FT_UInt glyph_index,
FT_Stream stream,
FT_Bitmap *map,
TT_SBit_MetricsRec *metrics,
FT_Bool metrics_only )
{
FT_UInt strike_offset, glyph_start, glyph_end;
FT_Int originOffsetX, originOffsetY;
FT_Tag graphicType;
FT_Int recurse_depth = 0;
FT_Error error;
FT_Byte* p;
FT_UNUSED( map );
#ifndef FT_CONFIG_OPTION_USE_PNG
FT_UNUSED( metrics_only );
#endif
strike_index = face->sbit_strike_map[strike_index];
metrics->width = 0;
metrics->height = 0;
p = face->sbit_table + 8 + 4 * strike_index;
strike_offset = FT_NEXT_ULONG( p );
retry:
if ( glyph_index > (FT_UInt)face->root.num_glyphs )
return FT_THROW( Invalid_Argument );
if ( strike_offset >= face->ebdt_size ||
face->ebdt_size - strike_offset < 4 + glyph_index * 4 + 8 )
return FT_THROW( Invalid_File_Format );
if ( FT_STREAM_SEEK( face->ebdt_start +
strike_offset + 4 +
glyph_index * 4 ) ||
FT_FRAME_ENTER( 8 ) )
return error;
glyph_start = FT_GET_ULONG();
glyph_end = FT_GET_ULONG();
FT_FRAME_EXIT();
if ( glyph_start == glyph_end )
return FT_THROW( Missing_Bitmap );
if ( glyph_start > glyph_end ||
glyph_end - glyph_start < 8 ||
face->ebdt_size - strike_offset < glyph_end )
return FT_THROW( Invalid_File_Format );
if ( FT_STREAM_SEEK( face->ebdt_start + strike_offset + glyph_start ) ||
FT_FRAME_ENTER( glyph_end - glyph_start ) )
return error;
originOffsetX = FT_GET_SHORT();
originOffsetY = FT_GET_SHORT();
graphicType = FT_GET_TAG4();
switch ( graphicType )
{
case FT_MAKE_TAG( 'd', 'u', 'p', 'e' ):
if ( recurse_depth < 4 )
{
glyph_index = FT_GET_USHORT();
FT_FRAME_EXIT();
recurse_depth++;
goto retry;
}
error = FT_THROW( Invalid_File_Format );
break;
case FT_MAKE_TAG( 'p', 'n', 'g', ' ' ):
#ifdef FT_CONFIG_OPTION_USE_PNG
error = Load_SBit_Png( face->root.glyph,
0,
0,
32,
metrics,
stream->memory,
stream->cursor,
glyph_end - glyph_start - 8,
TRUE,
metrics_only );
#else
error = FT_THROW( Unimplemented_Feature );
#endif
break;
case FT_MAKE_TAG( 'j', 'p', 'g', ' ' ):
case FT_MAKE_TAG( 't', 'i', 'f', 'f' ):
case FT_MAKE_TAG( 'r', 'g', 'b', 'l' ): /* used on iOS 7.1 */
error = FT_THROW( Unknown_File_Format );
break;
default:
error = FT_THROW( Unimplemented_Feature );
break;
}
FT_FRAME_EXIT();
if ( !error )
{
FT_Short abearing; /* not used here */
FT_UShort aadvance;
tt_face_get_metrics( face, FALSE, glyph_index, &abearing, &aadvance );
metrics->horiBearingX = (FT_Short)originOffsetX;
metrics->vertBearingX = (FT_Short)originOffsetX;
metrics->horiBearingY = (FT_Short)( originOffsetY + metrics->height );
metrics->vertBearingY = (FT_Short)originOffsetY;
metrics->horiAdvance = (FT_UShort)( aadvance *
face->root.size->metrics.x_ppem /
face->header.Units_Per_EM );
if ( face->vertical_info )
tt_face_get_metrics( face, TRUE, glyph_index, &abearing, &aadvance );
else if ( face->os2.version != 0xFFFFU )
aadvance = (FT_UShort)FT_ABS( face->os2.sTypoAscender -
face->os2.sTypoDescender );
else
aadvance = (FT_UShort)FT_ABS( face->horizontal.Ascender -
face->horizontal.Descender );
metrics->vertAdvance = (FT_UShort)( aadvance *
face->root.size->metrics.x_ppem /
face->header.Units_Per_EM );
}
return error;
}
FT_LOCAL_DEF( FT_Error )
tt_face_load_sbit_image( TT_Face face,
FT_ULong strike_index,
FT_UInt glyph_index,
FT_UInt load_flags,
FT_Stream stream,
FT_Bitmap *map,
TT_SBit_MetricsRec *metrics )
{
FT_Error error = FT_Err_Ok;
switch ( (FT_UInt)face->sbit_table_type )
{
case TT_SBIT_TABLE_TYPE_EBLC:
case TT_SBIT_TABLE_TYPE_CBLC:
{
TT_SBitDecoderRec decoder[1];
error = tt_sbit_decoder_init( decoder, face, strike_index, metrics );
if ( !error )
{
error = tt_sbit_decoder_load_image(
decoder,
glyph_index,
0,
0,
0,
( load_flags & FT_LOAD_BITMAP_METRICS_ONLY ) != 0 );
tt_sbit_decoder_done( decoder );
}
}
break;
case TT_SBIT_TABLE_TYPE_SBIX:
error = tt_face_load_sbix_image(
face,
strike_index,
glyph_index,
stream,
map,
metrics,
( load_flags & FT_LOAD_BITMAP_METRICS_ONLY ) != 0 );
break;
default:
error = FT_THROW( Unknown_File_Format );
break;
}
/* Flatten color bitmaps if color was not requested. */
if ( !error &&
!( load_flags & FT_LOAD_COLOR ) &&
!( load_flags & FT_LOAD_BITMAP_METRICS_ONLY ) &&
map->pixel_mode == FT_PIXEL_MODE_BGRA )
{
FT_Bitmap new_map;
FT_Library library = face->root.glyph->library;
FT_Bitmap_Init( &new_map );
/* Convert to 8bit grayscale. */
error = FT_Bitmap_Convert( library, map, &new_map, 1 );
if ( error )
FT_Bitmap_Done( library, &new_map );
else
{
map->pixel_mode = new_map.pixel_mode;
map->pitch = new_map.pitch;
map->num_grays = new_map.num_grays;
ft_glyphslot_set_bitmap( face->root.glyph, new_map.buffer );
face->root.glyph->internal->flags |= FT_GLYPH_OWN_BITMAP;
}
}
return error;
}
#else /* !TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
/* ANSI C doesn't like empty source files */
typedef int tt_sbit_dummy_;
#endif /* !TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
/* END */