summaryrefslogtreecommitdiffstats
path: root/modules/freetype2/src/sfnt
diff options
context:
space:
mode:
Diffstat (limited to 'modules/freetype2/src/sfnt')
-rw-r--r--modules/freetype2/src/sfnt/module.mk23
-rw-r--r--modules/freetype2/src/sfnt/pngshim.c465
-rw-r--r--modules/freetype2/src/sfnt/pngshim.h50
-rw-r--r--modules/freetype2/src/sfnt/rules.mk86
-rw-r--r--modules/freetype2/src/sfnt/sfdriver.c1346
-rw-r--r--modules/freetype2/src/sfnt/sfdriver.h35
-rw-r--r--modules/freetype2/src/sfnt/sferrors.h41
-rw-r--r--modules/freetype2/src/sfnt/sfnt.c40
-rw-r--r--modules/freetype2/src/sfnt/sfobjs.c1515
-rw-r--r--modules/freetype2/src/sfnt/sfobjs.h58
-rw-r--r--modules/freetype2/src/sfnt/sfwoff.c434
-rw-r--r--modules/freetype2/src/sfnt/sfwoff.h43
-rw-r--r--modules/freetype2/src/sfnt/sfwoff2.c2386
-rw-r--r--modules/freetype2/src/sfnt/sfwoff2.h78
-rw-r--r--modules/freetype2/src/sfnt/ttbdf.c256
-rw-r--r--modules/freetype2/src/sfnt/ttbdf.h49
-rw-r--r--modules/freetype2/src/sfnt/ttcmap.c3897
-rw-r--r--modules/freetype2/src/sfnt/ttcmap.h126
-rw-r--r--modules/freetype2/src/sfnt/ttcmapc.h56
-rw-r--r--modules/freetype2/src/sfnt/ttcolr.c1921
-rw-r--r--modules/freetype2/src/sfnt/ttcolr.h83
-rw-r--r--modules/freetype2/src/sfnt/ttcpal.c310
-rw-r--r--modules/freetype2/src/sfnt/ttcpal.h48
-rw-r--r--modules/freetype2/src/sfnt/ttkern.c317
-rw-r--r--modules/freetype2/src/sfnt/ttkern.h51
-rw-r--r--modules/freetype2/src/sfnt/ttload.c1489
-rw-r--r--modules/freetype2/src/sfnt/ttload.h111
-rw-r--r--modules/freetype2/src/sfnt/ttmtx.c338
-rw-r--r--modules/freetype2/src/sfnt/ttmtx.h54
-rw-r--r--modules/freetype2/src/sfnt/ttpost.c566
-rw-r--r--modules/freetype2/src/sfnt/ttpost.h46
-rw-r--r--modules/freetype2/src/sfnt/ttsbit.c1685
-rw-r--r--modules/freetype2/src/sfnt/ttsbit.h62
-rw-r--r--modules/freetype2/src/sfnt/ttsvg.c413
-rw-r--r--modules/freetype2/src/sfnt/ttsvg.h43
-rw-r--r--modules/freetype2/src/sfnt/woff2tags.c119
-rw-r--r--modules/freetype2/src/sfnt/woff2tags.h41
37 files changed, 18681 insertions, 0 deletions
diff --git a/modules/freetype2/src/sfnt/module.mk b/modules/freetype2/src/sfnt/module.mk
new file mode 100644
index 0000000000..4491a1b22f
--- /dev/null
+++ b/modules/freetype2/src/sfnt/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 SFNT module definition
+#
+
+
+# Copyright (C) 1996-2023 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.
+
+
+FTMODULE_H_COMMANDS += SFNT_MODULE
+
+define SFNT_MODULE
+$(OPEN_DRIVER) FT_Module_Class, sfnt_module_class $(CLOSE_DRIVER)
+$(ECHO_DRIVER)sfnt $(ECHO_DRIVER_DESC)helper module for TrueType & OpenType formats$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/modules/freetype2/src/sfnt/pngshim.c b/modules/freetype2/src/sfnt/pngshim.c
new file mode 100644
index 0000000000..423b07b02a
--- /dev/null
+++ b/modules/freetype2/src/sfnt/pngshim.c
@@ -0,0 +1,465 @@
+/****************************************************************************
+ *
+ * pngshim.c
+ *
+ * PNG Bitmap glyph support.
+ *
+ * Copyright (C) 2013-2023 by
+ * Google, Inc.
+ * Written by Stuart Gill and 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 FT_CONFIG_STANDARD_LIBRARY_H
+
+
+#if defined( TT_CONFIG_OPTION_EMBEDDED_BITMAPS ) && \
+ defined( FT_CONFIG_OPTION_USE_PNG )
+
+ /* We always include <setjmp.h>, so make libpng shut up! */
+#define PNG_SKIP_SETJMP_CHECK 1
+#include <png.h>
+#include "pngshim.h"
+
+#include "sferrors.h"
+
+
+ /* This code is freely based on cairo-png.c. There's so many ways */
+ /* to call libpng, and the way cairo does it is defacto standard. */
+
+ static unsigned int
+ multiply_alpha( unsigned int alpha,
+ unsigned int color )
+ {
+ unsigned int temp = alpha * color + 0x80;
+
+
+ return ( temp + ( temp >> 8 ) ) >> 8;
+ }
+
+
+ /* Premultiplies data and converts RGBA bytes => BGRA. */
+ static void
+ premultiply_data( png_structp png,
+ png_row_infop row_info,
+ png_bytep data )
+ {
+ unsigned int i = 0, limit;
+
+ /* The `vector_size' attribute was introduced in gcc 3.1, which */
+ /* predates clang; the `__BYTE_ORDER__' preprocessor symbol was */
+ /* introduced in gcc 4.6 and clang 3.2, respectively. */
+ /* `__builtin_shuffle' for gcc was introduced in gcc 4.7.0. */
+ /* */
+ /* Intel compilers do not currently support __builtin_shuffle; */
+
+ /* The Intel check must be first. */
+#if !defined( __INTEL_COMPILER ) && \
+ ( ( defined( __GNUC__ ) && \
+ ( ( __GNUC__ >= 5 ) || \
+ ( ( __GNUC__ == 4 ) && ( __GNUC_MINOR__ >= 7 ) ) ) ) || \
+ ( defined( __clang__ ) && \
+ ( ( __clang_major__ >= 4 ) || \
+ ( ( __clang_major__ == 3 ) && ( __clang_minor__ >= 2 ) ) ) ) ) && \
+ defined( __OPTIMIZE__ ) && \
+ defined( __SSE__ ) && \
+ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+#ifdef __clang__
+ /* the clang documentation doesn't cover the two-argument case of */
+ /* `__builtin_shufflevector'; however, it is is implemented since */
+ /* version 2.8 */
+#define vector_shuffle __builtin_shufflevector
+#else
+#define vector_shuffle __builtin_shuffle
+#endif
+
+ typedef unsigned short v82 __attribute__(( vector_size( 16 ) ));
+
+
+ if ( row_info->rowbytes > 15 )
+ {
+ /* process blocks of 16 bytes in one rush, which gives a nice speed-up */
+ limit = row_info->rowbytes - 16 + 1;
+ for ( ; i < limit; i += 16 )
+ {
+ unsigned char* base = &data[i];
+
+ v82 s, s0, s1, a;
+
+ /* clang <= 3.9 can't apply scalar values to vectors */
+ /* (or rather, it needs a different syntax) */
+ v82 n0x80 = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
+ v82 n0xFF = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ v82 n8 = { 8, 8, 8, 8, 8, 8, 8, 8 };
+
+ v82 ma = { 1, 1, 3, 3, 5, 5, 7, 7 };
+ v82 o1 = { 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF };
+ v82 m0 = { 1, 0, 3, 2, 5, 4, 7, 6 };
+
+
+ ft_memcpy( &s, base, 16 ); /* RGBA RGBA RGBA RGBA */
+ s0 = s & n0xFF; /* R B R B R B R B */
+ s1 = s >> n8; /* G A G A G A G A */
+
+ a = vector_shuffle( s1, ma ); /* A A A A A A A A */
+ s1 |= o1; /* G 1 G 1 G 1 G 1 */
+ s0 = vector_shuffle( s0, m0 ); /* B R B R B R B R */
+
+ s0 *= a;
+ s1 *= a;
+ s0 += n0x80;
+ s1 += n0x80;
+ s0 = ( s0 + ( s0 >> n8 ) ) >> n8;
+ s1 = ( s1 + ( s1 >> n8 ) ) >> n8;
+
+ s = s0 | ( s1 << n8 );
+ ft_memcpy( base, &s, 16 );
+ }
+ }
+#endif /* use `vector_size' */
+
+ FT_UNUSED( png );
+
+ limit = row_info->rowbytes;
+ for ( ; i < limit; i += 4 )
+ {
+ unsigned char* base = &data[i];
+ unsigned int alpha = base[3];
+
+
+ if ( alpha == 0 )
+ base[0] = base[1] = base[2] = base[3] = 0;
+
+ else
+ {
+ unsigned int red = base[0];
+ unsigned int green = base[1];
+ unsigned int blue = base[2];
+
+
+ if ( alpha != 0xFF )
+ {
+ red = multiply_alpha( alpha, red );
+ green = multiply_alpha( alpha, green );
+ blue = multiply_alpha( alpha, blue );
+ }
+
+ base[0] = (unsigned char)blue;
+ base[1] = (unsigned char)green;
+ base[2] = (unsigned char)red;
+ base[3] = (unsigned char)alpha;
+ }
+ }
+ }
+
+
+ /* Converts RGBx bytes to BGRA. */
+ static void
+ convert_bytes_to_data( png_structp png,
+ png_row_infop row_info,
+ png_bytep data )
+ {
+ unsigned int i;
+
+ FT_UNUSED( png );
+
+
+ for ( i = 0; i < row_info->rowbytes; i += 4 )
+ {
+ unsigned char* base = &data[i];
+ unsigned int red = base[0];
+ unsigned int green = base[1];
+ unsigned int blue = base[2];
+
+
+ base[0] = (unsigned char)blue;
+ base[1] = (unsigned char)green;
+ base[2] = (unsigned char)red;
+ base[3] = 0xFF;
+ }
+ }
+
+
+ /* Use error callback to avoid png writing to stderr. */
+ static void
+ error_callback( png_structp png,
+ png_const_charp error_msg )
+ {
+ FT_Error* error = (FT_Error*)png_get_error_ptr( png );
+
+ FT_UNUSED( error_msg );
+
+
+ *error = FT_THROW( Out_Of_Memory );
+#ifdef PNG_SETJMP_SUPPORTED
+ ft_longjmp( png_jmpbuf( png ), 1 );
+#endif
+ /* if we get here, then we have no choice but to abort ... */
+ }
+
+
+ /* Use warning callback to avoid png writing to stderr. */
+ static void
+ warning_callback( png_structp png,
+ png_const_charp error_msg )
+ {
+ FT_UNUSED( png );
+ FT_UNUSED( error_msg );
+
+ /* Just ignore warnings. */
+ }
+
+
+ static void
+ read_data_from_FT_Stream( png_structp png,
+ png_bytep data,
+ png_size_t length )
+ {
+ FT_Error error;
+ png_voidp p = png_get_io_ptr( png );
+ FT_Stream stream = (FT_Stream)p;
+
+
+ if ( FT_FRAME_ENTER( length ) )
+ {
+ FT_Error* e = (FT_Error*)png_get_error_ptr( png );
+
+
+ *e = FT_THROW( Invalid_Stream_Read );
+ png_error( png, NULL );
+
+ /* return; (never reached) */
+ }
+
+ ft_memcpy( data, stream->cursor, length );
+
+ FT_FRAME_EXIT();
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ Load_SBit_Png( FT_GlyphSlot slot,
+ FT_Int x_offset,
+ FT_Int y_offset,
+ FT_Int pix_bits,
+ TT_SBit_Metrics metrics,
+ FT_Memory memory,
+ FT_Byte* data,
+ FT_UInt png_len,
+ FT_Bool populate_map_and_metrics,
+ FT_Bool metrics_only )
+ {
+ FT_Bitmap *map = &slot->bitmap;
+ FT_Error error = FT_Err_Ok;
+ FT_StreamRec stream;
+
+ png_structp png;
+ png_infop info;
+ png_uint_32 imgWidth, imgHeight;
+
+ int bitdepth, color_type, interlace;
+ FT_Int i;
+
+ /* `rows` gets modified within a 'setjmp' scope; */
+ /* we thus need the `volatile` keyword. */
+ png_byte* *volatile rows = NULL;
+
+
+ if ( x_offset < 0 ||
+ y_offset < 0 )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ if ( !populate_map_and_metrics &&
+ ( (FT_UInt)x_offset + metrics->width > map->width ||
+ (FT_UInt)y_offset + metrics->height > map->rows ||
+ pix_bits != 32 ||
+ map->pixel_mode != FT_PIXEL_MODE_BGRA ) )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ FT_Stream_OpenMemory( &stream, data, png_len );
+
+ png = png_create_read_struct( PNG_LIBPNG_VER_STRING,
+ &error,
+ error_callback,
+ warning_callback );
+ if ( !png )
+ {
+ error = FT_THROW( Out_Of_Memory );
+ goto Exit;
+ }
+
+ info = png_create_info_struct( png );
+ if ( !info )
+ {
+ error = FT_THROW( Out_Of_Memory );
+ png_destroy_read_struct( &png, NULL, NULL );
+ goto Exit;
+ }
+
+ if ( ft_setjmp( png_jmpbuf( png ) ) )
+ {
+ error = FT_THROW( Invalid_File_Format );
+ goto DestroyExit;
+ }
+
+ png_set_read_fn( png, &stream, read_data_from_FT_Stream );
+
+ png_read_info( png, info );
+ png_get_IHDR( png, info,
+ &imgWidth, &imgHeight,
+ &bitdepth, &color_type, &interlace,
+ NULL, NULL );
+
+ if ( error ||
+ ( !populate_map_and_metrics &&
+ ( (FT_Int)imgWidth != metrics->width ||
+ (FT_Int)imgHeight != metrics->height ) ) )
+ goto DestroyExit;
+
+ if ( populate_map_and_metrics )
+ {
+ /* reject too large bitmaps similarly to the rasterizer */
+ if ( imgHeight > 0x7FFF || imgWidth > 0x7FFF )
+ {
+ error = FT_THROW( Array_Too_Large );
+ goto DestroyExit;
+ }
+
+ metrics->width = (FT_UShort)imgWidth;
+ metrics->height = (FT_UShort)imgHeight;
+
+ map->width = metrics->width;
+ map->rows = metrics->height;
+ map->pixel_mode = FT_PIXEL_MODE_BGRA;
+ map->pitch = (int)( map->width * 4 );
+ map->num_grays = 256;
+ }
+
+ /* convert palette/gray image to rgb */
+ if ( color_type == PNG_COLOR_TYPE_PALETTE )
+ png_set_palette_to_rgb( png );
+
+ /* expand gray bit depth if needed */
+ if ( color_type == PNG_COLOR_TYPE_GRAY )
+ {
+#if PNG_LIBPNG_VER >= 10209
+ png_set_expand_gray_1_2_4_to_8( png );
+#else
+ png_set_gray_1_2_4_to_8( png );
+#endif
+ }
+
+ /* transform transparency to alpha */
+ if ( png_get_valid( png, info, PNG_INFO_tRNS ) )
+ png_set_tRNS_to_alpha( png );
+
+ if ( bitdepth == 16 )
+ png_set_strip_16( png );
+
+ if ( bitdepth < 8 )
+ png_set_packing( png );
+
+ /* convert grayscale to RGB */
+ if ( color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
+ png_set_gray_to_rgb( png );
+
+ if ( interlace != PNG_INTERLACE_NONE )
+ png_set_interlace_handling( png );
+
+ png_set_filler( png, 0xFF, PNG_FILLER_AFTER );
+
+ /* recheck header after setting EXPAND options */
+ png_read_update_info( png, info );
+ png_get_IHDR( png, info,
+ &imgWidth, &imgHeight,
+ &bitdepth, &color_type, &interlace,
+ NULL, NULL );
+
+ if ( bitdepth != 8 ||
+ !( color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA ) )
+ {
+ error = FT_THROW( Invalid_File_Format );
+ goto DestroyExit;
+ }
+
+ if ( metrics_only )
+ goto DestroyExit;
+
+ switch ( color_type )
+ {
+ default:
+ /* Shouldn't happen, but ... */
+ FALL_THROUGH;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ png_set_read_user_transform_fn( png, premultiply_data );
+ break;
+
+ case PNG_COLOR_TYPE_RGB:
+ /* Humm, this smells. Carry on though. */
+ png_set_read_user_transform_fn( png, convert_bytes_to_data );
+ break;
+ }
+
+ if ( populate_map_and_metrics )
+ {
+ /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */
+ FT_ULong size = map->rows * (FT_ULong)map->pitch;
+
+
+ error = ft_glyphslot_alloc_bitmap( slot, size );
+ if ( error )
+ goto DestroyExit;
+ }
+
+ if ( FT_QNEW_ARRAY( rows, imgHeight ) )
+ {
+ error = FT_THROW( Out_Of_Memory );
+ goto DestroyExit;
+ }
+
+ for ( i = 0; i < (FT_Int)imgHeight; i++ )
+ rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4;
+
+ png_read_image( png, rows );
+
+ png_read_end( png, info );
+
+ DestroyExit:
+ /* even if reading fails with longjmp, rows must be freed */
+ FT_FREE( rows );
+ png_destroy_read_struct( &png, &info, NULL );
+ FT_Stream_Close( &stream );
+
+ Exit:
+ return error;
+ }
+
+#else /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _pngshim_dummy;
+
+#endif /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/pngshim.h b/modules/freetype2/src/sfnt/pngshim.h
new file mode 100644
index 0000000000..903bd2bc34
--- /dev/null
+++ b/modules/freetype2/src/sfnt/pngshim.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+ *
+ * pngshim.h
+ *
+ * PNG Bitmap glyph support.
+ *
+ * Copyright (C) 2013-2023 by
+ * Google, Inc.
+ * Written by Stuart Gill and 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.
+ *
+ */
+
+
+#ifndef PNGSHIM_H_
+#define PNGSHIM_H_
+
+
+#include "ttload.h"
+
+
+FT_BEGIN_HEADER
+
+#ifdef FT_CONFIG_OPTION_USE_PNG
+
+ FT_LOCAL( FT_Error )
+ Load_SBit_Png( FT_GlyphSlot slot,
+ FT_Int x_offset,
+ FT_Int y_offset,
+ FT_Int pix_bits,
+ TT_SBit_Metrics metrics,
+ FT_Memory memory,
+ FT_Byte* data,
+ FT_UInt png_len,
+ FT_Bool populate_map_and_metrics,
+ FT_Bool metrics_only );
+
+#endif
+
+FT_END_HEADER
+
+#endif /* PNGSHIM_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/rules.mk b/modules/freetype2/src/sfnt/rules.mk
new file mode 100644
index 0000000000..4d2d7e8d84
--- /dev/null
+++ b/modules/freetype2/src/sfnt/rules.mk
@@ -0,0 +1,86 @@
+#
+# FreeType 2 SFNT driver configuration rules
+#
+
+
+# Copyright (C) 1996-2023 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.
+
+
+# SFNT driver directory
+#
+SFNT_DIR := $(SRC_DIR)/sfnt
+
+
+# compilation flags for the driver
+#
+SFNT_COMPILE := $(CC) $(ANSIFLAGS) \
+ $I$(subst /,$(COMPILER_SEP),$(SFNT_DIR)) \
+ $(INCLUDE_FLAGS) \
+ $(FT_CFLAGS)
+
+
+# SFNT driver sources (i.e., C files)
+#
+SFNT_DRV_SRC := $(SFNT_DIR)/pngshim.c \
+ $(SFNT_DIR)/sfdriver.c \
+ $(SFNT_DIR)/sfobjs.c \
+ $(SFNT_DIR)/sfwoff.c \
+ $(SFNT_DIR)/sfwoff2.c \
+ $(SFNT_DIR)/ttbdf.c \
+ $(SFNT_DIR)/ttcmap.c \
+ $(SFNT_DIR)/ttcolr.c \
+ $(SFNT_DIR)/ttsvg.c \
+ $(SFNT_DIR)/ttcpal.c \
+ $(SFNT_DIR)/ttkern.c \
+ $(SFNT_DIR)/ttload.c \
+ $(SFNT_DIR)/ttmtx.c \
+ $(SFNT_DIR)/ttpost.c \
+ $(SFNT_DIR)/ttsbit.c \
+ $(SFNT_DIR)/woff2tags.c
+
+# SFNT driver headers
+#
+SFNT_DRV_H := $(SFNT_DRV_SRC:%c=%h) \
+ $(SFNT_DIR)/sferrors.h
+
+
+# SFNT driver object(s)
+#
+# SFNT_DRV_OBJ_M is used during `multi' builds.
+# SFNT_DRV_OBJ_S is used during `single' builds.
+#
+SFNT_DRV_OBJ_M := $(SFNT_DRV_SRC:$(SFNT_DIR)/%.c=$(OBJ_DIR)/%.$O)
+SFNT_DRV_OBJ_S := $(OBJ_DIR)/sfnt.$O
+
+# SFNT driver source file for single build
+#
+SFNT_DRV_SRC_S := $(SFNT_DIR)/sfnt.c
+
+
+# SFNT driver - single object
+#
+$(SFNT_DRV_OBJ_S): $(SFNT_DRV_SRC_S) $(SFNT_DRV_SRC) \
+ $(FREETYPE_H) $(SFNT_DRV_H)
+ $(SFNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(SFNT_DRV_SRC_S))
+
+
+# SFNT driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(SFNT_DIR)/%.c $(FREETYPE_H) $(SFNT_DRV_H)
+ $(SFNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(SFNT_DRV_OBJ_S)
+DRV_OBJS_M += $(SFNT_DRV_OBJ_M)
+
+
+# EOF
diff --git a/modules/freetype2/src/sfnt/sfdriver.c b/modules/freetype2/src/sfnt/sfdriver.c
new file mode 100644
index 0000000000..762883db54
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfdriver.c
@@ -0,0 +1,1346 @@
+/****************************************************************************
+ *
+ * sfdriver.c
+ *
+ * High-level SFNT driver interface (body).
+ *
+ * Copyright (C) 1996-2023 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 <freetype/internal/ftdebug.h>
+#include <freetype/internal/sfnt.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/ttnameid.h>
+
+#include "sfdriver.h"
+#include "ttload.h"
+#include "sfobjs.h"
+
+#include "sferrors.h"
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+#include "ttsbit.h"
+#endif
+
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+#include "ttcolr.h"
+#include "ttcpal.h"
+#endif
+
+#ifdef FT_CONFIG_OPTION_SVG
+#include "ttsvg.h"
+#endif
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+#include "ttpost.h"
+#endif
+
+#ifdef TT_CONFIG_OPTION_BDF
+#include "ttbdf.h"
+#include <freetype/internal/services/svbdf.h>
+#endif
+
+#include "ttcmap.h"
+#include "ttkern.h"
+#include "ttmtx.h"
+
+#include <freetype/internal/services/svgldict.h>
+#include <freetype/internal/services/svpostnm.h>
+#include <freetype/internal/services/svsfnt.h>
+#include <freetype/internal/services/svttcmap.h>
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include <freetype/ftmm.h>
+#include <freetype/internal/services/svmm.h>
+#endif
+
+
+ /**************************************************************************
+ *
+ * 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 sfdriver
+
+
+ /*
+ * SFNT TABLE SERVICE
+ *
+ */
+
+ static void*
+ get_sfnt_table( TT_Face face,
+ FT_Sfnt_Tag tag )
+ {
+ void* table;
+
+
+ switch ( tag )
+ {
+ case FT_SFNT_HEAD:
+ table = &face->header;
+ break;
+
+ case FT_SFNT_HHEA:
+ table = &face->horizontal;
+ break;
+
+ case FT_SFNT_VHEA:
+ table = face->vertical_info ? &face->vertical : NULL;
+ break;
+
+ case FT_SFNT_OS2:
+ table = ( face->os2.version == 0xFFFFU ) ? NULL : &face->os2;
+ break;
+
+ case FT_SFNT_POST:
+ table = &face->postscript;
+ break;
+
+ case FT_SFNT_MAXP:
+ table = &face->max_profile;
+ break;
+
+ case FT_SFNT_PCLT:
+ table = face->pclt.Version ? &face->pclt : NULL;
+ break;
+
+ default:
+ table = NULL;
+ }
+
+ return table;
+ }
+
+
+ static FT_Error
+ sfnt_table_info( TT_Face face,
+ FT_UInt idx,
+ FT_ULong *tag,
+ FT_ULong *offset,
+ FT_ULong *length )
+ {
+ if ( !offset || !length )
+ return FT_THROW( Invalid_Argument );
+
+ if ( !tag )
+ *length = face->num_tables;
+ else
+ {
+ if ( idx >= face->num_tables )
+ return FT_THROW( Table_Missing );
+
+ *tag = face->dir_tables[idx].Tag;
+ *offset = face->dir_tables[idx].Offset;
+ *length = face->dir_tables[idx].Length;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_SERVICE_SFNT_TABLEREC(
+ sfnt_service_sfnt_table,
+
+ (FT_SFNT_TableLoadFunc)tt_face_load_any, /* load_table */
+ (FT_SFNT_TableGetFunc) get_sfnt_table, /* get_table */
+ (FT_SFNT_TableInfoFunc)sfnt_table_info /* table_info */
+ )
+
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+ /*
+ * GLYPH DICT SERVICE
+ *
+ */
+
+ static FT_Error
+ sfnt_get_glyph_name( FT_Face face,
+ FT_UInt glyph_index,
+ FT_Pointer buffer,
+ FT_UInt buffer_max )
+ {
+ FT_String* gname;
+ FT_Error error;
+
+
+ error = tt_face_get_ps_name( (TT_Face)face, glyph_index, &gname );
+ if ( !error )
+ FT_STRCPYN( buffer, gname, buffer_max );
+
+ return error;
+ }
+
+
+ static FT_UInt
+ sfnt_get_name_index( FT_Face face,
+ const FT_String* glyph_name )
+ {
+ TT_Face ttface = (TT_Face)face;
+
+ FT_UInt i, max_gid = FT_UINT_MAX;
+
+
+ if ( face->num_glyphs < 0 )
+ return 0;
+ else if ( (FT_ULong)face->num_glyphs < FT_UINT_MAX )
+ max_gid = (FT_UInt)face->num_glyphs;
+ else
+ FT_TRACE0(( "Ignore glyph names for invalid GID 0x%08x - 0x%08lx\n",
+ FT_UINT_MAX, face->num_glyphs ));
+
+ for ( i = 0; i < max_gid; i++ )
+ {
+ FT_String* gname;
+ FT_Error error = tt_face_get_ps_name( ttface, i, &gname );
+
+
+ if ( error )
+ continue;
+
+ if ( !ft_strcmp( glyph_name, gname ) )
+ return i;
+ }
+
+ return 0;
+ }
+
+
+ FT_DEFINE_SERVICE_GLYPHDICTREC(
+ sfnt_service_glyph_dict,
+
+ (FT_GlyphDict_GetNameFunc) sfnt_get_glyph_name, /* get_name */
+ (FT_GlyphDict_NameIndexFunc)sfnt_get_name_index /* name_index */
+ )
+
+#endif /* TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+
+ /*
+ * POSTSCRIPT NAME SERVICE
+ *
+ */
+
+ /* an array representing allowed ASCII characters in a PS string */
+ static const unsigned char sfnt_ps_map[16] =
+ {
+ /* 4 0 C 8 */
+ 0x00, 0x00, /* 0x00: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 */
+ 0x00, 0x00, /* 0x10: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 */
+ 0xDE, 0x7C, /* 0x20: 1 1 0 1 1 1 1 0 0 1 1 1 1 1 0 0 */
+ 0xFF, 0xAF, /* 0x30: 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 */
+ 0xFF, 0xFF, /* 0x40: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 */
+ 0xFF, 0xD7, /* 0x50: 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 */
+ 0xFF, 0xFF, /* 0x60: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 */
+ 0xFF, 0x57 /* 0x70: 1 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1 */
+ };
+
+
+ static int
+ sfnt_is_postscript( int c )
+ {
+ unsigned int cc;
+
+
+ if ( c < 0 || c >= 0x80 )
+ return 0;
+
+ cc = (unsigned int)c;
+
+ return sfnt_ps_map[cc >> 3] & ( 1 << ( cc & 0x07 ) );
+ }
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+ /* Only ASCII letters and digits are taken for a variation font */
+ /* instance's PostScript name. */
+ /* */
+ /* `ft_isalnum' is a macro, but we need a function here, thus */
+ /* this definition. */
+ static int
+ sfnt_is_alphanumeric( int c )
+ {
+ return ft_isalnum( c );
+ }
+
+
+ /* the implementation of MurmurHash3 is taken and adapted from */
+ /* https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp */
+
+#define ROTL32( x, r ) ( x << r ) | ( x >> ( 32 - r ) )
+
+
+ static FT_UInt32
+ fmix32( FT_UInt32 h )
+ {
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+
+ return h;
+ }
+
+
+ static void
+ murmur_hash_3_128( const void* key,
+ const unsigned int len,
+ FT_UInt32 seed,
+ void* out )
+ {
+ const FT_Byte* data = (const FT_Byte*)key;
+ const int nblocks = (int)len / 16;
+
+ FT_UInt32 h1 = seed;
+ FT_UInt32 h2 = seed;
+ FT_UInt32 h3 = seed;
+ FT_UInt32 h4 = seed;
+
+ const FT_UInt32 c1 = 0x239b961b;
+ const FT_UInt32 c2 = 0xab0e9789;
+ const FT_UInt32 c3 = 0x38b34ae5;
+ const FT_UInt32 c4 = 0xa1e38b93;
+
+ const FT_UInt32* blocks = (const FT_UInt32*)( data + nblocks * 16 );
+
+ int i;
+
+
+ for( i = -nblocks; i; i++ )
+ {
+ FT_UInt32 k1 = blocks[i * 4 + 0];
+ FT_UInt32 k2 = blocks[i * 4 + 1];
+ FT_UInt32 k3 = blocks[i * 4 + 2];
+ FT_UInt32 k4 = blocks[i * 4 + 3];
+
+
+ k1 *= c1;
+ k1 = ROTL32( k1, 15 );
+ k1 *= c2;
+ h1 ^= k1;
+
+ h1 = ROTL32( h1, 19 );
+ h1 += h2;
+ h1 = h1 * 5 + 0x561ccd1b;
+
+ k2 *= c2;
+ k2 = ROTL32( k2, 16 );
+ k2 *= c3;
+ h2 ^= k2;
+
+ h2 = ROTL32( h2, 17 );
+ h2 += h3;
+ h2 = h2 * 5 + 0x0bcaa747;
+
+ k3 *= c3;
+ k3 = ROTL32( k3, 17 );
+ k3 *= c4;
+ h3 ^= k3;
+
+ h3 = ROTL32( h3, 15 );
+ h3 += h4;
+ h3 = h3 * 5 + 0x96cd1c35;
+
+ k4 *= c4;
+ k4 = ROTL32( k4, 18 );
+ k4 *= c1;
+ h4 ^= k4;
+
+ h4 = ROTL32( h4, 13 );
+ h4 += h1;
+ h4 = h4 * 5 + 0x32ac3b17;
+ }
+
+ {
+ const FT_Byte* tail = (const FT_Byte*)( data + nblocks * 16 );
+
+ FT_UInt32 k1 = 0;
+ FT_UInt32 k2 = 0;
+ FT_UInt32 k3 = 0;
+ FT_UInt32 k4 = 0;
+
+
+ switch ( len & 15 )
+ {
+ case 15:
+ k4 ^= (FT_UInt32)tail[14] << 16;
+ FALL_THROUGH;
+ case 14:
+ k4 ^= (FT_UInt32)tail[13] << 8;
+ FALL_THROUGH;
+ case 13:
+ k4 ^= (FT_UInt32)tail[12];
+ k4 *= c4;
+ k4 = ROTL32( k4, 18 );
+ k4 *= c1;
+ h4 ^= k4;
+ FALL_THROUGH;
+
+ case 12:
+ k3 ^= (FT_UInt32)tail[11] << 24;
+ FALL_THROUGH;
+ case 11:
+ k3 ^= (FT_UInt32)tail[10] << 16;
+ FALL_THROUGH;
+ case 10:
+ k3 ^= (FT_UInt32)tail[9] << 8;
+ FALL_THROUGH;
+ case 9:
+ k3 ^= (FT_UInt32)tail[8];
+ k3 *= c3;
+ k3 = ROTL32( k3, 17 );
+ k3 *= c4;
+ h3 ^= k3;
+ FALL_THROUGH;
+
+ case 8:
+ k2 ^= (FT_UInt32)tail[7] << 24;
+ FALL_THROUGH;
+ case 7:
+ k2 ^= (FT_UInt32)tail[6] << 16;
+ FALL_THROUGH;
+ case 6:
+ k2 ^= (FT_UInt32)tail[5] << 8;
+ FALL_THROUGH;
+ case 5:
+ k2 ^= (FT_UInt32)tail[4];
+ k2 *= c2;
+ k2 = ROTL32( k2, 16 );
+ k2 *= c3;
+ h2 ^= k2;
+ FALL_THROUGH;
+
+ case 4:
+ k1 ^= (FT_UInt32)tail[3] << 24;
+ FALL_THROUGH;
+ case 3:
+ k1 ^= (FT_UInt32)tail[2] << 16;
+ FALL_THROUGH;
+ case 2:
+ k1 ^= (FT_UInt32)tail[1] << 8;
+ FALL_THROUGH;
+ case 1:
+ k1 ^= (FT_UInt32)tail[0];
+ k1 *= c1;
+ k1 = ROTL32( k1, 15 );
+ k1 *= c2;
+ h1 ^= k1;
+ }
+ }
+
+ h1 ^= len;
+ h2 ^= len;
+ h3 ^= len;
+ h4 ^= len;
+
+ h1 += h2;
+ h1 += h3;
+ h1 += h4;
+
+ h2 += h1;
+ h3 += h1;
+ h4 += h1;
+
+ h1 = fmix32( h1 );
+ h2 = fmix32( h2 );
+ h3 = fmix32( h3 );
+ h4 = fmix32( h4 );
+
+ h1 += h2;
+ h1 += h3;
+ h1 += h4;
+
+ h2 += h1;
+ h3 += h1;
+ h4 += h1;
+
+ ((FT_UInt32*)out)[0] = h1;
+ ((FT_UInt32*)out)[1] = h2;
+ ((FT_UInt32*)out)[2] = h3;
+ ((FT_UInt32*)out)[3] = h4;
+ }
+
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+ typedef int (*char_type_func)( int c );
+
+
+ /* Handling of PID/EID 3/0 and 3/1 is the same. */
+#define IS_WIN( n ) ( (n)->platformID == 3 && \
+ ( (n)->encodingID == 1 || (n)->encodingID == 0 ) )
+
+#define IS_APPLE( n ) ( (n)->platformID == 1 && \
+ (n)->encodingID == 0 )
+
+ static char*
+ get_win_string( FT_Memory memory,
+ FT_Stream stream,
+ TT_Name entry,
+ char_type_func char_type,
+ FT_Bool report_invalid_characters )
+ {
+ FT_Error error;
+
+ char* result = NULL;
+ FT_String* r;
+ FT_Char* p;
+ FT_UInt len;
+
+
+ if ( FT_QALLOC( result, entry->stringLength / 2 + 1 ) )
+ return NULL;
+
+ if ( FT_STREAM_SEEK( entry->stringOffset ) ||
+ FT_FRAME_ENTER( entry->stringLength ) )
+ goto get_win_string_error;
+
+ r = (FT_String*)result;
+ p = (FT_Char*)stream->cursor;
+
+ for ( len = entry->stringLength / 2; len > 0; len--, p += 2 )
+ {
+ if ( p[0] == 0 && char_type( p[1] ) )
+ *r++ = p[1];
+ else
+ {
+ if ( report_invalid_characters )
+ FT_TRACE0(( "get_win_string:"
+ " Character 0x%X invalid in PS name string\n",
+ ((unsigned)p[0])*256 + (unsigned)p[1] ));
+ break;
+ }
+ }
+ if ( !len )
+ *r = '\0';
+
+ FT_FRAME_EXIT();
+
+ if ( !len )
+ return result;
+
+ get_win_string_error:
+ FT_FREE( result );
+
+ entry->stringLength = 0;
+ entry->stringOffset = 0;
+ FT_FREE( entry->string );
+
+ return NULL;
+ }
+
+
+ static char*
+ get_apple_string( FT_Memory memory,
+ FT_Stream stream,
+ TT_Name entry,
+ char_type_func char_type,
+ FT_Bool report_invalid_characters )
+ {
+ FT_Error error;
+
+ char* result = NULL;
+ FT_String* r;
+ FT_Char* p;
+ FT_UInt len;
+
+
+ if ( FT_QALLOC( result, entry->stringLength + 1 ) )
+ return NULL;
+
+ if ( FT_STREAM_SEEK( entry->stringOffset ) ||
+ FT_FRAME_ENTER( entry->stringLength ) )
+ goto get_apple_string_error;
+
+ r = (FT_String*)result;
+ p = (FT_Char*)stream->cursor;
+
+ for ( len = entry->stringLength; len > 0; len--, p++ )
+ {
+ if ( char_type( *p ) )
+ *r++ = *p;
+ else
+ {
+ if ( report_invalid_characters )
+ FT_TRACE0(( "get_apple_string:"
+ " Character `%c' (0x%X) invalid in PS name string\n",
+ *p, *p ));
+ break;
+ }
+ }
+ if ( !len )
+ *r = '\0';
+
+ FT_FRAME_EXIT();
+
+ if ( !len )
+ return result;
+
+ get_apple_string_error:
+ FT_FREE( result );
+
+ entry->stringOffset = 0;
+ entry->stringLength = 0;
+ FT_FREE( entry->string );
+
+ return NULL;
+ }
+
+
+ static FT_Bool
+ sfnt_get_name_id( TT_Face face,
+ FT_UShort id,
+ FT_Int *win,
+ FT_Int *apple )
+ {
+ FT_Int n;
+
+
+ *win = -1;
+ *apple = -1;
+
+ for ( n = 0; n < face->num_names; n++ )
+ {
+ TT_Name name = face->name_table.names + n;
+
+
+ if ( name->nameID == id && name->stringLength > 0 )
+ {
+ if ( IS_WIN( name ) && ( name->languageID == 0x409 || *win == -1 ) )
+ *win = n;
+
+ if ( IS_APPLE( name ) && ( name->languageID == 0 || *apple == -1 ) )
+ *apple = n;
+ }
+ }
+
+ return ( *win >= 0 ) || ( *apple >= 0 );
+ }
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+ /*
+ The maximum length of an axis value descriptor.
+
+ We need 65536 different values for the decimal fraction; this fits
+ nicely into five decimal places. Consequently, it consists of
+
+ . the minus sign if the number is negative,
+ . up to five characters for the digits before the decimal point,
+ . the decimal point if there is a fractional part, and
+ . up to five characters for the digits after the decimal point.
+
+ We also need one byte for the leading `_' character and up to four
+ bytes for the axis tag.
+ */
+#define MAX_VALUE_DESCRIPTOR_LEN ( 1 + 5 + 1 + 5 + 1 + 4 )
+
+
+ /* the maximum length of PostScript font names */
+#define MAX_PS_NAME_LEN 127
+
+
+ /*
+ * Find the shortest decimal representation of a 16.16 fixed-point
+ * number. The function fills `buf' with the result, returning a pointer
+ * to the position after the representation's last byte.
+ */
+
+ static char*
+ fixed2float( FT_Int fixed,
+ char* buf )
+ {
+ char* p;
+ char* q;
+ char tmp[5];
+
+ FT_Int int_part;
+ FT_Int frac_part;
+
+ FT_Int i;
+
+
+ p = buf;
+
+ if ( fixed == 0 )
+ {
+ *p++ = '0';
+ return p;
+ }
+
+ if ( fixed < 0 )
+ {
+ *p++ = '-';
+ fixed = NEG_INT( fixed );
+ }
+
+ int_part = ( fixed >> 16 ) & 0xFFFF;
+ frac_part = fixed & 0xFFFF;
+
+ /* get digits of integer part (in reverse order) */
+ q = tmp;
+ while ( int_part > 0 )
+ {
+ *q++ = '0' + int_part % 10;
+ int_part /= 10;
+ }
+
+ /* copy digits in correct order to buffer */
+ while ( q > tmp )
+ *p++ = *--q;
+
+ if ( !frac_part )
+ return p;
+
+ /* save position of point */
+ q = p;
+ *p++ = '.';
+
+ /* apply rounding */
+ frac_part = frac_part * 10 + 5;
+
+ /* get digits of fractional part */
+ for ( i = 0; i < 5; i++ )
+ {
+ *p++ = '0' + (char)( frac_part / 0x10000L );
+
+ frac_part %= 0x10000L;
+ if ( !frac_part )
+ break;
+
+ frac_part *= 10;
+ }
+
+ /*
+ If the remainder stored in `frac_part' (after the last FOR loop) is
+ smaller than 34480*10, the resulting decimal value minus 0.00001 is
+ an equivalent representation of `fixed'.
+
+ The above FOR loop always finds the larger of the two values; I
+ verified this by iterating over all possible fixed-point numbers.
+
+ If the remainder is 17232*10, both values are equally good, and we
+ take the next even number (following IEEE 754's `round to nearest,
+ ties to even' rounding rule).
+
+ If the remainder is smaller than 17232*10, the lower of the two
+ numbers is nearer to the exact result (values 17232 and 34480 were
+ also found by testing all possible fixed-point values).
+
+ We use this to find a shorter decimal representation. If not ending
+ with digit zero, we take the representation with less error.
+ */
+ p--;
+ if ( p - q == 5 ) /* five digits? */
+ {
+ /* take the representation that has zero as the last digit */
+ if ( frac_part < 34480 * 10 &&
+ *p == '1' )
+ *p = '0';
+
+ /* otherwise use the one with less error */
+ else if ( frac_part == 17232 * 10 &&
+ *p & 1 )
+ *p -= 1;
+
+ else if ( frac_part < 17232 * 10 &&
+ *p != '0' )
+ *p -= 1;
+ }
+
+ /* remove trailing zeros */
+ while ( *p == '0' )
+ *p-- = '\0';
+
+ return p + 1;
+ }
+
+
+ static const char hexdigits[16] =
+ {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+
+ static const char*
+ sfnt_get_var_ps_name( TT_Face face )
+ {
+ FT_Error error;
+ FT_Memory memory = face->root.memory;
+
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+
+ FT_UInt num_coords;
+ FT_Fixed* coords;
+ FT_MM_Var* mm_var;
+
+ FT_Int found, win, apple;
+ FT_UInt i, j;
+
+ char* result = NULL;
+ char* p;
+
+
+ if ( !face->var_postscript_prefix )
+ {
+ FT_UInt len;
+
+
+ /* check whether we have a Variations PostScript Name Prefix */
+ found = sfnt_get_name_id( face,
+ TT_NAME_ID_VARIATIONS_PREFIX,
+ &win,
+ &apple );
+ if ( !found )
+ {
+ /* otherwise use the typographic family name */
+ found = sfnt_get_name_id( face,
+ TT_NAME_ID_TYPOGRAPHIC_FAMILY,
+ &win,
+ &apple );
+ }
+
+ if ( !found )
+ {
+ /* as a last resort we try the family name; note that this is */
+ /* not in the Adobe TechNote, but GX fonts (which predate the */
+ /* TechNote) benefit from this behaviour */
+ found = sfnt_get_name_id( face,
+ TT_NAME_ID_FONT_FAMILY,
+ &win,
+ &apple );
+ }
+
+ if ( !found )
+ {
+ FT_TRACE0(( "sfnt_get_var_ps_name:"
+ " Can't construct PS name prefix for font instances\n" ));
+ return NULL;
+ }
+
+ /* prefer Windows entries over Apple */
+ if ( win != -1 )
+ result = get_win_string( face->root.memory,
+ face->name_table.stream,
+ face->name_table.names + win,
+ sfnt_is_alphanumeric,
+ 0 );
+ if ( !result && apple != -1 )
+ result = get_apple_string( face->root.memory,
+ face->name_table.stream,
+ face->name_table.names + apple,
+ sfnt_is_alphanumeric,
+ 0 );
+
+ if ( !result )
+ {
+ FT_TRACE0(( "sfnt_get_var_ps_name:"
+ " No valid PS name prefix for font instances found\n" ));
+ return NULL;
+ }
+
+ len = ft_strlen( result );
+
+ /* sanitize if necessary; we reserve space for 36 bytes (a 128bit */
+ /* checksum as a hex number, preceded by `-' and followed by three */
+ /* ASCII dots, to be used if the constructed PS name would be too */
+ /* long); this is also sufficient for a single instance */
+ if ( len > MAX_PS_NAME_LEN - ( 1 + 32 + 3 ) )
+ {
+ len = MAX_PS_NAME_LEN - ( 1 + 32 + 3 );
+ result[len] = '\0';
+
+ FT_TRACE0(( "sfnt_get_var_ps_name:"
+ " Shortening variation PS name prefix\n" ));
+ FT_TRACE0(( " "
+ " to %d characters\n", len ));
+ }
+
+ face->var_postscript_prefix = result;
+ face->var_postscript_prefix_len = len;
+ }
+
+ mm->get_var_blend( FT_FACE( face ),
+ &num_coords,
+ &coords,
+ NULL,
+ &mm_var );
+
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) &&
+ !FT_IS_VARIATION( FT_FACE( face ) ) )
+ {
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+ FT_Long instance = ( ( face->root.face_index & 0x7FFF0000L ) >> 16 ) - 1;
+ FT_UInt psid = mm_var->namedstyle[instance].psid;
+
+ char* ps_name = NULL;
+
+
+ /* try first to load the name string with index `postScriptNameID' */
+ if ( psid == 6 ||
+ ( psid > 255 && psid < 32768 ) )
+ (void)sfnt->get_name( face, (FT_UShort)psid, &ps_name );
+
+ if ( ps_name )
+ {
+ result = ps_name;
+ p = result + ft_strlen( result ) + 1;
+
+ goto check_length;
+ }
+ else
+ {
+ /* otherwise construct a name using `subfamilyNameID' */
+ FT_UInt strid = mm_var->namedstyle[instance].strid;
+
+ char* subfamily_name;
+ char* s;
+
+
+ (void)sfnt->get_name( face, (FT_UShort)strid, &subfamily_name );
+
+ if ( !subfamily_name )
+ {
+ FT_TRACE1(( "sfnt_get_var_ps_name:"
+ " can't construct named instance PS name;\n" ));
+ FT_TRACE1(( " "
+ " trying to construct normal instance PS name\n" ));
+ goto construct_instance_name;
+ }
+
+ /* after the prefix we have character `-' followed by the */
+ /* subfamily name (using only characters a-z, A-Z, and 0-9) */
+ if ( FT_QALLOC( result, face->var_postscript_prefix_len +
+ 1 + ft_strlen( subfamily_name ) + 1 ) )
+ return NULL;
+
+ ft_strcpy( result, face->var_postscript_prefix );
+
+ p = result + face->var_postscript_prefix_len;
+ *p++ = '-';
+
+ s = subfamily_name;
+ while ( *s )
+ {
+ if ( ft_isalnum( *s ) )
+ *p++ = *s;
+ s++;
+ }
+ *p++ = '\0';
+
+ FT_FREE( subfamily_name );
+ }
+ }
+ else
+ {
+ FT_Var_Axis* axis;
+
+
+ construct_instance_name:
+ axis = mm_var->axis;
+
+ if ( FT_QALLOC( result,
+ face->var_postscript_prefix_len +
+ num_coords * MAX_VALUE_DESCRIPTOR_LEN + 1 ) )
+ return NULL;
+
+ p = result;
+
+ ft_strcpy( p, face->var_postscript_prefix );
+ p += face->var_postscript_prefix_len;
+
+ for ( i = 0; i < num_coords; i++, coords++, axis++ )
+ {
+ char t;
+
+
+ /* omit axis value descriptor if it is identical */
+ /* to the default axis value */
+ if ( *coords == axis->def )
+ continue;
+
+ *p++ = '_';
+ p = fixed2float( *coords, p );
+
+ t = (char)( axis->tag >> 24 );
+ if ( t != ' ' && ft_isalnum( t ) )
+ *p++ = t;
+ t = (char)( axis->tag >> 16 );
+ if ( t != ' ' && ft_isalnum( t ) )
+ *p++ = t;
+ t = (char)( axis->tag >> 8 );
+ if ( t != ' ' && ft_isalnum( t ) )
+ *p++ = t;
+ t = (char)axis->tag;
+ if ( t != ' ' && ft_isalnum( t ) )
+ *p++ = t;
+ }
+ *p++ = '\0';
+ }
+
+ check_length:
+ if ( p - result > MAX_PS_NAME_LEN )
+ {
+ /* the PS name is too long; replace the part after the prefix with */
+ /* a checksum; we use MurmurHash 3 with a hash length of 128 bit */
+
+ FT_UInt32 seed = 123456789;
+
+ FT_UInt32 hash[4];
+ FT_UInt32* h;
+
+
+ murmur_hash_3_128( result, p - result, seed, hash );
+
+ p = result + face->var_postscript_prefix_len;
+ *p++ = '-';
+
+ /* we convert the hash value to hex digits from back to front */
+ p += 32 + 3;
+ h = hash + 3;
+
+ *p-- = '\0';
+ *p-- = '.';
+ *p-- = '.';
+ *p-- = '.';
+
+ for ( i = 0; i < 4; i++, h-- )
+ {
+ FT_UInt32 v = *h;
+
+
+ for ( j = 0; j < 8; j++ )
+ {
+ *p-- = hexdigits[v & 0xF];
+ v >>= 4;
+ }
+ }
+ }
+
+ return result;
+ }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+ static const char*
+ sfnt_get_ps_name( TT_Face face )
+ {
+ FT_Int found, win, apple;
+ const char* result = NULL;
+
+
+ if ( face->postscript_name )
+ return face->postscript_name;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( face->blend &&
+ ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
+ FT_IS_VARIATION( FT_FACE( face ) ) ) )
+ {
+ face->postscript_name = sfnt_get_var_ps_name( face );
+ return face->postscript_name;
+ }
+#endif
+
+ /* scan the name table to see whether we have a Postscript name here, */
+ /* either in Macintosh or Windows platform encodings */
+ found = sfnt_get_name_id( face, TT_NAME_ID_PS_NAME, &win, &apple );
+ if ( !found )
+ return NULL;
+
+ /* prefer Windows entries over Apple */
+ if ( win != -1 )
+ result = get_win_string( face->root.memory,
+ face->name_table.stream,
+ face->name_table.names + win,
+ sfnt_is_postscript,
+ 1 );
+ if ( !result && apple != -1 )
+ result = get_apple_string( face->root.memory,
+ face->name_table.stream,
+ face->name_table.names + apple,
+ sfnt_is_postscript,
+ 1 );
+
+ face->postscript_name = result;
+
+ return result;
+ }
+
+
+ FT_DEFINE_SERVICE_PSFONTNAMEREC(
+ sfnt_service_ps_name,
+
+ (FT_PsName_GetFunc)sfnt_get_ps_name /* get_ps_font_name */
+ )
+
+
+ /*
+ * TT CMAP INFO
+ */
+ FT_DEFINE_SERVICE_TTCMAPSREC(
+ tt_service_get_cmap_info,
+
+ (TT_CMap_Info_GetFunc)tt_get_cmap_info /* get_cmap_info */
+ )
+
+
+#ifdef TT_CONFIG_OPTION_BDF
+
+ static FT_Error
+ sfnt_get_charset_id( TT_Face face,
+ const char* *acharset_encoding,
+ const char* *acharset_registry )
+ {
+ BDF_PropertyRec encoding, registry;
+ FT_Error error;
+
+
+ /* XXX: I don't know whether this is correct, since
+ * tt_face_find_bdf_prop only returns something correct if we have
+ * previously selected a size that is listed in the BDF table.
+ * Should we change the BDF table format to include single offsets
+ * for `CHARSET_REGISTRY' and `CHARSET_ENCODING'?
+ */
+ error = tt_face_find_bdf_prop( face, "CHARSET_REGISTRY", &registry );
+ if ( !error )
+ {
+ error = tt_face_find_bdf_prop( face, "CHARSET_ENCODING", &encoding );
+ if ( !error )
+ {
+ if ( registry.type == BDF_PROPERTY_TYPE_ATOM &&
+ encoding.type == BDF_PROPERTY_TYPE_ATOM )
+ {
+ *acharset_encoding = encoding.u.atom;
+ *acharset_registry = registry.u.atom;
+ }
+ else
+ error = FT_THROW( Invalid_Argument );
+ }
+ }
+
+ return error;
+ }
+
+
+ FT_DEFINE_SERVICE_BDFRec(
+ sfnt_service_bdf,
+
+ (FT_BDF_GetCharsetIdFunc)sfnt_get_charset_id, /* get_charset_id */
+ (FT_BDF_GetPropertyFunc) tt_face_find_bdf_prop /* get_property */
+ )
+
+
+#endif /* TT_CONFIG_OPTION_BDF */
+
+
+ /*
+ * SERVICE LIST
+ */
+
+#if defined TT_CONFIG_OPTION_POSTSCRIPT_NAMES && defined TT_CONFIG_OPTION_BDF
+ FT_DEFINE_SERVICEDESCREC5(
+ sfnt_services,
+
+ FT_SERVICE_ID_SFNT_TABLE, &sfnt_service_sfnt_table,
+ FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name,
+ FT_SERVICE_ID_GLYPH_DICT, &sfnt_service_glyph_dict,
+ FT_SERVICE_ID_BDF, &sfnt_service_bdf,
+ FT_SERVICE_ID_TT_CMAP, &tt_service_get_cmap_info )
+#elif defined TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ FT_DEFINE_SERVICEDESCREC4(
+ sfnt_services,
+
+ FT_SERVICE_ID_SFNT_TABLE, &sfnt_service_sfnt_table,
+ FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name,
+ FT_SERVICE_ID_GLYPH_DICT, &sfnt_service_glyph_dict,
+ FT_SERVICE_ID_TT_CMAP, &tt_service_get_cmap_info )
+#elif defined TT_CONFIG_OPTION_BDF
+ FT_DEFINE_SERVICEDESCREC4(
+ sfnt_services,
+
+ FT_SERVICE_ID_SFNT_TABLE, &sfnt_service_sfnt_table,
+ FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name,
+ FT_SERVICE_ID_BDF, &sfnt_service_bdf,
+ FT_SERVICE_ID_TT_CMAP, &tt_service_get_cmap_info )
+#else
+ FT_DEFINE_SERVICEDESCREC3(
+ sfnt_services,
+
+ FT_SERVICE_ID_SFNT_TABLE, &sfnt_service_sfnt_table,
+ FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name,
+ FT_SERVICE_ID_TT_CMAP, &tt_service_get_cmap_info )
+#endif
+
+
+ FT_CALLBACK_DEF( FT_Module_Interface )
+ sfnt_get_interface( FT_Module module,
+ const char* module_interface )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( sfnt_services, module_interface );
+ }
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+#define PUT_EMBEDDED_BITMAPS( a ) a
+#else
+#define PUT_EMBEDDED_BITMAPS( a ) NULL
+#endif
+
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+#define PUT_COLOR_LAYERS( a ) a
+#else
+#define PUT_COLOR_LAYERS( a ) NULL
+#endif
+
+#ifdef FT_CONFIG_OPTION_SVG
+#define PUT_SVG_SUPPORT( a ) a
+#else
+#define PUT_SVG_SUPPORT( a ) NULL
+#endif
+
+#define PUT_COLOR_LAYERS_V1( a ) PUT_COLOR_LAYERS( a )
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+#define PUT_PS_NAMES( a ) a
+#else
+#define PUT_PS_NAMES( a ) NULL
+#endif
+
+ FT_DEFINE_SFNT_INTERFACE(
+ sfnt_interface,
+
+ tt_face_goto_table, /* TT_Loader_GotoTableFunc goto_table */
+
+ sfnt_init_face, /* TT_Init_Face_Func init_face */
+ sfnt_load_face, /* TT_Load_Face_Func load_face */
+ sfnt_done_face, /* TT_Done_Face_Func done_face */
+ sfnt_get_interface, /* FT_Module_Requester get_interface */
+
+ tt_face_load_any, /* TT_Load_Any_Func load_any */
+
+ tt_face_load_head, /* TT_Load_Table_Func load_head */
+ tt_face_load_hhea, /* TT_Load_Metrics_Func load_hhea */
+ tt_face_load_cmap, /* TT_Load_Table_Func load_cmap */
+ tt_face_load_maxp, /* TT_Load_Table_Func load_maxp */
+ tt_face_load_os2, /* TT_Load_Table_Func load_os2 */
+ tt_face_load_post, /* TT_Load_Table_Func load_post */
+
+ tt_face_load_name, /* TT_Load_Table_Func load_name */
+ tt_face_free_name, /* TT_Free_Table_Func free_name */
+
+ tt_face_load_kern, /* TT_Load_Table_Func load_kern */
+ tt_face_load_gasp, /* TT_Load_Table_Func load_gasp */
+ tt_face_load_pclt, /* TT_Load_Table_Func load_init */
+
+ /* see `ttload.h' */
+ PUT_EMBEDDED_BITMAPS( tt_face_load_bhed ),
+ /* TT_Load_Table_Func load_bhed */
+ PUT_EMBEDDED_BITMAPS( tt_face_load_sbit_image ),
+ /* TT_Load_SBit_Image_Func load_sbit_image */
+
+ /* see `ttpost.h' */
+ PUT_PS_NAMES( tt_face_get_ps_name ),
+ /* TT_Get_PS_Name_Func get_psname */
+ PUT_PS_NAMES( tt_face_free_ps_names ),
+ /* TT_Free_Table_Func free_psnames */
+
+ /* since version 2.1.8 */
+ tt_face_get_kerning, /* TT_Face_GetKerningFunc get_kerning */
+
+ /* since version 2.2 */
+ tt_face_load_font_dir, /* TT_Load_Table_Func load_font_dir */
+ tt_face_load_hmtx, /* TT_Load_Metrics_Func load_hmtx */
+
+ /* see `ttsbit.h' and `sfnt.h' */
+ PUT_EMBEDDED_BITMAPS( tt_face_load_sbit ),
+ /* TT_Load_Table_Func load_eblc */
+ PUT_EMBEDDED_BITMAPS( tt_face_free_sbit ),
+ /* TT_Free_Table_Func free_eblc */
+
+ PUT_EMBEDDED_BITMAPS( tt_face_set_sbit_strike ),
+ /* TT_Set_SBit_Strike_Func set_sbit_strike */
+ PUT_EMBEDDED_BITMAPS( tt_face_load_strike_metrics ),
+ /* TT_Load_Strike_Metrics_Func load_strike_metrics */
+
+ PUT_COLOR_LAYERS( tt_face_load_cpal ),
+ /* TT_Load_Table_Func load_cpal */
+ PUT_COLOR_LAYERS( tt_face_load_colr ),
+ /* TT_Load_Table_Func load_colr */
+ PUT_COLOR_LAYERS( tt_face_free_cpal ),
+ /* TT_Free_Table_Func free_cpal */
+ PUT_COLOR_LAYERS( tt_face_free_colr ),
+ /* TT_Free_Table_Func free_colr */
+ PUT_COLOR_LAYERS( tt_face_palette_set ),
+ /* TT_Set_Palette_Func set_palette */
+ PUT_COLOR_LAYERS( tt_face_get_colr_layer ),
+ /* TT_Get_Colr_Layer_Func get_colr_layer */
+
+ PUT_COLOR_LAYERS_V1( tt_face_get_colr_glyph_paint ),
+ /* TT_Get_Color_Glyph_Paint_Func get_colr_glyph_paint */
+ PUT_COLOR_LAYERS_V1( tt_face_get_color_glyph_clipbox ),
+ /* TT_Get_Color_Glyph_ClipBox_Func get_clipbox */
+ PUT_COLOR_LAYERS_V1( tt_face_get_paint_layers ),
+ /* TT_Get_Paint_Layers_Func get_paint_layers */
+ PUT_COLOR_LAYERS_V1( tt_face_get_colorline_stops ),
+ /* TT_Get_Paint get_paint */
+ PUT_COLOR_LAYERS_V1( tt_face_get_paint ),
+ /* TT_Get_Colorline_Stops_Func get_colorline_stops */
+
+ PUT_COLOR_LAYERS( tt_face_colr_blend_layer ),
+ /* TT_Blend_Colr_Func colr_blend */
+
+ tt_face_get_metrics, /* TT_Get_Metrics_Func get_metrics */
+
+ tt_face_get_name, /* TT_Get_Name_Func get_name */
+ sfnt_get_name_id, /* TT_Get_Name_ID_Func get_name_id */
+
+ PUT_SVG_SUPPORT( tt_face_load_svg ),
+ /* TT_Load_Table_Func load_svg */
+ PUT_SVG_SUPPORT( tt_face_free_svg ),
+ /* TT_Free_Table_Func free_svg */
+ PUT_SVG_SUPPORT( tt_face_load_svg_doc )
+ /* TT_Load_Svg_Doc_Func load_svg_doc */
+ )
+
+
+ FT_DEFINE_MODULE(
+ sfnt_module_class,
+
+ 0, /* not a font driver or renderer */
+ sizeof ( FT_ModuleRec ),
+
+ "sfnt", /* driver name */
+ 0x10000L, /* driver version 1.0 */
+ 0x20000L, /* driver requires FreeType 2.0 or higher */
+
+ (const void*)&sfnt_interface, /* module specific interface */
+
+ (FT_Module_Constructor)NULL, /* module_init */
+ (FT_Module_Destructor) NULL, /* module_done */
+ (FT_Module_Requester) sfnt_get_interface /* get_interface */
+ )
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sfdriver.h b/modules/freetype2/src/sfnt/sfdriver.h
new file mode 100644
index 0000000000..2445958b69
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfdriver.h
@@ -0,0 +1,35 @@
+/****************************************************************************
+ *
+ * sfdriver.h
+ *
+ * High-level SFNT driver interface (specification).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#ifndef SFDRIVER_H_
+#define SFDRIVER_H_
+
+
+#include <freetype/ftmodapi.h>
+
+
+FT_BEGIN_HEADER
+
+ FT_DECLARE_MODULE( sfnt_module_class )
+
+FT_END_HEADER
+
+#endif /* SFDRIVER_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sferrors.h b/modules/freetype2/src/sfnt/sferrors.h
new file mode 100644
index 0000000000..e7a8eb04bb
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sferrors.h
@@ -0,0 +1,41 @@
+/****************************************************************************
+ *
+ * sferrors.h
+ *
+ * SFNT error codes (specification only).
+ *
+ * Copyright (C) 2001-2023 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.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * This file is used to define the SFNT error enumeration constants.
+ *
+ */
+
+#ifndef SFERRORS_H_
+#define SFERRORS_H_
+
+#include <freetype/ftmoderr.h>
+
+#undef FTERRORS_H_
+
+#undef FT_ERR_PREFIX
+#define FT_ERR_PREFIX SFNT_Err_
+#define FT_ERR_BASE FT_Mod_Err_SFNT
+
+#include <freetype/fterrors.h>
+
+#endif /* SFERRORS_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sfnt.c b/modules/freetype2/src/sfnt/sfnt.c
new file mode 100644
index 0000000000..8e4f08a90c
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfnt.c
@@ -0,0 +1,40 @@
+/****************************************************************************
+ *
+ * sfnt.c
+ *
+ * Single object library component.
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include "pngshim.c"
+#include "sfdriver.c"
+#include "sfobjs.c"
+#include "sfwoff.c"
+#include "sfwoff2.c"
+#include "ttbdf.c"
+#include "ttcmap.c"
+#include "ttcolr.c"
+#include "ttcpal.c"
+#include "ttsvg.c"
+
+#include "ttkern.c"
+#include "ttload.c"
+#include "ttmtx.c"
+#include "ttpost.c"
+#include "ttsbit.c"
+#include "woff2tags.c"
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sfobjs.c b/modules/freetype2/src/sfnt/sfobjs.c
new file mode 100644
index 0000000000..e018934cca
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfobjs.c
@@ -0,0 +1,1515 @@
+/****************************************************************************
+ *
+ * sfobjs.c
+ *
+ * SFNT object management (base).
+ *
+ * Copyright (C) 1996-2023 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 "sfobjs.h"
+#include "ttload.h"
+#include "ttcmap.h"
+#include "ttkern.h"
+#include "sfwoff.h"
+#include "sfwoff2.h"
+#include <freetype/internal/sfnt.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/ttnameid.h>
+#include <freetype/tttags.h>
+#include <freetype/internal/services/svpscmap.h>
+#include <freetype/ftsnames.h>
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include <freetype/internal/services/svmm.h>
+#include <freetype/internal/services/svmetric.h>
+#endif
+
+#include "sferrors.h"
+
+#ifdef TT_CONFIG_OPTION_BDF
+#include "ttbdf.h"
+#endif
+
+
+ /**************************************************************************
+ *
+ * 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 sfobjs
+
+
+
+ /* convert a UTF-16 name entry to ASCII */
+ static FT_String*
+ tt_name_ascii_from_utf16( TT_Name entry,
+ FT_Memory memory )
+ {
+ FT_String* string = NULL;
+ FT_UInt len, code, n;
+ FT_Byte* read = (FT_Byte*)entry->string;
+ FT_Error error;
+
+
+ len = (FT_UInt)entry->stringLength / 2;
+
+ if ( FT_QNEW_ARRAY( string, len + 1 ) )
+ return NULL;
+
+ for ( n = 0; n < len; n++ )
+ {
+ code = FT_NEXT_USHORT( read );
+
+ if ( code == 0 )
+ break;
+
+ if ( code < 32 || code > 127 )
+ code = '?';
+
+ string[n] = (char)code;
+ }
+
+ string[n] = 0;
+
+ return string;
+ }
+
+
+ /* convert an Apple Roman or symbol name entry to ASCII */
+ static FT_String*
+ tt_name_ascii_from_other( TT_Name entry,
+ FT_Memory memory )
+ {
+ FT_String* string = NULL;
+ FT_UInt len, code, n;
+ FT_Byte* read = (FT_Byte*)entry->string;
+ FT_Error error;
+
+
+ len = (FT_UInt)entry->stringLength;
+
+ if ( FT_QNEW_ARRAY( string, len + 1 ) )
+ return NULL;
+
+ for ( n = 0; n < len; n++ )
+ {
+ code = *read++;
+
+ if ( code == 0 )
+ break;
+
+ if ( code < 32 || code > 127 )
+ code = '?';
+
+ string[n] = (char)code;
+ }
+
+ string[n] = 0;
+
+ return string;
+ }
+
+
+ typedef FT_String* (*TT_Name_ConvertFunc)( TT_Name entry,
+ FT_Memory memory );
+
+
+ /* documentation is in sfnt.h */
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_get_name( TT_Face face,
+ FT_UShort nameid,
+ FT_String** name )
+ {
+ FT_Memory memory = face->root.memory;
+ FT_Error error = FT_Err_Ok;
+ FT_String* result = NULL;
+ FT_UShort n;
+ TT_Name rec;
+
+ FT_Int found_apple = -1;
+ FT_Int found_apple_roman = -1;
+ FT_Int found_apple_english = -1;
+ FT_Int found_win = -1;
+ FT_Int found_unicode = -1;
+
+ FT_Bool is_english = 0;
+
+ TT_Name_ConvertFunc convert;
+
+
+ FT_ASSERT( name );
+
+ rec = face->name_table.names;
+ for ( n = 0; n < face->num_names; n++, rec++ )
+ {
+ /* According to the OpenType 1.3 specification, only Microsoft or */
+ /* Apple platform IDs might be used in the `name' table. The */
+ /* `Unicode' platform is reserved for the `cmap' table, and the */
+ /* `ISO' one is deprecated. */
+ /* */
+ /* However, the Apple TrueType specification doesn't say the same */
+ /* thing and goes to suggest that all Unicode `name' table entries */
+ /* should be coded in UTF-16 (in big-endian format I suppose). */
+ /* */
+ if ( rec->nameID == nameid && rec->stringLength > 0 )
+ {
+ switch ( rec->platformID )
+ {
+ case TT_PLATFORM_APPLE_UNICODE:
+ case TT_PLATFORM_ISO:
+ /* there is `languageID' to check there. We should use this */
+ /* field only as a last solution when nothing else is */
+ /* available. */
+ /* */
+ found_unicode = n;
+ break;
+
+ case TT_PLATFORM_MACINTOSH:
+ /* This is a bit special because some fonts will use either */
+ /* an English language id, or a Roman encoding id, to indicate */
+ /* the English version of its font name. */
+ /* */
+ if ( rec->languageID == TT_MAC_LANGID_ENGLISH )
+ found_apple_english = n;
+ else if ( rec->encodingID == TT_MAC_ID_ROMAN )
+ found_apple_roman = n;
+ break;
+
+ case TT_PLATFORM_MICROSOFT:
+ /* we only take a non-English name when there is nothing */
+ /* else available in the font */
+ /* */
+ if ( found_win == -1 || ( rec->languageID & 0x3FF ) == 0x009 )
+ {
+ switch ( rec->encodingID )
+ {
+ case TT_MS_ID_SYMBOL_CS:
+ case TT_MS_ID_UNICODE_CS:
+ case TT_MS_ID_UCS_4:
+ is_english = FT_BOOL( ( rec->languageID & 0x3FF ) == 0x009 );
+ found_win = n;
+ break;
+
+ default:
+ ;
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+ }
+ }
+
+ found_apple = found_apple_roman;
+ if ( found_apple_english >= 0 )
+ found_apple = found_apple_english;
+
+ /* some fonts contain invalid Unicode or Macintosh formatted entries; */
+ /* we will thus favor names encoded in Windows formats if available */
+ /* (provided it is an English name) */
+ /* */
+ convert = NULL;
+ if ( found_win >= 0 && !( found_apple >= 0 && !is_english ) )
+ {
+ rec = face->name_table.names + found_win;
+ switch ( rec->encodingID )
+ {
+ /* all Unicode strings are encoded using UTF-16BE */
+ case TT_MS_ID_UNICODE_CS:
+ case TT_MS_ID_SYMBOL_CS:
+ convert = tt_name_ascii_from_utf16;
+ break;
+
+ case TT_MS_ID_UCS_4:
+ /* Apparently, if this value is found in a name table entry, it is */
+ /* documented as `full Unicode repertoire'. Experience with the */
+ /* MsGothic font shipped with Windows Vista shows that this really */
+ /* means UTF-16 encoded names (UCS-4 values are only used within */
+ /* charmaps). */
+ convert = tt_name_ascii_from_utf16;
+ break;
+
+ default:
+ ;
+ }
+ }
+ else if ( found_apple >= 0 )
+ {
+ rec = face->name_table.names + found_apple;
+ convert = tt_name_ascii_from_other;
+ }
+ else if ( found_unicode >= 0 )
+ {
+ rec = face->name_table.names + found_unicode;
+ convert = tt_name_ascii_from_utf16;
+ }
+
+ if ( rec && convert )
+ {
+ if ( !rec->string )
+ {
+ FT_Stream stream = face->name_table.stream;
+
+
+ if ( FT_QNEW_ARRAY ( rec->string, rec->stringLength ) ||
+ FT_STREAM_SEEK( rec->stringOffset ) ||
+ FT_STREAM_READ( rec->string, rec->stringLength ) )
+ {
+ FT_FREE( rec->string );
+ rec->stringLength = 0;
+ result = NULL;
+ goto Exit;
+ }
+ }
+
+ result = convert( rec, memory );
+ }
+
+ Exit:
+ *name = result;
+ return error;
+ }
+
+
+ static FT_Encoding
+ sfnt_find_encoding( int platform_id,
+ int encoding_id )
+ {
+ typedef struct TEncoding_
+ {
+ int platform_id;
+ int encoding_id;
+ FT_Encoding encoding;
+
+ } TEncoding;
+
+ static
+ const TEncoding tt_encodings[] =
+ {
+ { TT_PLATFORM_ISO, -1, FT_ENCODING_UNICODE },
+
+ { TT_PLATFORM_APPLE_UNICODE, -1, FT_ENCODING_UNICODE },
+
+ { TT_PLATFORM_MACINTOSH, TT_MAC_ID_ROMAN, FT_ENCODING_APPLE_ROMAN },
+
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS, FT_ENCODING_MS_SYMBOL },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_UCS_4, FT_ENCODING_UNICODE },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, FT_ENCODING_UNICODE },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_SJIS, FT_ENCODING_SJIS },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_PRC, FT_ENCODING_PRC },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_BIG_5, FT_ENCODING_BIG5 },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_WANSUNG, FT_ENCODING_WANSUNG },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_JOHAB, FT_ENCODING_JOHAB }
+ };
+
+ const TEncoding *cur, *limit;
+
+
+ cur = tt_encodings;
+ limit = cur + sizeof ( tt_encodings ) / sizeof ( tt_encodings[0] );
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur->platform_id == platform_id )
+ {
+ if ( cur->encoding_id == encoding_id ||
+ cur->encoding_id == -1 )
+ return cur->encoding;
+ }
+ }
+
+ return FT_ENCODING_NONE;
+ }
+
+
+ /* Fill in face->ttc_header. If the font is not a TTC, it is */
+ /* synthesized into a TTC with one offset table. */
+ static FT_Error
+ sfnt_open_font( FT_Stream stream,
+ TT_Face face,
+ FT_Int* face_instance_index,
+ FT_Long* woff2_num_faces )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+ FT_ULong tag, offset;
+
+ static const FT_Frame_Field ttc_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TTC_HeaderRec
+
+ FT_FRAME_START( 8 ),
+ FT_FRAME_LONG( version ),
+ FT_FRAME_LONG( count ), /* this is ULong in the specs */
+ FT_FRAME_END
+ };
+
+#ifndef FT_CONFIG_OPTION_USE_BROTLI
+ FT_UNUSED( face_instance_index );
+ FT_UNUSED( woff2_num_faces );
+#endif
+
+
+ face->ttc_header.tag = 0;
+ face->ttc_header.version = 0;
+ face->ttc_header.count = 0;
+
+#if defined( FT_CONFIG_OPTION_USE_ZLIB ) || \
+ defined( FT_CONFIG_OPTION_USE_BROTLI )
+ retry:
+#endif
+
+ offset = FT_STREAM_POS();
+
+ if ( FT_READ_ULONG( tag ) )
+ return error;
+
+#ifdef FT_CONFIG_OPTION_USE_ZLIB
+ if ( tag == TTAG_wOFF )
+ {
+ FT_TRACE2(( "sfnt_open_font: file is a WOFF; synthesizing SFNT\n" ));
+
+ if ( FT_STREAM_SEEK( offset ) )
+ return error;
+
+ error = woff_open_font( stream, face );
+ if ( error )
+ return error;
+
+ /* Swap out stream and retry! */
+ stream = face->root.stream;
+ goto retry;
+ }
+#endif
+
+#ifdef FT_CONFIG_OPTION_USE_BROTLI
+ if ( tag == TTAG_wOF2 )
+ {
+ FT_TRACE2(( "sfnt_open_font: file is a WOFF2; synthesizing SFNT\n" ));
+
+ if ( FT_STREAM_SEEK( offset ) )
+ return error;
+
+ error = woff2_open_font( stream,
+ face,
+ face_instance_index,
+ woff2_num_faces );
+ if ( error )
+ return error;
+
+ /* Swap out stream and retry! */
+ stream = face->root.stream;
+ goto retry;
+ }
+#endif
+
+ if ( tag != 0x00010000UL &&
+ tag != TTAG_ttcf &&
+ tag != TTAG_OTTO &&
+ tag != TTAG_true &&
+ tag != TTAG_typ1 &&
+ tag != TTAG_0xA5kbd &&
+ tag != TTAG_0xA5lst &&
+ tag != 0x00020000UL )
+ {
+ FT_TRACE2(( " not a font using the SFNT container format\n" ));
+ return FT_THROW( Unknown_File_Format );
+ }
+
+ face->ttc_header.tag = TTAG_ttcf;
+
+ if ( tag == TTAG_ttcf )
+ {
+ FT_Int n;
+
+
+ FT_TRACE3(( "sfnt_open_font: file is a collection\n" ));
+
+ if ( FT_STREAM_READ_FIELDS( ttc_header_fields, &face->ttc_header ) )
+ return error;
+
+ FT_TRACE3(( " with %ld subfonts\n",
+ face->ttc_header.count ));
+
+ if ( face->ttc_header.count == 0 )
+ return FT_THROW( Invalid_Table );
+
+ /* a rough size estimate: let's conservatively assume that there */
+ /* is just a single table info in each subfont header (12 + 16*1 = */
+ /* 28 bytes), thus we have (at least) `12 + 4*count' bytes for the */
+ /* size of the TTC header plus `28*count' bytes for all subfont */
+ /* headers */
+ if ( (FT_ULong)face->ttc_header.count > stream->size / ( 28 + 4 ) )
+ return FT_THROW( Array_Too_Large );
+
+ /* now read the offsets of each font in the file */
+ if ( FT_QNEW_ARRAY( face->ttc_header.offsets, face->ttc_header.count ) )
+ return error;
+
+ if ( FT_FRAME_ENTER( face->ttc_header.count * 4L ) )
+ return error;
+
+ for ( n = 0; n < face->ttc_header.count; n++ )
+ face->ttc_header.offsets[n] = FT_GET_ULONG();
+
+ FT_FRAME_EXIT();
+ }
+ else
+ {
+ FT_TRACE3(( "sfnt_open_font: synthesize TTC\n" ));
+
+ face->ttc_header.version = 1 << 16;
+ face->ttc_header.count = 1;
+
+ if ( FT_QNEW( face->ttc_header.offsets ) )
+ return error;
+
+ face->ttc_header.offsets[0] = offset;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ sfnt_init_face( FT_Stream stream,
+ TT_Face face,
+ FT_Int face_instance_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error;
+ FT_Library library = face->root.driver->root.library;
+ SFNT_Service sfnt;
+ FT_Int face_index;
+ FT_Long woff2_num_faces = 0;
+
+
+ /* for now, parameters are unused */
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+
+
+ sfnt = (SFNT_Service)face->sfnt;
+ if ( !sfnt )
+ {
+ sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" );
+ if ( !sfnt )
+ {
+ FT_ERROR(( "sfnt_init_face: cannot access `sfnt' module\n" ));
+ return FT_THROW( Missing_Module );
+ }
+
+ face->sfnt = sfnt;
+ face->goto_table = sfnt->goto_table;
+ }
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, face->psnames, POSTSCRIPT_CMAPS );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( !face->mm )
+ {
+ /* we want the MM interface from the `truetype' module only */
+ FT_Module tt_module = FT_Get_Module( library, "truetype" );
+
+
+ face->mm = ft_module_get_service( tt_module,
+ FT_SERVICE_ID_MULTI_MASTERS,
+ 0 );
+ }
+
+ if ( !face->var )
+ {
+ /* we want the metrics variations interface */
+ /* from the `truetype' module only */
+ FT_Module tt_module = FT_Get_Module( library, "truetype" );
+
+
+ face->var = ft_module_get_service( tt_module,
+ FT_SERVICE_ID_METRICS_VARIATIONS,
+ 0 );
+ }
+#endif
+
+ FT_TRACE2(( "SFNT driver\n" ));
+
+ error = sfnt_open_font( stream,
+ face,
+ &face_instance_index,
+ &woff2_num_faces );
+ if ( error )
+ return error;
+
+ /* Stream may have changed in sfnt_open_font. */
+ stream = face->root.stream;
+
+ FT_TRACE2(( "sfnt_init_face: %p (index %d)\n",
+ (void *)face,
+ face_instance_index ));
+
+ face_index = FT_ABS( face_instance_index ) & 0xFFFF;
+
+ /* value -(N+1) requests information on index N */
+ if ( face_instance_index < 0 && face_index > 0 )
+ face_index--;
+
+ if ( face_index >= face->ttc_header.count )
+ {
+ if ( face_instance_index >= 0 )
+ return FT_THROW( Invalid_Argument );
+ else
+ face_index = 0;
+ }
+
+ if ( FT_STREAM_SEEK( face->ttc_header.offsets[face_index] ) )
+ return error;
+
+ /* check whether we have a valid TrueType file */
+ error = sfnt->load_font_dir( face, stream );
+ if ( error )
+ return error;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ {
+ FT_Memory memory = face->root.memory;
+
+ FT_ULong fvar_len;
+
+ FT_ULong version;
+ FT_ULong offset;
+
+ FT_UShort num_axes;
+ FT_UShort axis_size;
+ FT_UShort num_instances;
+ FT_UShort instance_size;
+
+ FT_Int instance_index;
+
+ FT_Byte* default_values = NULL;
+ FT_Byte* instance_values = NULL;
+
+
+ instance_index = FT_ABS( face_instance_index ) >> 16;
+
+ /* test whether current face is a GX font with named instances */
+ if ( face->goto_table( face, TTAG_fvar, stream, &fvar_len ) ||
+ fvar_len < 20 ||
+ FT_READ_ULONG( version ) ||
+ FT_READ_USHORT( offset ) ||
+ FT_STREAM_SKIP( 2 ) /* reserved */ ||
+ FT_READ_USHORT( num_axes ) ||
+ FT_READ_USHORT( axis_size ) ||
+ FT_READ_USHORT( num_instances ) ||
+ FT_READ_USHORT( instance_size ) )
+ {
+ version = 0;
+ offset = 0;
+ num_axes = 0;
+ axis_size = 0;
+ num_instances = 0;
+ instance_size = 0;
+ }
+
+ /* check that the data is bound by the table length */
+ if ( version != 0x00010000UL ||
+ axis_size != 20 ||
+ num_axes == 0 ||
+ /* `num_axes' limit implied by 16-bit `instance_size' */
+ num_axes > 0x3FFE ||
+ !( instance_size == 4 + 4 * num_axes ||
+ instance_size == 6 + 4 * num_axes ) ||
+ /* `num_instances' limit implied by limited range of name IDs */
+ num_instances > 0x7EFF ||
+ offset +
+ axis_size * num_axes +
+ instance_size * num_instances > fvar_len )
+ num_instances = 0;
+ else
+ face->variation_support |= TT_FACE_FLAG_VAR_FVAR;
+
+ /*
+ * As documented in the OpenType specification, an entry for the
+ * default instance may be omitted in the named instance table. In
+ * particular this means that even if there is no named instance
+ * table in the font we actually do have a named instance, namely the
+ * default instance.
+ *
+ * For consistency, we always want the default instance in our list
+ * of named instances. If it is missing, we try to synthesize it
+ * later on. Here, we have to adjust `num_instances' accordingly.
+ */
+
+ if ( ( face->variation_support & TT_FACE_FLAG_VAR_FVAR ) &&
+ !( FT_QALLOC( default_values, num_axes * 4 ) ||
+ FT_QALLOC( instance_values, num_axes * 4 ) ) )
+ {
+ /* the current stream position is 16 bytes after the table start */
+ FT_ULong array_start = FT_STREAM_POS() - 16 + offset;
+ FT_ULong default_value_offset, instance_offset;
+
+ FT_Byte* p;
+ FT_UInt i;
+
+
+ default_value_offset = array_start + 8;
+ p = default_values;
+
+ for ( i = 0; i < num_axes; i++ )
+ {
+ (void)FT_STREAM_READ_AT( default_value_offset, p, 4 );
+
+ default_value_offset += axis_size;
+ p += 4;
+ }
+
+ instance_offset = array_start + axis_size * num_axes + 4;
+
+ for ( i = 0; i < num_instances; i++ )
+ {
+ (void)FT_STREAM_READ_AT( instance_offset,
+ instance_values,
+ num_axes * 4 );
+
+ if ( !ft_memcmp( default_values, instance_values, num_axes * 4 ) )
+ break;
+
+ instance_offset += instance_size;
+ }
+
+ if ( i == num_instances )
+ {
+ /* no default instance in named instance table; */
+ /* we thus have to synthesize it */
+ num_instances++;
+ }
+ }
+
+ FT_FREE( default_values );
+ FT_FREE( instance_values );
+
+ /* we don't support Multiple Master CFFs yet; */
+ /* note that `glyf' or `CFF2' have precedence */
+ if ( face->goto_table( face, TTAG_glyf, stream, 0 ) &&
+ face->goto_table( face, TTAG_CFF2, stream, 0 ) &&
+ !face->goto_table( face, TTAG_CFF, stream, 0 ) )
+ num_instances = 0;
+
+ /* instance indices in `face_instance_index' start with index 1, */
+ /* thus `>' and not `>=' */
+ if ( instance_index > num_instances )
+ {
+ if ( face_instance_index >= 0 )
+ return FT_THROW( Invalid_Argument );
+ else
+ num_instances = 0;
+ }
+
+ face->root.style_flags = (FT_Long)num_instances << 16;
+ }
+#endif
+
+ face->root.num_faces = face->ttc_header.count;
+ face->root.face_index = face_instance_index;
+
+ /* `num_faces' for a WOFF2 needs to be handled separately. */
+ if ( woff2_num_faces )
+ face->root.num_faces = woff2_num_faces;
+
+ return error;
+ }
+
+
+#define LOAD_( x ) \
+ do \
+ { \
+ FT_TRACE2(( "`" #x "' " )); \
+ FT_TRACE3(( "-->\n" )); \
+ \
+ error = sfnt->load_ ## x( face, stream ); \
+ \
+ FT_TRACE2(( "%s\n", ( !error ) \
+ ? "loaded" \
+ : FT_ERR_EQ( error, Table_Missing ) \
+ ? "missing" \
+ : "failed to load" )); \
+ FT_TRACE3(( "\n" )); \
+ } while ( 0 )
+
+#define LOADM_( x, vertical ) \
+ do \
+ { \
+ FT_TRACE2(( "`%s" #x "' ", \
+ vertical ? "vertical " : "" )); \
+ FT_TRACE3(( "-->\n" )); \
+ \
+ error = sfnt->load_ ## x( face, stream, vertical ); \
+ \
+ FT_TRACE2(( "%s\n", ( !error ) \
+ ? "loaded" \
+ : FT_ERR_EQ( error, Table_Missing ) \
+ ? "missing" \
+ : "failed to load" )); \
+ FT_TRACE3(( "\n" )); \
+ } while ( 0 )
+
+#define GET_NAME( id, field ) \
+ do \
+ { \
+ error = tt_face_get_name( face, TT_NAME_ID_ ## id, field ); \
+ if ( error ) \
+ goto Exit; \
+ } while ( 0 )
+
+
+ FT_LOCAL_DEF( FT_Error )
+ sfnt_load_face( FT_Stream stream,
+ TT_Face face,
+ FT_Int face_instance_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error;
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ FT_Error psnames_error;
+#endif
+
+ FT_Bool has_outline;
+ FT_Bool is_apple_sbit;
+
+ FT_Bool has_CBLC;
+ FT_Bool has_CBDT;
+ FT_Bool has_EBLC;
+ FT_Bool has_bloc;
+ FT_Bool has_sbix;
+
+ FT_Bool ignore_typographic_family = FALSE;
+ FT_Bool ignore_typographic_subfamily = FALSE;
+ FT_Bool ignore_sbix = FALSE;
+
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+ FT_UNUSED( face_instance_index );
+
+
+ /* Check parameters */
+
+ {
+ FT_Int i;
+
+
+ for ( i = 0; i < num_params; i++ )
+ {
+ if ( params[i].tag == FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_FAMILY )
+ ignore_typographic_family = TRUE;
+ else if ( params[i].tag == FT_PARAM_TAG_IGNORE_TYPOGRAPHIC_SUBFAMILY )
+ ignore_typographic_subfamily = TRUE;
+ else if ( params[i].tag == FT_PARAM_TAG_IGNORE_SBIX )
+ ignore_sbix = TRUE;
+ }
+ }
+
+ /* Load tables */
+
+ /* We now support two SFNT-based bitmapped font formats. They */
+ /* are recognized easily as they do not include a `glyf' */
+ /* table. */
+ /* */
+ /* The first format comes from Apple, and uses a table named */
+ /* `bhed' instead of `head' to store the font header (using */
+ /* the same format). It also doesn't include horizontal and */
+ /* vertical metrics tables (i.e. `hhea' and `vhea' tables are */
+ /* missing). */
+ /* */
+ /* The other format comes from Microsoft, and is used with */
+ /* WinCE/PocketPC. It looks like a standard TTF, except that */
+ /* it doesn't contain outlines. */
+ /* */
+
+ FT_TRACE2(( "sfnt_load_face: %p\n", (void *)face ));
+ FT_TRACE2(( "\n" ));
+
+ /* do we have outlines in there? */
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ has_outline = FT_BOOL( face->root.internal->incremental_interface ||
+ tt_face_lookup_table( face, TTAG_glyf ) ||
+ tt_face_lookup_table( face, TTAG_CFF ) ||
+ tt_face_lookup_table( face, TTAG_CFF2 ) );
+#else
+ has_outline = FT_BOOL( tt_face_lookup_table( face, TTAG_glyf ) ||
+ tt_face_lookup_table( face, TTAG_CFF ) ||
+ tt_face_lookup_table( face, TTAG_CFF2 ) );
+#endif
+
+ /* check which sbit formats are present */
+ has_CBLC = !face->goto_table( face, TTAG_CBLC, stream, 0 );
+ has_CBDT = !face->goto_table( face, TTAG_CBDT, stream, 0 );
+ has_EBLC = !face->goto_table( face, TTAG_EBLC, stream, 0 );
+ has_bloc = !face->goto_table( face, TTAG_bloc, stream, 0 );
+ has_sbix = !face->goto_table( face, TTAG_sbix, stream, 0 );
+
+ is_apple_sbit = FALSE;
+
+ if ( ignore_sbix )
+ has_sbix = FALSE;
+
+ /* if this font doesn't contain outlines, we try to load */
+ /* a `bhed' table */
+ if ( !has_outline && sfnt->load_bhed )
+ {
+ LOAD_( bhed );
+ is_apple_sbit = FT_BOOL( !error );
+ }
+
+ /* load the font header (`head' table) if this isn't an Apple */
+ /* sbit font file */
+ if ( !is_apple_sbit || has_sbix )
+ {
+ LOAD_( head );
+ if ( error )
+ goto Exit;
+ }
+
+ /* Ignore outlines for CBLC/CBDT fonts. */
+ if ( has_CBLC || has_CBDT )
+ has_outline = FALSE;
+
+ /* OpenType 1.8.2 introduced limits to this value; */
+ /* however, they make sense for older SFNT fonts also */
+ if ( face->header.Units_Per_EM < 16 ||
+ face->header.Units_Per_EM > 16384 )
+ {
+ error = FT_THROW( Invalid_Table );
+
+ goto Exit;
+ }
+
+ /* the following tables are often not present in embedded TrueType */
+ /* fonts within PDF documents, so don't check for them. */
+ LOAD_( maxp );
+ LOAD_( cmap );
+
+ /* the following tables are optional in PCL fonts -- */
+ /* don't check for errors */
+ LOAD_( name );
+ LOAD_( post );
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ psnames_error = error;
+#endif
+
+ /* do not load the metrics headers and tables if this is an Apple */
+ /* sbit font file */
+ if ( !is_apple_sbit )
+ {
+ /* load the `hhea' and `hmtx' tables */
+ LOADM_( hhea, 0 );
+ if ( !error )
+ {
+ LOADM_( hmtx, 0 );
+ if ( FT_ERR_EQ( error, Table_Missing ) )
+ {
+ error = FT_THROW( Hmtx_Table_Missing );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* If this is an incrementally loaded font and there are */
+ /* overriding metrics, tolerate a missing `hmtx' table. */
+ if ( face->root.internal->incremental_interface &&
+ face->root.internal->incremental_interface->funcs->
+ get_glyph_metrics )
+ {
+ face->horizontal.number_Of_HMetrics = 0;
+ error = FT_Err_Ok;
+ }
+#endif
+ }
+ }
+ else if ( FT_ERR_EQ( error, Table_Missing ) )
+ {
+ /* No `hhea' table necessary for SFNT Mac fonts. */
+ if ( face->format_tag == TTAG_true )
+ {
+ FT_TRACE2(( "This is an SFNT Mac font.\n" ));
+
+ has_outline = 0;
+ error = FT_Err_Ok;
+ }
+ else
+ {
+ error = FT_THROW( Horiz_Header_Missing );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* If this is an incrementally loaded font and there are */
+ /* overriding metrics, tolerate a missing `hhea' table. */
+ if ( face->root.internal->incremental_interface &&
+ face->root.internal->incremental_interface->funcs->
+ get_glyph_metrics )
+ {
+ face->horizontal.number_Of_HMetrics = 0;
+ error = FT_Err_Ok;
+ }
+#endif
+
+ }
+ }
+
+ if ( error )
+ goto Exit;
+
+ /* try to load the `vhea' and `vmtx' tables */
+ LOADM_( hhea, 1 );
+ if ( !error )
+ {
+ LOADM_( hmtx, 1 );
+ if ( !error )
+ face->vertical_info = 1;
+ }
+
+ if ( error && FT_ERR_NEQ( error, Table_Missing ) )
+ goto Exit;
+
+ LOAD_( os2 );
+ if ( error )
+ {
+ /* we treat the table as missing if there are any errors */
+ face->os2.version = 0xFFFFU;
+ }
+ }
+
+ /* the optional tables */
+
+ /* embedded bitmap support */
+ /* TODO: Replace this clumsy check for all possible sbit tables */
+ /* with something better (for example, by passing a parameter */
+ /* to suppress 'sbix' loading). */
+ if ( sfnt->load_eblc &&
+ ( has_CBLC || has_EBLC || has_bloc || has_sbix ) )
+ LOAD_( eblc );
+
+ /* colored glyph support */
+ if ( sfnt->load_cpal )
+ {
+ LOAD_( cpal );
+ LOAD_( colr );
+ }
+
+ /* OpenType-SVG glyph support */
+ if ( sfnt->load_svg )
+ LOAD_( svg );
+
+ /* consider the pclt, kerning, and gasp tables as optional */
+ LOAD_( pclt );
+ LOAD_( gasp );
+ LOAD_( kern );
+
+ face->root.num_glyphs = face->max_profile.numGlyphs;
+
+ /* Bit 8 of the `fsSelection' field in the `OS/2' table denotes */
+ /* a WWS-only font face. `WWS' stands for `weight', width', and */
+ /* `slope', a term used by Microsoft's Windows Presentation */
+ /* Foundation (WPF). This flag has been introduced in version */
+ /* 1.5 of the OpenType specification (May 2008). */
+
+ face->root.family_name = NULL;
+ face->root.style_name = NULL;
+ if ( face->os2.version != 0xFFFFU && face->os2.fsSelection & 256 )
+ {
+ if ( !ignore_typographic_family )
+ GET_NAME( TYPOGRAPHIC_FAMILY, &face->root.family_name );
+ if ( !face->root.family_name )
+ GET_NAME( FONT_FAMILY, &face->root.family_name );
+
+ if ( !ignore_typographic_subfamily )
+ GET_NAME( TYPOGRAPHIC_SUBFAMILY, &face->root.style_name );
+ if ( !face->root.style_name )
+ GET_NAME( FONT_SUBFAMILY, &face->root.style_name );
+ }
+ else
+ {
+ GET_NAME( WWS_FAMILY, &face->root.family_name );
+ if ( !face->root.family_name && !ignore_typographic_family )
+ GET_NAME( TYPOGRAPHIC_FAMILY, &face->root.family_name );
+ if ( !face->root.family_name )
+ GET_NAME( FONT_FAMILY, &face->root.family_name );
+
+ GET_NAME( WWS_SUBFAMILY, &face->root.style_name );
+ if ( !face->root.style_name && !ignore_typographic_subfamily )
+ GET_NAME( TYPOGRAPHIC_SUBFAMILY, &face->root.style_name );
+ if ( !face->root.style_name )
+ GET_NAME( FONT_SUBFAMILY, &face->root.style_name );
+ }
+
+ /* now set up root fields */
+ {
+ FT_Face root = &face->root;
+ FT_Long flags = root->face_flags;
+
+
+ /**********************************************************************
+ *
+ * Compute face flags.
+ */
+ if ( face->sbit_table_type == TT_SBIT_TABLE_TYPE_CBLC ||
+ face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX ||
+ face->colr ||
+ face->svg )
+ flags |= FT_FACE_FLAG_COLOR; /* color glyphs */
+
+ if ( has_outline == TRUE )
+ {
+ /* by default (and for backward compatibility) we handle */
+ /* fonts with an 'sbix' table as bitmap-only */
+ if ( has_sbix )
+ flags |= FT_FACE_FLAG_SBIX; /* with 'sbix' bitmaps */
+ else
+ flags |= FT_FACE_FLAG_SCALABLE; /* scalable outlines */
+ }
+
+ /* The sfnt driver only supports bitmap fonts natively, thus we */
+ /* don't set FT_FACE_FLAG_HINTER. */
+ flags |= FT_FACE_FLAG_SFNT | /* SFNT file format */
+ FT_FACE_FLAG_HORIZONTAL; /* horizontal data */
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ if ( !psnames_error &&
+ face->postscript.FormatType != 0x00030000L )
+ flags |= FT_FACE_FLAG_GLYPH_NAMES;
+#endif
+
+ /* fixed width font? */
+ if ( face->postscript.isFixedPitch )
+ flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ /* vertical information? */
+ if ( face->vertical_info )
+ flags |= FT_FACE_FLAG_VERTICAL;
+
+ /* kerning available ? */
+ if ( TT_FACE_HAS_KERNING( face ) )
+ flags |= FT_FACE_FLAG_KERNING;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ /* Don't bother to load the tables unless somebody asks for them. */
+ /* No need to do work which will (probably) not be used. */
+ if ( face->variation_support & TT_FACE_FLAG_VAR_FVAR )
+ flags |= FT_FACE_FLAG_MULTIPLE_MASTERS;
+#endif
+
+ root->face_flags = flags;
+
+ /**********************************************************************
+ *
+ * Compute style flags.
+ */
+
+ flags = 0;
+ if ( has_outline == TRUE && face->os2.version != 0xFFFFU )
+ {
+ /* We have an OS/2 table; use the `fsSelection' field. Bit 9 */
+ /* indicates an oblique font face. This flag has been */
+ /* introduced in version 1.5 of the OpenType specification. */
+
+ if ( face->os2.fsSelection & 512 ) /* bit 9 */
+ flags |= FT_STYLE_FLAG_ITALIC;
+ else if ( face->os2.fsSelection & 1 ) /* bit 0 */
+ flags |= FT_STYLE_FLAG_ITALIC;
+
+ if ( face->os2.fsSelection & 32 ) /* bit 5 */
+ flags |= FT_STYLE_FLAG_BOLD;
+ }
+ else
+ {
+ /* this is an old Mac font, use the header field */
+
+ if ( face->header.Mac_Style & 1 )
+ flags |= FT_STYLE_FLAG_BOLD;
+
+ if ( face->header.Mac_Style & 2 )
+ flags |= FT_STYLE_FLAG_ITALIC;
+ }
+
+ root->style_flags |= flags;
+
+ /**********************************************************************
+ *
+ * Polish the charmaps.
+ *
+ * Try to set the charmap encoding according to the platform &
+ * encoding ID of each charmap. Emulate Unicode charmap if one
+ * is missing.
+ */
+
+ tt_face_build_cmaps( face ); /* ignore errors */
+
+
+ /* set the encoding fields */
+ {
+ FT_Int m;
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ FT_Bool has_unicode = FALSE;
+#endif
+
+
+ for ( m = 0; m < root->num_charmaps; m++ )
+ {
+ FT_CharMap charmap = root->charmaps[m];
+
+
+ charmap->encoding = sfnt_find_encoding( charmap->platform_id,
+ charmap->encoding_id );
+
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+ if ( charmap->encoding == FT_ENCODING_UNICODE ||
+ charmap->encoding == FT_ENCODING_MS_SYMBOL ) /* PUA */
+ has_unicode = TRUE;
+ }
+
+ /* synthesize Unicode charmap if one is missing */
+ if ( !has_unicode &&
+ root->face_flags & FT_FACE_FLAG_GLYPH_NAMES )
+ {
+ FT_CharMapRec cmaprec;
+
+
+ cmaprec.face = root;
+ cmaprec.platform_id = TT_PLATFORM_MICROSOFT;
+ cmaprec.encoding_id = TT_MS_ID_UNICODE_CS;
+ cmaprec.encoding = FT_ENCODING_UNICODE;
+
+
+ error = FT_CMap_New( (FT_CMap_Class)&tt_cmap_unicode_class_rec,
+ NULL, &cmaprec, NULL );
+ if ( error &&
+ FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) &&
+ FT_ERR_NEQ( error, Unimplemented_Feature ) )
+ goto Exit;
+ error = FT_Err_Ok;
+
+#endif /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+ }
+ }
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ /*
+ * Now allocate the root array of FT_Bitmap_Size records and
+ * populate them. Unfortunately, it isn't possible to indicate bit
+ * depths in the FT_Bitmap_Size record. This is a design error.
+ */
+ {
+ FT_UInt count;
+
+
+ count = face->sbit_num_strikes;
+
+ if ( count > 0 )
+ {
+ FT_Memory memory = face->root.stream->memory;
+ FT_UShort em_size = face->header.Units_Per_EM;
+ FT_Short avgwidth = face->os2.xAvgCharWidth;
+ FT_Size_Metrics metrics;
+
+ FT_UInt* sbit_strike_map = NULL;
+ FT_UInt strike_idx, bsize_idx;
+
+
+ if ( em_size == 0 || face->os2.version == 0xFFFFU )
+ {
+ avgwidth = 1;
+ em_size = 1;
+ }
+
+ /* to avoid invalid strike data in the `available_sizes' field */
+ /* of `FT_Face', we map `available_sizes' indices to strike */
+ /* indices */
+ if ( FT_NEW_ARRAY( root->available_sizes, count ) ||
+ FT_QNEW_ARRAY( sbit_strike_map, count ) )
+ goto Exit;
+
+ bsize_idx = 0;
+ for ( strike_idx = 0; strike_idx < count; strike_idx++ )
+ {
+ FT_Bitmap_Size* bsize = root->available_sizes + bsize_idx;
+
+
+ error = sfnt->load_strike_metrics( face, strike_idx, &metrics );
+ if ( error )
+ continue;
+
+ bsize->height = (FT_Short)( metrics.height >> 6 );
+ bsize->width = (FT_Short)(
+ ( avgwidth * metrics.x_ppem + em_size / 2 ) / em_size );
+
+ bsize->x_ppem = metrics.x_ppem << 6;
+ bsize->y_ppem = metrics.y_ppem << 6;
+
+ /* assume 72dpi */
+ bsize->size = metrics.y_ppem << 6;
+
+ /* only use strikes with valid PPEM values */
+ if ( bsize->x_ppem && bsize->y_ppem )
+ sbit_strike_map[bsize_idx++] = strike_idx;
+ }
+
+ /* reduce array size to the actually used elements */
+ FT_MEM_QRENEW_ARRAY( sbit_strike_map, count, bsize_idx );
+
+ /* from now on, all strike indices are mapped */
+ /* using `sbit_strike_map' */
+ if ( bsize_idx )
+ {
+ face->sbit_strike_map = sbit_strike_map;
+
+ root->face_flags |= FT_FACE_FLAG_FIXED_SIZES;
+ root->num_fixed_sizes = (FT_Int)bsize_idx;
+ }
+ }
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+ /* a font with no bitmaps and no outlines is scalable; */
+ /* it has only empty glyphs then */
+ if ( !FT_HAS_FIXED_SIZES( root ) && !FT_IS_SCALABLE( root ) )
+ root->face_flags |= FT_FACE_FLAG_SCALABLE;
+
+
+ /**********************************************************************
+ *
+ * Set up metrics.
+ */
+ if ( FT_IS_SCALABLE( root ) ||
+ FT_HAS_SBIX( root ) )
+ {
+ /* XXX What about if outline header is missing */
+ /* (e.g. sfnt wrapped bitmap)? */
+ root->bbox.xMin = face->header.xMin;
+ root->bbox.yMin = face->header.yMin;
+ root->bbox.xMax = face->header.xMax;
+ root->bbox.yMax = face->header.yMax;
+ root->units_per_EM = face->header.Units_Per_EM;
+
+
+ /*
+ * Computing the ascender/descender/height is tricky.
+ *
+ * The OpenType specification v1.8.3 says:
+ *
+ * [OS/2's] sTypoAscender, sTypoDescender and sTypoLineGap fields
+ * are intended to allow applications to lay out documents in a
+ * typographically-correct and portable fashion.
+ *
+ * This is somewhat at odds with the decades of backwards
+ * compatibility, operating systems and applications doing whatever
+ * they want, not to mention broken fonts.
+ *
+ * Not all fonts have an OS/2 table; in this case, we take the values
+ * in the horizontal header, although there is nothing stopping the
+ * values from being unreliable. Even with a OS/2 table, certain fonts
+ * set the sTypoAscender, sTypoDescender and sTypoLineGap fields to 0
+ * and instead correctly set usWinAscent and usWinDescent.
+ *
+ * As an example, Arial Narrow is shipped as four files ARIALN.TTF,
+ * ARIALNI.TTF, ARIALNB.TTF and ARIALNBI.TTF. Strangely, all fonts have
+ * the same values in their sTypo* fields, except ARIALNB.ttf which
+ * sets them to 0. All of them have different usWinAscent/Descent
+ * values. The OS/2 table therefore cannot be trusted for computing the
+ * text height reliably.
+ *
+ * As a compromise, do the following:
+ *
+ * 1. If the OS/2 table exists and the fsSelection bit 7 is set
+ * (USE_TYPO_METRICS), trust the font and use the sTypo* metrics.
+ * 2. Otherwise, use the `hhea' table's metrics.
+ * 3. If they are zero and the OS/2 table exists,
+ * 1. use the OS/2 table's sTypo* metrics if they are non-zero.
+ * 2. Otherwise, use the OS/2 table's usWin* metrics.
+ */
+
+ if ( face->os2.version != 0xFFFFU && face->os2.fsSelection & 128 )
+ {
+ root->ascender = face->os2.sTypoAscender;
+ root->descender = face->os2.sTypoDescender;
+ root->height = root->ascender - root->descender +
+ face->os2.sTypoLineGap;
+ }
+ else
+ {
+ root->ascender = face->horizontal.Ascender;
+ root->descender = face->horizontal.Descender;
+ root->height = root->ascender - root->descender +
+ face->horizontal.Line_Gap;
+
+ if ( !( root->ascender || root->descender ) )
+ {
+ if ( face->os2.version != 0xFFFFU )
+ {
+ if ( face->os2.sTypoAscender || face->os2.sTypoDescender )
+ {
+ root->ascender = face->os2.sTypoAscender;
+ root->descender = face->os2.sTypoDescender;
+ root->height = root->ascender - root->descender +
+ face->os2.sTypoLineGap;
+ }
+ else
+ {
+ root->ascender = (FT_Short)face->os2.usWinAscent;
+ root->descender = -(FT_Short)face->os2.usWinDescent;
+ root->height = root->ascender - root->descender;
+ }
+ }
+ }
+ }
+
+ root->max_advance_width =
+ (FT_Short)face->horizontal.advance_Width_Max;
+ root->max_advance_height =
+ (FT_Short)( face->vertical_info ? face->vertical.advance_Height_Max
+ : root->height );
+
+ /* See https://www.microsoft.com/typography/otspec/post.htm -- */
+ /* Adjust underline position from top edge to centre of */
+ /* stroke to convert TrueType meaning to FreeType meaning. */
+ root->underline_position = face->postscript.underlinePosition -
+ face->postscript.underlineThickness / 2;
+ root->underline_thickness = face->postscript.underlineThickness;
+ }
+
+ }
+
+ Exit:
+ FT_TRACE2(( "sfnt_load_face: done\n" ));
+
+ return error;
+ }
+
+
+#undef LOAD_
+#undef LOADM_
+#undef GET_NAME
+
+
+ FT_LOCAL_DEF( void )
+ sfnt_done_face( TT_Face face )
+ {
+ FT_Memory memory;
+ SFNT_Service sfnt;
+
+
+ if ( !face )
+ return;
+
+ memory = face->root.memory;
+ sfnt = (SFNT_Service)face->sfnt;
+
+ if ( sfnt )
+ {
+ /* destroy the postscript names table if it is loaded */
+ if ( sfnt->free_psnames )
+ sfnt->free_psnames( face );
+
+ /* destroy the embedded bitmaps table if it is loaded */
+ if ( sfnt->free_eblc )
+ sfnt->free_eblc( face );
+
+ /* destroy color table data if it is loaded */
+ if ( sfnt->free_cpal )
+ {
+ sfnt->free_cpal( face );
+ sfnt->free_colr( face );
+ }
+
+#ifdef FT_CONFIG_OPTION_SVG
+ /* free SVG data */
+ if ( sfnt->free_svg )
+ sfnt->free_svg( face );
+#endif
+ }
+
+#ifdef TT_CONFIG_OPTION_BDF
+ /* freeing the embedded BDF properties */
+ tt_face_free_bdf_props( face );
+#endif
+
+ /* freeing the kerning table */
+ tt_face_done_kern( face );
+
+ /* freeing the collection table */
+ FT_FREE( face->ttc_header.offsets );
+ face->ttc_header.count = 0;
+
+ /* freeing table directory */
+ FT_FREE( face->dir_tables );
+ face->num_tables = 0;
+
+ {
+ FT_Stream stream = FT_FACE_STREAM( face );
+
+
+ /* simply release the 'cmap' table frame */
+ FT_FRAME_RELEASE( face->cmap_table );
+ face->cmap_size = 0;
+ }
+
+ face->horz_metrics_size = 0;
+ face->vert_metrics_size = 0;
+
+ /* freeing vertical metrics, if any */
+ if ( face->vertical_info )
+ {
+ FT_FREE( face->vertical.long_metrics );
+ FT_FREE( face->vertical.short_metrics );
+ face->vertical_info = 0;
+ }
+
+ /* freeing the gasp table */
+ FT_FREE( face->gasp.gaspRanges );
+ face->gasp.numRanges = 0;
+
+ /* freeing the name table */
+ if ( sfnt )
+ sfnt->free_name( face );
+
+ /* freeing family and style name */
+ FT_FREE( face->root.family_name );
+ FT_FREE( face->root.style_name );
+
+ /* freeing sbit size table */
+ FT_FREE( face->root.available_sizes );
+ FT_FREE( face->sbit_strike_map );
+ face->root.num_fixed_sizes = 0;
+
+ FT_FREE( face->postscript_name );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ FT_FREE( face->var_postscript_prefix );
+#endif
+
+ /* freeing glyph color palette data */
+ FT_FREE( face->palette_data.palette_name_ids );
+ FT_FREE( face->palette_data.palette_flags );
+ FT_FREE( face->palette_data.palette_entry_name_ids );
+ FT_FREE( face->palette );
+
+ face->sfnt = NULL;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sfobjs.h b/modules/freetype2/src/sfnt/sfobjs.h
new file mode 100644
index 0000000000..906aebbf90
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfobjs.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+ *
+ * sfobjs.h
+ *
+ * SFNT object management (specification).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#ifndef SFOBJS_H_
+#define SFOBJS_H_
+
+
+#include <freetype/internal/sfnt.h>
+#include <freetype/internal/ftobjs.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ sfnt_init_face( FT_Stream stream,
+ TT_Face face,
+ FT_Int face_instance_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( FT_Error )
+ sfnt_load_face( FT_Stream stream,
+ TT_Face face,
+ FT_Int face_instance_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( void )
+ sfnt_done_face( TT_Face face );
+
+ FT_LOCAL( FT_Error )
+ tt_face_get_name( TT_Face face,
+ FT_UShort nameid,
+ FT_String** name );
+
+
+FT_END_HEADER
+
+#endif /* SFOBJS_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sfwoff.c b/modules/freetype2/src/sfnt/sfwoff.c
new file mode 100644
index 0000000000..9559bf3421
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfwoff.c
@@ -0,0 +1,434 @@
+/****************************************************************************
+ *
+ * sfwoff.c
+ *
+ * WOFF format management (base).
+ *
+ * Copyright (C) 1996-2023 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 "sfwoff.h"
+#include <freetype/tttags.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/ftgzip.h>
+
+
+#ifdef FT_CONFIG_OPTION_USE_ZLIB
+
+
+ /**************************************************************************
+ *
+ * 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 sfwoff
+
+
+#define WRITE_USHORT( p, v ) \
+ do \
+ { \
+ *(p)++ = (FT_Byte)( (v) >> 8 ); \
+ *(p)++ = (FT_Byte)( (v) >> 0 ); \
+ \
+ } while ( 0 )
+
+#define WRITE_ULONG( p, v ) \
+ do \
+ { \
+ *(p)++ = (FT_Byte)( (v) >> 24 ); \
+ *(p)++ = (FT_Byte)( (v) >> 16 ); \
+ *(p)++ = (FT_Byte)( (v) >> 8 ); \
+ *(p)++ = (FT_Byte)( (v) >> 0 ); \
+ \
+ } while ( 0 )
+
+
+ static void
+ sfnt_stream_close( FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+
+
+ FT_FREE( stream->base );
+
+ stream->size = 0;
+ stream->close = NULL;
+ }
+
+
+ FT_COMPARE_DEF( int )
+ compare_offsets( const void* a,
+ const void* b )
+ {
+ WOFF_Table table1 = *(WOFF_Table*)a;
+ WOFF_Table table2 = *(WOFF_Table*)b;
+
+ FT_ULong offset1 = table1->Offset;
+ FT_ULong offset2 = table2->Offset;
+
+
+ if ( offset1 > offset2 )
+ return 1;
+ else if ( offset1 < offset2 )
+ return -1;
+ else
+ return 0;
+ }
+
+
+ /* Replace `face->root.stream' with a stream containing the extracted */
+ /* SFNT of a WOFF font. */
+
+ FT_LOCAL_DEF( FT_Error )
+ woff_open_font( FT_Stream stream,
+ TT_Face face )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error = FT_Err_Ok;
+
+ WOFF_HeaderRec woff;
+ WOFF_Table tables = NULL;
+ WOFF_Table* indices = NULL;
+
+ FT_ULong woff_offset;
+
+ FT_Byte* sfnt = NULL;
+ FT_Stream sfnt_stream = NULL;
+
+ FT_Byte* sfnt_header;
+ FT_ULong sfnt_offset;
+
+ FT_Int nn;
+ FT_Tag old_tag = 0;
+
+ static const FT_Frame_Field woff_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WOFF_HeaderRec
+
+ FT_FRAME_START( 44 ),
+ FT_FRAME_ULONG ( signature ),
+ FT_FRAME_ULONG ( flavor ),
+ FT_FRAME_ULONG ( length ),
+ FT_FRAME_USHORT( num_tables ),
+ FT_FRAME_USHORT( reserved ),
+ FT_FRAME_ULONG ( totalSfntSize ),
+ FT_FRAME_USHORT( majorVersion ),
+ FT_FRAME_USHORT( minorVersion ),
+ FT_FRAME_ULONG ( metaOffset ),
+ FT_FRAME_ULONG ( metaLength ),
+ FT_FRAME_ULONG ( metaOrigLength ),
+ FT_FRAME_ULONG ( privOffset ),
+ FT_FRAME_ULONG ( privLength ),
+ FT_FRAME_END
+ };
+
+
+ FT_ASSERT( stream == face->root.stream );
+ FT_ASSERT( FT_STREAM_POS() == 0 );
+
+ if ( FT_STREAM_READ_FIELDS( woff_header_fields, &woff ) )
+ return error;
+
+ /* Make sure we don't recurse back here or hit TTC code. */
+ if ( woff.flavor == TTAG_wOFF || woff.flavor == TTAG_ttcf )
+ return FT_THROW( Invalid_Table );
+
+ /* Miscellaneous checks. */
+ if ( woff.length != stream->size ||
+ woff.num_tables == 0 ||
+ 44 + woff.num_tables * 20UL >= woff.length ||
+ 12 + woff.num_tables * 16UL >= woff.totalSfntSize ||
+ ( woff.totalSfntSize & 3 ) != 0 ||
+ ( woff.metaOffset == 0 && ( woff.metaLength != 0 ||
+ woff.metaOrigLength != 0 ) ) ||
+ ( woff.metaLength != 0 && woff.metaOrigLength == 0 ) ||
+ ( woff.privOffset == 0 && woff.privLength != 0 ) )
+ {
+ FT_ERROR(( "woff_font_open: invalid WOFF header\n" ));
+ return FT_THROW( Invalid_Table );
+ }
+
+ /* Don't trust `totalSfntSize' before thorough checks. */
+ if ( FT_QALLOC( sfnt, 12 ) || FT_NEW( sfnt_stream ) )
+ goto Exit;
+
+ sfnt_header = sfnt;
+
+ /* Write sfnt header. */
+ {
+ FT_UInt searchRange, entrySelector, rangeShift, x;
+
+
+ x = woff.num_tables;
+ entrySelector = 0;
+ while ( x )
+ {
+ x >>= 1;
+ entrySelector += 1;
+ }
+ entrySelector--;
+
+ searchRange = ( 1 << entrySelector ) * 16;
+ rangeShift = woff.num_tables * 16 - searchRange;
+
+ WRITE_ULONG ( sfnt_header, woff.flavor );
+ WRITE_USHORT( sfnt_header, woff.num_tables );
+ WRITE_USHORT( sfnt_header, searchRange );
+ WRITE_USHORT( sfnt_header, entrySelector );
+ WRITE_USHORT( sfnt_header, rangeShift );
+ }
+
+ /* While the entries in the sfnt header must be sorted by the */
+ /* tag value, the tables themselves are not. We thus have to */
+ /* sort them by offset and check that they don't overlap. */
+
+ if ( FT_QNEW_ARRAY( tables, woff.num_tables ) ||
+ FT_QNEW_ARRAY( indices, woff.num_tables ) )
+ goto Exit;
+
+ FT_TRACE2(( "\n" ));
+ FT_TRACE2(( " tag offset compLen origLen checksum\n" ));
+ FT_TRACE2(( " -------------------------------------------\n" ));
+
+ if ( FT_FRAME_ENTER( 20L * woff.num_tables ) )
+ goto Exit;
+
+ for ( nn = 0; nn < woff.num_tables; nn++ )
+ {
+ WOFF_Table table = tables + nn;
+
+ table->Tag = FT_GET_TAG4();
+ table->Offset = FT_GET_ULONG();
+ table->CompLength = FT_GET_ULONG();
+ table->OrigLength = FT_GET_ULONG();
+ table->CheckSum = FT_GET_ULONG();
+
+ FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx %08lx\n",
+ (FT_Char)( table->Tag >> 24 ),
+ (FT_Char)( table->Tag >> 16 ),
+ (FT_Char)( table->Tag >> 8 ),
+ (FT_Char)( table->Tag ),
+ table->Offset,
+ table->CompLength,
+ table->OrigLength,
+ table->CheckSum ));
+
+ if ( table->Tag <= old_tag )
+ {
+ FT_FRAME_EXIT();
+
+ FT_ERROR(( "woff_font_open: table tags are not sorted\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ old_tag = table->Tag;
+ indices[nn] = table;
+ }
+
+ FT_FRAME_EXIT();
+
+ /* Sort by offset. */
+
+ ft_qsort( indices,
+ woff.num_tables,
+ sizeof ( WOFF_Table ),
+ compare_offsets );
+
+ /* Check offsets and lengths. */
+
+ woff_offset = 44 + woff.num_tables * 20L;
+ sfnt_offset = 12 + woff.num_tables * 16L;
+
+ for ( nn = 0; nn < woff.num_tables; nn++ )
+ {
+ WOFF_Table table = indices[nn];
+
+
+ if ( table->Offset != woff_offset ||
+ table->CompLength > woff.length ||
+ table->Offset > woff.length - table->CompLength ||
+ table->OrigLength > woff.totalSfntSize ||
+ sfnt_offset > woff.totalSfntSize - table->OrigLength ||
+ table->CompLength > table->OrigLength )
+ {
+ FT_ERROR(( "woff_font_open: invalid table offsets\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ table->OrigOffset = sfnt_offset;
+
+ /* The offsets must be multiples of 4. */
+ woff_offset += ( table->CompLength + 3 ) & ~3U;
+ sfnt_offset += ( table->OrigLength + 3 ) & ~3U;
+ }
+
+ /*
+ * Final checks!
+ *
+ * We don't decode and check the metadata block.
+ * We don't check table checksums either.
+ * But other than those, I think we implement all
+ * `MUST' checks from the spec.
+ */
+
+ if ( woff.metaOffset )
+ {
+ if ( woff.metaOffset != woff_offset ||
+ woff.metaOffset + woff.metaLength > woff.length )
+ {
+ FT_ERROR(( "woff_font_open:"
+ " invalid `metadata' offset or length\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ /* We have padding only ... */
+ woff_offset += woff.metaLength;
+ }
+
+ if ( woff.privOffset )
+ {
+ /* ... if it isn't the last block. */
+ woff_offset = ( woff_offset + 3 ) & ~3U;
+
+ if ( woff.privOffset != woff_offset ||
+ woff.privOffset + woff.privLength > woff.length )
+ {
+ FT_ERROR(( "woff_font_open: invalid `private' offset or length\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ /* No padding for the last block. */
+ woff_offset += woff.privLength;
+ }
+
+ if ( sfnt_offset != woff.totalSfntSize ||
+ woff_offset != woff.length )
+ {
+ FT_ERROR(( "woff_font_open: invalid `sfnt' table structure\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ /* Now use `totalSfntSize'. */
+ if ( FT_QREALLOC( sfnt, 12, woff.totalSfntSize ) )
+ goto Exit;
+
+ sfnt_header = sfnt + 12;
+
+ /* Write the tables. */
+
+ for ( nn = 0; nn < woff.num_tables; nn++ )
+ {
+ WOFF_Table table = tables + nn;
+
+
+ /* Write SFNT table entry. */
+ WRITE_ULONG( sfnt_header, table->Tag );
+ WRITE_ULONG( sfnt_header, table->CheckSum );
+ WRITE_ULONG( sfnt_header, table->OrigOffset );
+ WRITE_ULONG( sfnt_header, table->OrigLength );
+
+ /* Write table data. */
+ if ( FT_STREAM_SEEK( table->Offset ) ||
+ FT_FRAME_ENTER( table->CompLength ) )
+ goto Exit;
+
+ if ( table->CompLength == table->OrigLength )
+ {
+ /* Uncompressed data; just copy. */
+ ft_memcpy( sfnt + table->OrigOffset,
+ stream->cursor,
+ table->OrigLength );
+ }
+ else
+ {
+ /* Uncompress with zlib. */
+ FT_ULong output_len = table->OrigLength;
+
+
+ error = FT_Gzip_Uncompress( memory,
+ sfnt + table->OrigOffset, &output_len,
+ stream->cursor, table->CompLength );
+ if ( error )
+ goto Exit1;
+ if ( output_len != table->OrigLength )
+ {
+ FT_ERROR(( "woff_font_open: compressed table length mismatch\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit1;
+ }
+ }
+
+ FT_FRAME_EXIT();
+
+ /* We don't check whether the padding bytes in the WOFF file are */
+ /* actually '\0'. For the output, however, we do set them properly. */
+ sfnt_offset = table->OrigOffset + table->OrigLength;
+ while ( sfnt_offset & 3 )
+ {
+ sfnt[sfnt_offset] = '\0';
+ sfnt_offset++;
+ }
+ }
+
+ /* Ok! Finally ready. Swap out stream and return. */
+ FT_Stream_OpenMemory( sfnt_stream, sfnt, woff.totalSfntSize );
+ sfnt_stream->memory = stream->memory;
+ sfnt_stream->close = sfnt_stream_close;
+
+ FT_Stream_Free(
+ face->root.stream,
+ ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 );
+
+ face->root.stream = sfnt_stream;
+
+ face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
+
+ Exit:
+ FT_FREE( tables );
+ FT_FREE( indices );
+
+ if ( error )
+ {
+ FT_FREE( sfnt );
+ FT_Stream_Close( sfnt_stream );
+ FT_FREE( sfnt_stream );
+ }
+
+ return error;
+
+ Exit1:
+ FT_FRAME_EXIT();
+ goto Exit;
+ }
+
+
+#undef WRITE_USHORT
+#undef WRITE_ULONG
+
+#else /* !FT_CONFIG_OPTION_USE_ZLIB */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _sfwoff_dummy;
+
+#endif /* !FT_CONFIG_OPTION_USE_ZLIB */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sfwoff.h b/modules/freetype2/src/sfnt/sfwoff.h
new file mode 100644
index 0000000000..d438422737
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfwoff.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+ *
+ * sfwoff.h
+ *
+ * WOFFF format management (specification).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#ifndef SFWOFF_H_
+#define SFWOFF_H_
+
+
+#include <freetype/internal/sfnt.h>
+#include <freetype/internal/ftobjs.h>
+
+
+FT_BEGIN_HEADER
+
+#ifdef FT_CONFIG_OPTION_USE_ZLIB
+
+ FT_LOCAL( FT_Error )
+ woff_open_font( FT_Stream stream,
+ TT_Face face );
+
+
+#endif
+
+FT_END_HEADER
+
+#endif /* SFWOFF_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sfwoff2.c b/modules/freetype2/src/sfnt/sfwoff2.c
new file mode 100644
index 0000000000..7a01977f86
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfwoff2.c
@@ -0,0 +1,2386 @@
+/****************************************************************************
+ *
+ * sfwoff2.c
+ *
+ * WOFF2 format management (base).
+ *
+ * Copyright (C) 2019-2023 by
+ * Nikhil Ramakrishnan, 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 "sfwoff2.h"
+#include "woff2tags.h"
+#include <freetype/tttags.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+
+
+#ifdef FT_CONFIG_OPTION_USE_BROTLI
+
+#include <brotli/decode.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 sfwoff2
+
+
+#define READ_255USHORT( var ) FT_SET_ERROR( Read255UShort( stream, &var ) )
+
+#define READ_BASE128( var ) FT_SET_ERROR( ReadBase128( stream, &var ) )
+
+ /* `var' should be FT_ULong */
+#define ROUND4( var ) ( ( var + 3 ) & ~3UL )
+
+#define WRITE_USHORT( p, v ) \
+ do \
+ { \
+ *(p)++ = (FT_Byte)( (v) >> 8 ); \
+ *(p)++ = (FT_Byte)( (v) >> 0 ); \
+ \
+ } while ( 0 )
+
+#define WRITE_ULONG( p, v ) \
+ do \
+ { \
+ *(p)++ = (FT_Byte)( (v) >> 24 ); \
+ *(p)++ = (FT_Byte)( (v) >> 16 ); \
+ *(p)++ = (FT_Byte)( (v) >> 8 ); \
+ *(p)++ = (FT_Byte)( (v) >> 0 ); \
+ \
+ } while ( 0 )
+
+#define WRITE_SHORT( p, v ) \
+ do \
+ { \
+ *(p)++ = (FT_Byte)( (v) >> 8 ); \
+ *(p)++ = (FT_Byte)( (v) >> 0 ); \
+ \
+ } while ( 0 )
+
+#define WRITE_SFNT_BUF( buf, s ) \
+ write_buf( &sfnt, sfnt_size, &dest_offset, buf, s, memory )
+
+#define WRITE_SFNT_BUF_AT( offset, buf, s ) \
+ write_buf( &sfnt, sfnt_size, &offset, buf, s, memory )
+
+#define N_CONTOUR_STREAM 0
+#define N_POINTS_STREAM 1
+#define FLAG_STREAM 2
+#define GLYPH_STREAM 3
+#define COMPOSITE_STREAM 4
+#define BBOX_STREAM 5
+#define INSTRUCTION_STREAM 6
+
+#define HAVE_OVERLAP_SIMPLE_BITMAP 0x1
+
+
+ static void
+ stream_close( FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+
+
+ FT_FREE( stream->base );
+
+ stream->size = 0;
+ stream->close = NULL;
+ }
+
+
+ FT_COMPARE_DEF( int )
+ compare_tags( const void* a,
+ const void* b )
+ {
+ WOFF2_Table table1 = *(WOFF2_Table*)a;
+ WOFF2_Table table2 = *(WOFF2_Table*)b;
+
+ FT_Tag tag1 = table1->Tag;
+ FT_Tag tag2 = table2->Tag;
+
+
+ if ( tag1 > tag2 )
+ return 1;
+ else if ( tag1 < tag2 )
+ return -1;
+ else
+ return 0;
+ }
+
+
+ static FT_Error
+ Read255UShort( FT_Stream stream,
+ FT_UShort* value )
+ {
+ const FT_Byte oneMoreByteCode1 = 255;
+ const FT_Byte oneMoreByteCode2 = 254;
+ const FT_Byte wordCode = 253;
+ const FT_UShort lowestUCode = 253;
+
+ FT_Error error = FT_Err_Ok;
+ FT_Byte code;
+ FT_Byte result_byte = 0;
+ FT_UShort result_short = 0;
+
+
+ if ( FT_READ_BYTE( code ) )
+ return error;
+ if ( code == wordCode )
+ {
+ /* Read next two bytes and store `FT_UShort' value. */
+ if ( FT_READ_USHORT( result_short ) )
+ return error;
+ *value = result_short;
+ return FT_Err_Ok;
+ }
+ else if ( code == oneMoreByteCode1 )
+ {
+ if ( FT_READ_BYTE( result_byte ) )
+ return error;
+ *value = result_byte + lowestUCode;
+ return FT_Err_Ok;
+ }
+ else if ( code == oneMoreByteCode2 )
+ {
+ if ( FT_READ_BYTE( result_byte ) )
+ return error;
+ *value = result_byte + lowestUCode * 2;
+ return FT_Err_Ok;
+ }
+ else
+ {
+ *value = code;
+ return FT_Err_Ok;
+ }
+ }
+
+
+ static FT_Error
+ ReadBase128( FT_Stream stream,
+ FT_ULong* value )
+ {
+ FT_ULong result = 0;
+ FT_Int i;
+ FT_Byte code;
+ FT_Error error = FT_Err_Ok;
+
+
+ for ( i = 0; i < 5; ++i )
+ {
+ code = 0;
+ if ( FT_READ_BYTE( code ) )
+ return error;
+
+ /* Leading zeros are invalid. */
+ if ( i == 0 && code == 0x80 )
+ return FT_THROW( Invalid_Table );
+
+ /* If any of top seven bits are set then we're about to overflow. */
+ if ( result & 0xfe000000 )
+ return FT_THROW( Invalid_Table );
+
+ result = ( result << 7 ) | ( code & 0x7f );
+
+ /* Spin until most significant bit of data byte is false. */
+ if ( ( code & 0x80 ) == 0 )
+ {
+ *value = result;
+ return FT_Err_Ok;
+ }
+ }
+
+ /* Make sure not to exceed the size bound. */
+ return FT_THROW( Invalid_Table );
+ }
+
+
+ /* Extend memory of `dst_bytes' buffer and copy data from `src'. */
+ static FT_Error
+ write_buf( FT_Byte** dst_bytes,
+ FT_ULong* dst_size,
+ FT_ULong* offset,
+ FT_Byte* src,
+ FT_ULong size,
+ FT_Memory memory )
+ {
+ FT_Error error = FT_Err_Ok;
+ /* We are reallocating memory for `dst', so its pointer may change. */
+ FT_Byte* dst = *dst_bytes;
+
+
+ /* Check whether we are within limits. */
+ if ( ( *offset + size ) > WOFF2_DEFAULT_MAX_SIZE )
+ return FT_THROW( Array_Too_Large );
+
+ /* Reallocate `dst'. */
+ if ( ( *offset + size ) > *dst_size )
+ {
+ FT_TRACE6(( "Reallocating %lu to %lu.\n",
+ *dst_size, (*offset + size) ));
+ if ( FT_QREALLOC( dst,
+ (FT_ULong)( *dst_size ),
+ (FT_ULong)( *offset + size ) ) )
+ goto Exit;
+
+ *dst_size = *offset + size;
+ }
+
+ /* Copy data. */
+ ft_memcpy( dst + *offset, src, size );
+
+ *offset += size;
+ /* Set pointer of `dst' to its correct value. */
+ *dst_bytes = dst;
+
+ Exit:
+ return error;
+ }
+
+
+ /* Pad buffer to closest multiple of 4. */
+ static FT_Error
+ pad4( FT_Byte** sfnt_bytes,
+ FT_ULong* sfnt_size,
+ FT_ULong* out_offset,
+ FT_Memory memory )
+ {
+ FT_Byte* sfnt = *sfnt_bytes;
+ FT_ULong dest_offset = *out_offset;
+
+ FT_Byte zeroes[] = { 0, 0, 0 };
+ FT_ULong pad_bytes;
+
+
+ if ( dest_offset + 3 < dest_offset )
+ return FT_THROW( Invalid_Table );
+
+ pad_bytes = ROUND4( dest_offset ) - dest_offset;
+ if ( pad_bytes > 0 )
+ {
+ if ( WRITE_SFNT_BUF( &zeroes[0], pad_bytes ) )
+ return FT_THROW( Invalid_Table );
+ }
+
+ *sfnt_bytes = sfnt;
+ *out_offset = dest_offset;
+ return FT_Err_Ok;
+ }
+
+
+ /* Calculate table checksum of `buf'. */
+ static FT_ULong
+ compute_ULong_sum( FT_Byte* buf,
+ FT_ULong size )
+ {
+ FT_ULong checksum = 0;
+ FT_ULong aligned_size = size & ~3UL;
+ FT_ULong i;
+ FT_ULong v;
+
+
+ for ( i = 0; i < aligned_size; i += 4 )
+ checksum += ( (FT_ULong)buf[i ] << 24 ) |
+ ( (FT_ULong)buf[i + 1] << 16 ) |
+ ( (FT_ULong)buf[i + 2] << 8 ) |
+ ( (FT_ULong)buf[i + 3] << 0 );
+
+ /* If size is not aligned to 4, treat as if it is padded with 0s. */
+ if ( size != aligned_size )
+ {
+ v = 0;
+ for ( i = aligned_size ; i < size; ++i )
+ v |= (FT_ULong)buf[i] << ( 24 - 8 * ( i & 3 ) );
+ checksum += v;
+ }
+
+ return checksum;
+ }
+
+
+ static FT_Error
+ woff2_decompress( FT_Byte* dst,
+ FT_ULong dst_size,
+ const FT_Byte* src,
+ FT_ULong src_size )
+ {
+ /* this cast is only of importance on 32bit systems; */
+ /* we don't validate it */
+ FT_Offset uncompressed_size = (FT_Offset)dst_size;
+ BrotliDecoderResult result;
+
+
+ result = BrotliDecoderDecompress( src_size,
+ src,
+ &uncompressed_size,
+ dst );
+
+ if ( result != BROTLI_DECODER_RESULT_SUCCESS ||
+ uncompressed_size != dst_size )
+ {
+ FT_ERROR(( "woff2_decompress: Stream length mismatch.\n" ));
+ return FT_THROW( Invalid_Table );
+ }
+
+ FT_TRACE2(( "woff2_decompress: Brotli stream decompressed.\n" ));
+ return FT_Err_Ok;
+ }
+
+
+ static WOFF2_Table
+ find_table( WOFF2_Table* tables,
+ FT_UShort num_tables,
+ FT_Tag tag )
+ {
+ FT_Int i;
+
+
+ for ( i = 0; i < num_tables; i++ )
+ {
+ if ( tables[i]->Tag == tag )
+ return tables[i];
+ }
+ return NULL;
+ }
+
+
+ /* Read `numberOfHMetrics' field from `hhea' table. */
+ static FT_Error
+ read_num_hmetrics( FT_Stream stream,
+ FT_UShort* num_hmetrics )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_UShort num_metrics;
+
+
+ if ( FT_STREAM_SKIP( 34 ) )
+ return FT_THROW( Invalid_Table );
+
+ if ( FT_READ_USHORT( num_metrics ) )
+ return FT_THROW( Invalid_Table );
+
+ *num_hmetrics = num_metrics;
+
+ return error;
+ }
+
+
+ /* An auxiliary function for overflow-safe addition. */
+ static FT_Int
+ with_sign( FT_Byte flag,
+ FT_Int base_val )
+ {
+ /* Precondition: 0 <= base_val < 65536 (to avoid overflow). */
+ return ( flag & 1 ) ? base_val : -base_val;
+ }
+
+
+ /* An auxiliary function for overflow-safe addition. */
+ static FT_Int
+ safe_int_addition( FT_Int a,
+ FT_Int b,
+ FT_Int* result )
+ {
+ if ( ( ( a > 0 ) && ( b > FT_INT_MAX - a ) ) ||
+ ( ( a < 0 ) && ( b < FT_INT_MIN - a ) ) )
+ return FT_THROW( Invalid_Table );
+
+ *result = a + b;
+ return FT_Err_Ok;
+ }
+
+
+ /*
+ * Decode variable-length (flag, xCoordinate, yCoordinate) triplet for a
+ * simple glyph. See
+ *
+ * https://www.w3.org/TR/WOFF2/#triplet_decoding
+ */
+ static FT_Error
+ triplet_decode( const FT_Byte* flags_in,
+ const FT_Byte* in,
+ FT_ULong in_size,
+ FT_ULong n_points,
+ WOFF2_Point result,
+ FT_ULong* in_bytes_used )
+ {
+ FT_Int x = 0;
+ FT_Int y = 0;
+ FT_Int dx;
+ FT_Int dy;
+ FT_Int b0, b1, b2;
+
+ FT_ULong triplet_index = 0;
+ FT_ULong data_bytes;
+
+ FT_UInt i;
+
+
+ if ( n_points > in_size )
+ return FT_THROW( Invalid_Table );
+
+ for ( i = 0; i < n_points; ++i )
+ {
+ FT_Byte flag = flags_in[i];
+ FT_Bool on_curve = !( flag >> 7 );
+
+
+ flag &= 0x7f;
+ if ( flag < 84 )
+ data_bytes = 1;
+ else if ( flag < 120 )
+ data_bytes = 2;
+ else if ( flag < 124 )
+ data_bytes = 3;
+ else
+ data_bytes = 4;
+
+ /* Overflow checks */
+ if ( triplet_index + data_bytes > in_size ||
+ triplet_index + data_bytes < triplet_index )
+ return FT_THROW( Invalid_Table );
+
+ if ( flag < 10 )
+ {
+ dx = 0;
+ dy = with_sign( flag,
+ ( ( flag & 14 ) << 7 ) + in[triplet_index] );
+ }
+ else if ( flag < 20 )
+ {
+ dx = with_sign( flag,
+ ( ( ( flag - 10 ) & 14 ) << 7 ) +
+ in[triplet_index] );
+ dy = 0;
+ }
+ else if ( flag < 84 )
+ {
+ b0 = flag - 20;
+ b1 = in[triplet_index];
+ dx = with_sign( flag,
+ 1 + ( b0 & 0x30 ) + ( b1 >> 4 ) );
+ dy = with_sign( flag >> 1,
+ 1 + ( ( b0 & 0x0c ) << 2 ) + ( b1 & 0x0f ) );
+ }
+ else if ( flag < 120 )
+ {
+ b0 = flag - 84;
+ dx = with_sign( flag,
+ 1 + ( ( b0 / 12 ) << 8 ) + in[triplet_index] );
+ dy = with_sign( flag >> 1,
+ 1 + ( ( ( b0 % 12 ) >> 2 ) << 8 ) +
+ in[triplet_index + 1] );
+ }
+ else if ( flag < 124 )
+ {
+ b2 = in[triplet_index + 1];
+ dx = with_sign( flag,
+ ( in[triplet_index] << 4 ) + ( b2 >> 4 ) );
+ dy = with_sign( flag >> 1,
+ ( ( b2 & 0x0f ) << 8 ) + in[triplet_index + 2] );
+ }
+ else
+ {
+ dx = with_sign( flag,
+ ( in[triplet_index] << 8 ) +
+ in[triplet_index + 1] );
+ dy = with_sign( flag >> 1,
+ ( in[triplet_index + 2] << 8 ) +
+ in[triplet_index + 3] );
+ }
+
+ triplet_index += data_bytes;
+
+ if ( safe_int_addition( x, dx, &x ) )
+ return FT_THROW( Invalid_Table );
+
+ if ( safe_int_addition( y, dy, &y ) )
+ return FT_THROW( Invalid_Table );
+
+ result[i].x = x;
+ result[i].y = y;
+ result[i].on_curve = on_curve;
+ }
+
+ *in_bytes_used = triplet_index;
+ return FT_Err_Ok;
+ }
+
+
+ /* Store decoded points in glyph buffer. */
+ static FT_Error
+ store_points( FT_ULong n_points,
+ const WOFF2_Point points,
+ FT_UShort n_contours,
+ FT_UShort instruction_len,
+ FT_Bool have_overlap,
+ FT_Byte* dst,
+ FT_ULong dst_size,
+ FT_ULong* glyph_size )
+ {
+ FT_UInt flag_offset = 10 + ( 2 * n_contours ) + 2 + instruction_len;
+ FT_Byte last_flag = 0xFFU;
+ FT_Byte repeat_count = 0;
+ FT_Int last_x = 0;
+ FT_Int last_y = 0;
+ FT_UInt x_bytes = 0;
+ FT_UInt y_bytes = 0;
+ FT_UInt xy_bytes;
+ FT_UInt i;
+ FT_UInt x_offset;
+ FT_UInt y_offset;
+ FT_Byte* pointer;
+
+
+ for ( i = 0; i < n_points; ++i )
+ {
+ const WOFF2_PointRec point = points[i];
+
+ FT_Byte flag = point.on_curve ? GLYF_ON_CURVE : 0;
+ FT_Int dx = point.x - last_x;
+ FT_Int dy = point.y - last_y;
+
+
+ if ( i == 0 && have_overlap )
+ flag |= GLYF_OVERLAP_SIMPLE;
+
+ if ( dx == 0 )
+ flag |= GLYF_THIS_X_IS_SAME;
+ else if ( dx > -256 && dx < 256 )
+ {
+ flag |= GLYF_X_SHORT | ( dx > 0 ? GLYF_THIS_X_IS_SAME : 0 );
+ x_bytes += 1;
+ }
+ else
+ x_bytes += 2;
+
+ if ( dy == 0 )
+ flag |= GLYF_THIS_Y_IS_SAME;
+ else if ( dy > -256 && dy < 256 )
+ {
+ flag |= GLYF_Y_SHORT | ( dy > 0 ? GLYF_THIS_Y_IS_SAME : 0 );
+ y_bytes += 1;
+ }
+ else
+ y_bytes += 2;
+
+ if ( flag == last_flag && repeat_count != 255 )
+ {
+ dst[flag_offset - 1] |= GLYF_REPEAT;
+ repeat_count++;
+ }
+ else
+ {
+ if ( repeat_count != 0 )
+ {
+ if ( flag_offset >= dst_size )
+ return FT_THROW( Invalid_Table );
+
+ dst[flag_offset++] = repeat_count;
+ }
+ if ( flag_offset >= dst_size )
+ return FT_THROW( Invalid_Table );
+
+ dst[flag_offset++] = flag;
+ repeat_count = 0;
+ }
+
+ last_x = point.x;
+ last_y = point.y;
+ last_flag = flag;
+ }
+
+ if ( repeat_count != 0 )
+ {
+ if ( flag_offset >= dst_size )
+ return FT_THROW( Invalid_Table );
+
+ dst[flag_offset++] = repeat_count;
+ }
+
+ xy_bytes = x_bytes + y_bytes;
+ if ( xy_bytes < x_bytes ||
+ flag_offset + xy_bytes < flag_offset ||
+ flag_offset + xy_bytes > dst_size )
+ return FT_THROW( Invalid_Table );
+
+ x_offset = flag_offset;
+ y_offset = flag_offset + x_bytes;
+ last_x = 0;
+ last_y = 0;
+
+ for ( i = 0; i < n_points; ++i )
+ {
+ FT_Int dx = points[i].x - last_x;
+ FT_Int dy = points[i].y - last_y;
+
+
+ if ( dx == 0 )
+ ;
+ else if ( dx > -256 && dx < 256 )
+ dst[x_offset++] = (FT_Byte)FT_ABS( dx );
+ else
+ {
+ pointer = dst + x_offset;
+ WRITE_SHORT( pointer, dx );
+ x_offset += 2;
+ }
+
+ last_x += dx;
+
+ if ( dy == 0 )
+ ;
+ else if ( dy > -256 && dy < 256 )
+ dst[y_offset++] = (FT_Byte)FT_ABS( dy );
+ else
+ {
+ pointer = dst + y_offset;
+ WRITE_SHORT( pointer, dy );
+ y_offset += 2;
+ }
+
+ last_y += dy;
+ }
+
+ *glyph_size = y_offset;
+ return FT_Err_Ok;
+ }
+
+
+ static void
+ compute_bbox( FT_ULong n_points,
+ const WOFF2_Point points,
+ FT_Byte* dst,
+ FT_UShort* src_x_min )
+ {
+ FT_Int x_min = 0;
+ FT_Int y_min = 0;
+ FT_Int x_max = 0;
+ FT_Int y_max = 0;
+
+ FT_UInt i;
+
+ FT_ULong offset;
+ FT_Byte* pointer;
+
+
+ if ( n_points > 0 )
+ {
+ x_min = points[0].x;
+ y_min = points[0].y;
+ x_max = points[0].x;
+ y_max = points[0].y;
+ }
+
+ for ( i = 1; i < n_points; ++i )
+ {
+ FT_Int x = points[i].x;
+ FT_Int y = points[i].y;
+
+
+ x_min = FT_MIN( x, x_min );
+ y_min = FT_MIN( y, y_min );
+ x_max = FT_MAX( x, x_max );
+ y_max = FT_MAX( y, y_max );
+ }
+
+ /* Write values to `glyf' record. */
+ offset = 2;
+ pointer = dst + offset;
+
+ WRITE_SHORT( pointer, x_min );
+ WRITE_SHORT( pointer, y_min );
+ WRITE_SHORT( pointer, x_max );
+ WRITE_SHORT( pointer, y_max );
+
+ *src_x_min = (FT_UShort)x_min;
+ }
+
+
+ static FT_Error
+ compositeGlyph_size( FT_Stream stream,
+ FT_ULong offset,
+ FT_ULong* size,
+ FT_Bool* have_instructions )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_ULong start_offset = offset;
+ FT_Bool we_have_inst = FALSE;
+ FT_UShort flags = FLAG_MORE_COMPONENTS;
+
+
+ if ( FT_STREAM_SEEK( start_offset ) )
+ goto Exit;
+ while ( flags & FLAG_MORE_COMPONENTS )
+ {
+ FT_ULong arg_size;
+
+
+ if ( FT_READ_USHORT( flags ) )
+ goto Exit;
+ we_have_inst |= ( flags & FLAG_WE_HAVE_INSTRUCTIONS ) != 0;
+ /* glyph index */
+ arg_size = 2;
+ if ( flags & FLAG_ARG_1_AND_2_ARE_WORDS )
+ arg_size += 4;
+ else
+ arg_size += 2;
+
+ if ( flags & FLAG_WE_HAVE_A_SCALE )
+ arg_size += 2;
+ else if ( flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE )
+ arg_size += 4;
+ else if ( flags & FLAG_WE_HAVE_A_TWO_BY_TWO )
+ arg_size += 8;
+
+ if ( FT_STREAM_SKIP( arg_size ) )
+ goto Exit;
+ }
+
+ *size = FT_STREAM_POS() - start_offset;
+ *have_instructions = we_have_inst;
+
+ Exit:
+ return error;
+ }
+
+
+ /* Store loca values (provided by `reconstruct_glyf') to output stream. */
+ static FT_Error
+ store_loca( FT_ULong* loca_values,
+ FT_ULong loca_values_size,
+ FT_UShort index_format,
+ FT_ULong* checksum,
+ FT_Byte** sfnt_bytes,
+ FT_ULong* sfnt_size,
+ FT_ULong* out_offset,
+ FT_Memory memory )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Byte* sfnt = *sfnt_bytes;
+ FT_ULong dest_offset = *out_offset;
+
+ FT_Byte* loca_buf = NULL;
+ FT_Byte* dst = NULL;
+
+ FT_UInt i = 0;
+ FT_ULong loca_buf_size;
+
+ const FT_ULong offset_size = index_format ? 4 : 2;
+
+
+ if ( ( loca_values_size << 2 ) >> 2 != loca_values_size )
+ goto Fail;
+
+ loca_buf_size = loca_values_size * offset_size;
+ if ( FT_QALLOC( loca_buf, loca_buf_size ) )
+ goto Fail;
+
+ dst = loca_buf;
+ for ( i = 0; i < loca_values_size; i++ )
+ {
+ FT_ULong value = loca_values[i];
+
+
+ if ( index_format )
+ WRITE_ULONG( dst, value );
+ else
+ WRITE_USHORT( dst, ( value >> 1 ) );
+ }
+
+ *checksum = compute_ULong_sum( loca_buf, loca_buf_size );
+ /* Write `loca' table to sfnt buffer. */
+ if ( WRITE_SFNT_BUF( loca_buf, loca_buf_size ) )
+ goto Fail;
+
+ /* Set pointer `sfnt_bytes' to its correct value. */
+ *sfnt_bytes = sfnt;
+ *out_offset = dest_offset;
+
+ FT_FREE( loca_buf );
+ return error;
+
+ Fail:
+ if ( !error )
+ error = FT_THROW( Invalid_Table );
+
+ FT_FREE( loca_buf );
+
+ return error;
+ }
+
+
+ static FT_Error
+ reconstruct_glyf( FT_Stream stream,
+ FT_ULong* glyf_checksum,
+ FT_ULong* loca_checksum,
+ FT_Byte** sfnt_bytes,
+ FT_ULong* sfnt_size,
+ FT_ULong* out_offset,
+ WOFF2_Info info,
+ FT_Memory memory )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Byte* sfnt = *sfnt_bytes;
+
+ /* current position in stream */
+ const FT_ULong pos = FT_STREAM_POS();
+
+ FT_UInt num_substreams = 7;
+
+ FT_UShort option_flags;
+ FT_UShort num_glyphs;
+ FT_UShort index_format;
+ FT_ULong expected_loca_length;
+ FT_UInt offset;
+ FT_UInt i;
+ FT_ULong points_size;
+ FT_ULong glyph_buf_size;
+ FT_ULong bbox_bitmap_offset;
+ FT_ULong bbox_bitmap_length;
+ FT_ULong overlap_bitmap_offset = 0;
+ FT_ULong overlap_bitmap_length = 0;
+
+ const FT_ULong glyf_start = *out_offset;
+ FT_ULong dest_offset = *out_offset;
+
+ WOFF2_Substream substreams = NULL;
+
+ FT_ULong* loca_values = NULL;
+ FT_UShort* n_points_arr = NULL;
+ FT_Byte* glyph_buf = NULL;
+ WOFF2_Point points = NULL;
+
+
+ if ( FT_QNEW_ARRAY( substreams, num_substreams ) )
+ goto Fail;
+
+ if ( FT_STREAM_SKIP( 2 ) )
+ goto Fail;
+ if ( FT_READ_USHORT( option_flags ) )
+ goto Fail;
+ if ( FT_READ_USHORT( num_glyphs ) )
+ goto Fail;
+ if ( FT_READ_USHORT( index_format ) )
+ goto Fail;
+
+ FT_TRACE4(( "option_flags = %u; num_glyphs = %u; index_format = %u\n",
+ option_flags, num_glyphs, index_format ));
+
+ info->num_glyphs = num_glyphs;
+
+ /* Calculate expected length of loca and compare. */
+ /* See https://www.w3.org/TR/WOFF2/#conform-mustRejectLoca */
+ /* index_format = 0 => Short version `loca'. */
+ /* index_format = 1 => Long version `loca'. */
+ expected_loca_length = ( index_format ? 4 : 2 ) *
+ ( (FT_ULong)num_glyphs + 1 );
+ if ( info->loca_table->dst_length != expected_loca_length )
+ goto Fail;
+
+ offset = 2 + 2 + 2 + 2 + ( num_substreams * 4 );
+ if ( offset > info->glyf_table->TransformLength )
+ goto Fail;
+
+ for ( i = 0; i < num_substreams; ++i )
+ {
+ FT_ULong substream_size;
+
+
+ if ( FT_READ_ULONG( substream_size ) )
+ goto Fail;
+ if ( substream_size > info->glyf_table->TransformLength - offset )
+ goto Fail;
+
+ substreams[i].start = pos + offset;
+ substreams[i].offset = pos + offset;
+ substreams[i].size = substream_size;
+
+ FT_TRACE5(( " Substream %d: offset = %lu; size = %lu;\n",
+ i, substreams[i].offset, substreams[i].size ));
+ offset += substream_size;
+ }
+
+ if ( option_flags & HAVE_OVERLAP_SIMPLE_BITMAP )
+ {
+ /* Size of overlapBitmap = floor((numGlyphs + 7) / 8) */
+ overlap_bitmap_length = ( num_glyphs + 7U ) >> 3;
+ if ( overlap_bitmap_length > info->glyf_table->TransformLength - offset )
+ goto Fail;
+
+ overlap_bitmap_offset = pos + offset;
+
+ FT_TRACE5(( " Overlap bitmap: offset = %lu; size = %lu;\n",
+ overlap_bitmap_offset, overlap_bitmap_length ));
+ offset += overlap_bitmap_length;
+ }
+
+ if ( FT_QNEW_ARRAY( loca_values, num_glyphs + 1 ) )
+ goto Fail;
+
+ points_size = 0;
+ bbox_bitmap_offset = substreams[BBOX_STREAM].offset;
+
+ /* Size of bboxBitmap = 4 * floor((numGlyphs + 31) / 32) */
+ bbox_bitmap_length = ( ( num_glyphs + 31U ) >> 5 ) << 2;
+ /* bboxStreamSize is the combined size of bboxBitmap and bboxStream. */
+ substreams[BBOX_STREAM].offset += bbox_bitmap_length;
+
+ glyph_buf_size = WOFF2_DEFAULT_GLYPH_BUF;
+ if ( FT_QALLOC( glyph_buf, glyph_buf_size ) )
+ goto Fail;
+
+ if ( FT_QNEW_ARRAY( info->x_mins, num_glyphs ) )
+ goto Fail;
+
+ for ( i = 0; i < num_glyphs; ++i )
+ {
+ FT_ULong glyph_size = 0;
+ FT_UShort n_contours = 0;
+ FT_Bool have_bbox = FALSE;
+ FT_Byte bbox_bitmap;
+ FT_ULong bbox_offset;
+ FT_UShort x_min = 0;
+
+
+ /* Set `have_bbox'. */
+ bbox_offset = bbox_bitmap_offset + ( i >> 3 );
+ if ( FT_STREAM_SEEK( bbox_offset ) ||
+ FT_READ_BYTE( bbox_bitmap ) )
+ goto Fail;
+ if ( bbox_bitmap & ( 0x80 >> ( i & 7 ) ) )
+ have_bbox = TRUE;
+
+ /* Read value from `nContourStream'. */
+ if ( FT_STREAM_SEEK( substreams[N_CONTOUR_STREAM].offset ) ||
+ FT_READ_USHORT( n_contours ) )
+ goto Fail;
+ substreams[N_CONTOUR_STREAM].offset += 2;
+
+ if ( n_contours == 0xffff )
+ {
+ /* composite glyph */
+ FT_Bool have_instructions = FALSE;
+ FT_UShort instruction_size = 0;
+ FT_ULong composite_size = 0;
+ FT_ULong size_needed;
+ FT_Byte* pointer = NULL;
+
+
+ /* Composite glyphs must have explicit bbox. */
+ if ( !have_bbox )
+ goto Fail;
+
+ if ( compositeGlyph_size( stream,
+ substreams[COMPOSITE_STREAM].offset,
+ &composite_size,
+ &have_instructions) )
+ goto Fail;
+
+ if ( have_instructions )
+ {
+ if ( FT_STREAM_SEEK( substreams[GLYPH_STREAM].offset ) ||
+ READ_255USHORT( instruction_size ) )
+ goto Fail;
+ substreams[GLYPH_STREAM].offset = FT_STREAM_POS();
+ }
+
+ size_needed = 12 + composite_size + instruction_size;
+ if ( glyph_buf_size < size_needed )
+ {
+ if ( FT_QREALLOC( glyph_buf, glyph_buf_size, size_needed ) )
+ goto Fail;
+ glyph_buf_size = size_needed;
+ }
+
+ pointer = glyph_buf + glyph_size;
+ WRITE_USHORT( pointer, n_contours );
+ glyph_size += 2;
+
+ /* Read x_min for current glyph. */
+ if ( FT_STREAM_SEEK( substreams[BBOX_STREAM].offset ) ||
+ FT_READ_USHORT( x_min ) )
+ goto Fail;
+ /* No increment here because we read again. */
+
+ if ( FT_STREAM_SEEK( substreams[BBOX_STREAM].offset ) ||
+ FT_STREAM_READ( glyph_buf + glyph_size, 8 ) )
+ goto Fail;
+
+ substreams[BBOX_STREAM].offset += 8;
+ glyph_size += 8;
+
+ if ( FT_STREAM_SEEK( substreams[COMPOSITE_STREAM].offset ) ||
+ FT_STREAM_READ( glyph_buf + glyph_size, composite_size ) )
+ goto Fail;
+
+ substreams[COMPOSITE_STREAM].offset += composite_size;
+ glyph_size += composite_size;
+
+ if ( have_instructions )
+ {
+ pointer = glyph_buf + glyph_size;
+ WRITE_USHORT( pointer, instruction_size );
+ glyph_size += 2;
+
+ if ( FT_STREAM_SEEK( substreams[INSTRUCTION_STREAM].offset ) ||
+ FT_STREAM_READ( glyph_buf + glyph_size, instruction_size ) )
+ goto Fail;
+
+ substreams[INSTRUCTION_STREAM].offset += instruction_size;
+ glyph_size += instruction_size;
+ }
+ }
+ else if ( n_contours > 0 )
+ {
+ /* simple glyph */
+ FT_ULong total_n_points = 0;
+ FT_UShort n_points_contour;
+ FT_UInt j;
+ FT_ULong flag_size;
+ FT_ULong triplet_size;
+ FT_ULong triplet_bytes_used;
+ FT_Bool have_overlap = FALSE;
+ FT_Byte overlap_bitmap;
+ FT_ULong overlap_offset;
+ FT_Byte* flags_buf = NULL;
+ FT_Byte* triplet_buf = NULL;
+ FT_UShort instruction_size;
+ FT_ULong size_needed;
+ FT_Int end_point;
+ FT_UInt contour_ix;
+
+ FT_Byte* pointer = NULL;
+
+
+ /* Set `have_overlap`. */
+ if ( overlap_bitmap_offset )
+ {
+ overlap_offset = overlap_bitmap_offset + ( i >> 3 );
+ if ( FT_STREAM_SEEK( overlap_offset ) ||
+ FT_READ_BYTE( overlap_bitmap ) )
+ goto Fail;
+ if ( overlap_bitmap & ( 0x80 >> ( i & 7 ) ) )
+ have_overlap = TRUE;
+ }
+
+ if ( FT_QNEW_ARRAY( n_points_arr, n_contours ) )
+ goto Fail;
+
+ if ( FT_STREAM_SEEK( substreams[N_POINTS_STREAM].offset ) )
+ goto Fail;
+
+ for ( j = 0; j < n_contours; ++j )
+ {
+ if ( READ_255USHORT( n_points_contour ) )
+ goto Fail;
+ n_points_arr[j] = n_points_contour;
+ /* Prevent negative/overflow. */
+ if ( total_n_points + n_points_contour < total_n_points )
+ goto Fail;
+ total_n_points += n_points_contour;
+ }
+ substreams[N_POINTS_STREAM].offset = FT_STREAM_POS();
+
+ flag_size = total_n_points;
+ if ( flag_size > substreams[FLAG_STREAM].size )
+ goto Fail;
+
+ flags_buf = stream->base + substreams[FLAG_STREAM].offset;
+ triplet_buf = stream->base + substreams[GLYPH_STREAM].offset;
+
+ if ( substreams[GLYPH_STREAM].size <
+ ( substreams[GLYPH_STREAM].offset -
+ substreams[GLYPH_STREAM].start ) )
+ goto Fail;
+
+ triplet_size = substreams[GLYPH_STREAM].size -
+ ( substreams[GLYPH_STREAM].offset -
+ substreams[GLYPH_STREAM].start );
+ triplet_bytes_used = 0;
+
+ /* Create array to store point information. */
+ points_size = total_n_points;
+ if ( FT_QNEW_ARRAY( points, points_size ) )
+ goto Fail;
+
+ if ( triplet_decode( flags_buf,
+ triplet_buf,
+ triplet_size,
+ total_n_points,
+ points,
+ &triplet_bytes_used ) )
+ goto Fail;
+
+ substreams[FLAG_STREAM].offset += flag_size;
+ substreams[GLYPH_STREAM].offset += triplet_bytes_used;
+
+ if ( FT_STREAM_SEEK( substreams[GLYPH_STREAM].offset ) ||
+ READ_255USHORT( instruction_size ) )
+ goto Fail;
+
+ substreams[GLYPH_STREAM].offset = FT_STREAM_POS();
+
+ if ( total_n_points >= ( 1 << 27 ) )
+ goto Fail;
+
+ size_needed = 12 +
+ ( 2 * n_contours ) +
+ ( 5 * total_n_points ) +
+ instruction_size;
+ if ( glyph_buf_size < size_needed )
+ {
+ if ( FT_QREALLOC( glyph_buf, glyph_buf_size, size_needed ) )
+ goto Fail;
+ glyph_buf_size = size_needed;
+ }
+
+ pointer = glyph_buf + glyph_size;
+ WRITE_USHORT( pointer, n_contours );
+ glyph_size += 2;
+
+ if ( have_bbox )
+ {
+ /* Read x_min for current glyph. */
+ if ( FT_STREAM_SEEK( substreams[BBOX_STREAM].offset ) ||
+ FT_READ_USHORT( x_min ) )
+ goto Fail;
+ /* No increment here because we read again. */
+
+ if ( FT_STREAM_SEEK( substreams[BBOX_STREAM].offset ) ||
+ FT_STREAM_READ( glyph_buf + glyph_size, 8 ) )
+ goto Fail;
+ substreams[BBOX_STREAM].offset += 8;
+ }
+ else
+ compute_bbox( total_n_points, points, glyph_buf, &x_min );
+
+ glyph_size = CONTOUR_OFFSET_END_POINT;
+
+ pointer = glyph_buf + glyph_size;
+ end_point = -1;
+
+ for ( contour_ix = 0; contour_ix < n_contours; ++contour_ix )
+ {
+ end_point += n_points_arr[contour_ix];
+ if ( end_point >= 65536 )
+ goto Fail;
+
+ WRITE_SHORT( pointer, end_point );
+ glyph_size += 2;
+ }
+
+ WRITE_USHORT( pointer, instruction_size );
+ glyph_size += 2;
+
+ if ( FT_STREAM_SEEK( substreams[INSTRUCTION_STREAM].offset ) ||
+ FT_STREAM_READ( glyph_buf + glyph_size, instruction_size ) )
+ goto Fail;
+
+ substreams[INSTRUCTION_STREAM].offset += instruction_size;
+ glyph_size += instruction_size;
+
+ if ( store_points( total_n_points,
+ points,
+ n_contours,
+ instruction_size,
+ have_overlap,
+ glyph_buf,
+ glyph_buf_size,
+ &glyph_size ) )
+ goto Fail;
+
+ FT_FREE( points );
+ FT_FREE( n_points_arr );
+ }
+ else
+ {
+ /* Empty glyph. */
+ /* Must not have a bbox. */
+ if ( have_bbox )
+ {
+ FT_ERROR(( "Empty glyph has a bbox.\n" ));
+ goto Fail;
+ }
+ }
+
+ loca_values[i] = dest_offset - glyf_start;
+
+ if ( WRITE_SFNT_BUF( glyph_buf, glyph_size ) )
+ goto Fail;
+
+ if ( pad4( &sfnt, sfnt_size, &dest_offset, memory ) )
+ goto Fail;
+
+ *glyf_checksum += compute_ULong_sum( glyph_buf, glyph_size );
+
+ /* Store x_mins, may be required to reconstruct `hmtx'. */
+ info->x_mins[i] = (FT_Short)x_min;
+ }
+
+ info->glyf_table->dst_length = dest_offset - info->glyf_table->dst_offset;
+ info->loca_table->dst_offset = dest_offset;
+
+ /* `loca[n]' will be equal to the length of the `glyf' table. */
+ loca_values[num_glyphs] = info->glyf_table->dst_length;
+
+ if ( store_loca( loca_values,
+ num_glyphs + 1,
+ index_format,
+ loca_checksum,
+ &sfnt,
+ sfnt_size,
+ &dest_offset,
+ memory ) )
+ goto Fail;
+
+ info->loca_table->dst_length = dest_offset - info->loca_table->dst_offset;
+
+ FT_TRACE4(( " loca table info:\n" ));
+ FT_TRACE4(( " dst_offset = %lu\n", info->loca_table->dst_offset ));
+ FT_TRACE4(( " dst_length = %lu\n", info->loca_table->dst_length ));
+ FT_TRACE4(( " checksum = %09lx\n", *loca_checksum ));
+
+ /* Set pointer `sfnt_bytes' to its correct value. */
+ *sfnt_bytes = sfnt;
+ *out_offset = dest_offset;
+
+ FT_FREE( substreams );
+ FT_FREE( loca_values );
+ FT_FREE( n_points_arr );
+ FT_FREE( glyph_buf );
+ FT_FREE( points );
+
+ return error;
+
+ Fail:
+ if ( !error )
+ error = FT_THROW( Invalid_Table );
+
+ /* Set pointer `sfnt_bytes' to its correct value. */
+ *sfnt_bytes = sfnt;
+
+ FT_FREE( substreams );
+ FT_FREE( loca_values );
+ FT_FREE( n_points_arr );
+ FT_FREE( glyph_buf );
+ FT_FREE( points );
+
+ return error;
+ }
+
+
+ /* Get `x_mins' for untransformed `glyf' table. */
+ static FT_Error
+ get_x_mins( FT_Stream stream,
+ WOFF2_Table* tables,
+ FT_UShort num_tables,
+ WOFF2_Info info,
+ FT_Memory memory )
+ {
+ FT_UShort num_glyphs;
+ FT_UShort index_format;
+ FT_ULong glyf_offset;
+ FT_UShort glyf_offset_short;
+ FT_ULong loca_offset;
+ FT_Int i;
+ FT_Error error = FT_Err_Ok;
+ FT_ULong offset_size;
+
+ /* At this point of time those tables might not have been read yet. */
+ const WOFF2_Table maxp_table = find_table( tables, num_tables,
+ TTAG_maxp );
+ const WOFF2_Table head_table = find_table( tables, num_tables,
+ TTAG_head );
+
+
+ if ( !maxp_table )
+ {
+ FT_ERROR(( "`maxp' table is missing.\n" ));
+ return FT_THROW( Invalid_Table );
+ }
+
+ if ( !head_table )
+ {
+ FT_ERROR(( "`head' table is missing.\n" ));
+ return FT_THROW( Invalid_Table );
+ }
+
+ if ( !info->loca_table )
+ {
+ FT_ERROR(( "`loca' table is missing.\n" ));
+ return FT_THROW( Invalid_Table );
+ }
+
+ /* Read `numGlyphs' field from `maxp' table. */
+ if ( FT_STREAM_SEEK( maxp_table->src_offset ) || FT_STREAM_SKIP( 8 ) )
+ return error;
+
+ if ( FT_READ_USHORT( num_glyphs ) )
+ return error;
+
+ info->num_glyphs = num_glyphs;
+
+ /* Read `indexToLocFormat' field from `head' table. */
+ if ( FT_STREAM_SEEK( head_table->src_offset ) ||
+ FT_STREAM_SKIP( 50 ) )
+ return error;
+
+ if ( FT_READ_USHORT( index_format ) )
+ return error;
+
+ offset_size = index_format ? 4 : 2;
+
+ /* Create `x_mins' array. */
+ if ( FT_QNEW_ARRAY( info->x_mins, num_glyphs ) )
+ return error;
+
+ loca_offset = info->loca_table->src_offset;
+
+ for ( i = 0; i < num_glyphs; ++i )
+ {
+ if ( FT_STREAM_SEEK( loca_offset ) )
+ return error;
+
+ loca_offset += offset_size;
+
+ if ( index_format )
+ {
+ if ( FT_READ_ULONG( glyf_offset ) )
+ return error;
+ }
+ else
+ {
+ if ( FT_READ_USHORT( glyf_offset_short ) )
+ return error;
+
+ glyf_offset = (FT_ULong)( glyf_offset_short );
+ glyf_offset = glyf_offset << 1;
+ }
+
+ glyf_offset += info->glyf_table->src_offset;
+
+ if ( FT_STREAM_SEEK( glyf_offset ) || FT_STREAM_SKIP( 2 ) )
+ return error;
+
+ if ( FT_READ_SHORT( info->x_mins[i] ) )
+ return error;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ reconstruct_hmtx( FT_Stream stream,
+ FT_UShort num_glyphs,
+ FT_UShort num_hmetrics,
+ FT_Short* x_mins,
+ FT_ULong* checksum,
+ FT_Byte** sfnt_bytes,
+ FT_ULong* sfnt_size,
+ FT_ULong* out_offset,
+ FT_Memory memory )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Byte* sfnt = *sfnt_bytes;
+ FT_ULong dest_offset = *out_offset;
+
+ FT_Byte hmtx_flags;
+ FT_Bool has_proportional_lsbs, has_monospace_lsbs;
+ FT_ULong hmtx_table_size;
+ FT_Int i;
+
+ FT_UShort* advance_widths = NULL;
+ FT_Short* lsbs = NULL;
+ FT_Byte* hmtx_table = NULL;
+ FT_Byte* dst = NULL;
+
+
+ if ( FT_READ_BYTE( hmtx_flags ) )
+ goto Fail;
+
+ has_proportional_lsbs = ( hmtx_flags & 1 ) == 0;
+ has_monospace_lsbs = ( hmtx_flags & 2 ) == 0;
+
+ /* Bits 2-7 are reserved and MUST be zero. */
+ if ( ( hmtx_flags & 0xFC ) != 0 )
+ goto Fail;
+
+ /* Are you REALLY transformed? */
+ if ( has_proportional_lsbs && has_monospace_lsbs )
+ goto Fail;
+
+ /* Cannot have a transformed `hmtx' without `glyf'. */
+ if ( ( num_hmetrics > num_glyphs ) ||
+ ( num_hmetrics < 1 ) )
+ goto Fail;
+
+ /* Must have at least one entry. */
+ if ( num_hmetrics < 1 )
+ goto Fail;
+
+ if ( FT_QNEW_ARRAY( advance_widths, num_hmetrics ) ||
+ FT_QNEW_ARRAY( lsbs, num_glyphs ) )
+ goto Fail;
+
+ /* Read `advanceWidth' stream. Always present. */
+ for ( i = 0; i < num_hmetrics; i++ )
+ {
+ FT_UShort advance_width;
+
+
+ if ( FT_READ_USHORT( advance_width ) )
+ goto Fail;
+
+ advance_widths[i] = advance_width;
+ }
+
+ /* lsb values for proportional glyphs. */
+ for ( i = 0; i < num_hmetrics; i++ )
+ {
+ FT_Short lsb;
+
+
+ if ( has_proportional_lsbs )
+ {
+ if ( FT_READ_SHORT( lsb ) )
+ goto Fail;
+ }
+ else
+ lsb = x_mins[i];
+
+ lsbs[i] = lsb;
+ }
+
+ /* lsb values for monospaced glyphs. */
+ for ( i = num_hmetrics; i < num_glyphs; i++ )
+ {
+ FT_Short lsb;
+
+
+ if ( has_monospace_lsbs )
+ {
+ if ( FT_READ_SHORT( lsb ) )
+ goto Fail;
+ }
+ else
+ lsb = x_mins[i];
+
+ lsbs[i] = lsb;
+ }
+
+ /* Build the hmtx table. */
+ hmtx_table_size = 2 * num_hmetrics + 2 * num_glyphs;
+ if ( FT_QALLOC( hmtx_table, hmtx_table_size ) )
+ goto Fail;
+
+ dst = hmtx_table;
+ FT_TRACE6(( "hmtx values: \n" ));
+ for ( i = 0; i < num_glyphs; i++ )
+ {
+ if ( i < num_hmetrics )
+ {
+ WRITE_SHORT( dst, advance_widths[i] );
+ FT_TRACE6(( "%d ", advance_widths[i] ));
+ }
+
+ WRITE_SHORT( dst, lsbs[i] );
+ FT_TRACE6(( "%d ", lsbs[i] ));
+ }
+ FT_TRACE6(( "\n" ));
+
+ *checksum = compute_ULong_sum( hmtx_table, hmtx_table_size );
+ /* Write `hmtx' table to sfnt buffer. */
+ if ( WRITE_SFNT_BUF( hmtx_table, hmtx_table_size ) )
+ goto Fail;
+
+ /* Set pointer `sfnt_bytes' to its correct value. */
+ *sfnt_bytes = sfnt;
+ *out_offset = dest_offset;
+
+ FT_FREE( advance_widths );
+ FT_FREE( lsbs );
+ FT_FREE( hmtx_table );
+
+ return error;
+
+ Fail:
+ FT_FREE( advance_widths );
+ FT_FREE( lsbs );
+ FT_FREE( hmtx_table );
+
+ if ( !error )
+ error = FT_THROW( Invalid_Table );
+
+ return error;
+ }
+
+
+ static FT_Error
+ reconstruct_font( FT_Byte* transformed_buf,
+ FT_ULong transformed_buf_size,
+ WOFF2_Table* indices,
+ WOFF2_Header woff2,
+ WOFF2_Info info,
+ FT_Byte** sfnt_bytes,
+ FT_ULong* sfnt_size,
+ FT_Memory memory )
+ {
+ /* Memory management of `transformed_buf' is handled by the caller. */
+
+ FT_Error error = FT_Err_Ok;
+ FT_Stream stream = NULL;
+ FT_Byte* buf_cursor = NULL;
+ FT_Byte table_entry[16];
+
+ /* We are reallocating memory for `sfnt', so its pointer may change. */
+ FT_Byte* sfnt = *sfnt_bytes;
+
+ FT_UShort num_tables = woff2->num_tables;
+ FT_ULong dest_offset = 12 + num_tables * 16UL;
+
+ FT_ULong checksum = 0;
+ FT_ULong loca_checksum = 0;
+ FT_Int nn = 0;
+ FT_UShort num_hmetrics = 0;
+ FT_ULong font_checksum = info->header_checksum;
+ FT_Bool is_glyf_xform = FALSE;
+
+ FT_ULong table_entry_offset = 12;
+
+
+ /* A few table checks before reconstruction. */
+ /* `glyf' must be present with `loca'. */
+ info->glyf_table = find_table( indices, num_tables, TTAG_glyf );
+ info->loca_table = find_table( indices, num_tables, TTAG_loca );
+
+ if ( ( info->glyf_table == NULL ) ^ ( info->loca_table == NULL ) )
+ {
+ FT_ERROR(( "One of `glyf'/`loca' tables missing.\n" ));
+ return FT_THROW( Invalid_Table );
+ }
+
+ /* Both `glyf' and `loca' must have same transformation. */
+ if ( info->glyf_table != NULL )
+ {
+ if ( ( info->glyf_table->flags & WOFF2_FLAGS_TRANSFORM ) !=
+ ( info->loca_table->flags & WOFF2_FLAGS_TRANSFORM ) )
+ {
+ FT_ERROR(( "Transformation mismatch"
+ " between `glyf' and `loca' table." ));
+ return FT_THROW( Invalid_Table );
+ }
+ }
+
+ /* Create a stream for the uncompressed buffer. */
+ if ( FT_NEW( stream ) )
+ goto Fail;
+ FT_Stream_OpenMemory( stream, transformed_buf, transformed_buf_size );
+
+ FT_ASSERT( FT_STREAM_POS() == 0 );
+
+ /* Reconstruct/copy tables to output stream. */
+ for ( nn = 0; nn < num_tables; nn++ )
+ {
+ WOFF2_TableRec table = *( indices[nn] );
+
+
+ FT_TRACE3(( "Seeking to %ld with table size %ld.\n",
+ table.src_offset, table.src_length ));
+ FT_TRACE3(( "Table tag: %c%c%c%c.\n",
+ (FT_Char)( table.Tag >> 24 ),
+ (FT_Char)( table.Tag >> 16 ),
+ (FT_Char)( table.Tag >> 8 ),
+ (FT_Char)( table.Tag ) ));
+
+ if ( FT_STREAM_SEEK( table.src_offset ) )
+ goto Fail;
+
+ if ( table.src_offset + table.src_length > transformed_buf_size )
+ goto Fail;
+
+ /* Get stream size for fields of `hmtx' table. */
+ if ( table.Tag == TTAG_hhea )
+ {
+ if ( read_num_hmetrics( stream, &num_hmetrics ) )
+ goto Fail;
+ }
+
+ info->num_hmetrics = num_hmetrics;
+
+ checksum = 0;
+ if ( ( table.flags & WOFF2_FLAGS_TRANSFORM ) != WOFF2_FLAGS_TRANSFORM )
+ {
+ /* Check whether `head' is at least 12 bytes. */
+ if ( table.Tag == TTAG_head )
+ {
+ if ( table.src_length < 12 )
+ goto Fail;
+
+ buf_cursor = transformed_buf + table.src_offset + 8;
+ /* Set checkSumAdjustment = 0 */
+ WRITE_ULONG( buf_cursor, 0 );
+ }
+
+ table.dst_offset = dest_offset;
+
+ checksum = compute_ULong_sum( transformed_buf + table.src_offset,
+ table.src_length );
+ FT_TRACE4(( "Checksum = %09lx.\n", checksum ));
+
+ if ( WRITE_SFNT_BUF( transformed_buf + table.src_offset,
+ table.src_length ) )
+ goto Fail;
+ }
+ else
+ {
+ FT_TRACE3(( "This table is transformed.\n" ));
+
+ if ( table.Tag == TTAG_glyf )
+ {
+ is_glyf_xform = TRUE;
+ table.dst_offset = dest_offset;
+
+ if ( reconstruct_glyf( stream,
+ &checksum,
+ &loca_checksum,
+ &sfnt,
+ sfnt_size,
+ &dest_offset,
+ info,
+ memory ) )
+ goto Fail;
+
+ FT_TRACE4(( "Checksum = %09lx.\n", checksum ));
+ }
+
+ else if ( table.Tag == TTAG_loca )
+ checksum = loca_checksum;
+
+ else if ( table.Tag == TTAG_hmtx )
+ {
+ /* If glyf is not transformed and hmtx is, handle separately. */
+ if ( !is_glyf_xform )
+ {
+ if ( get_x_mins( stream, indices, num_tables, info, memory ) )
+ goto Fail;
+ }
+
+ table.dst_offset = dest_offset;
+
+ if ( reconstruct_hmtx( stream,
+ info->num_glyphs,
+ info->num_hmetrics,
+ info->x_mins,
+ &checksum,
+ &sfnt,
+ sfnt_size,
+ &dest_offset,
+ memory ) )
+ goto Fail;
+ }
+ else
+ {
+ /* Unknown transform. */
+ FT_ERROR(( "Unknown table transform.\n" ));
+ goto Fail;
+ }
+ }
+
+ font_checksum += checksum;
+
+ buf_cursor = &table_entry[0];
+ WRITE_ULONG( buf_cursor, table.Tag );
+ WRITE_ULONG( buf_cursor, checksum );
+ WRITE_ULONG( buf_cursor, table.dst_offset );
+ WRITE_ULONG( buf_cursor, table.dst_length );
+
+ WRITE_SFNT_BUF_AT( table_entry_offset, table_entry, 16 );
+
+ /* Update checksum. */
+ font_checksum += compute_ULong_sum( table_entry, 16 );
+
+ if ( pad4( &sfnt, sfnt_size, &dest_offset, memory ) )
+ goto Fail;
+
+ /* Sanity check. */
+ if ( (FT_ULong)( table.dst_offset + table.dst_length ) > dest_offset )
+ {
+ FT_ERROR(( "Table was partially written.\n" ));
+ goto Fail;
+ }
+ }
+
+ /* Update `head' checkSumAdjustment. */
+ info->head_table = find_table( indices, num_tables, TTAG_head );
+ if ( !info->head_table )
+ {
+ FT_ERROR(( "`head' table is missing.\n" ));
+ goto Fail;
+ }
+
+ if ( info->head_table->dst_length < 12 )
+ goto Fail;
+
+ buf_cursor = sfnt + info->head_table->dst_offset + 8;
+ font_checksum = 0xB1B0AFBA - font_checksum;
+
+ WRITE_ULONG( buf_cursor, font_checksum );
+
+ FT_TRACE2(( "Final checksum = %09lx.\n", font_checksum ));
+
+ woff2->actual_sfnt_size = dest_offset;
+
+ /* Set pointer of sfnt stream to its correct value. */
+ *sfnt_bytes = sfnt;
+
+ FT_Stream_Close( stream );
+ FT_FREE( stream );
+
+ return error;
+
+ Fail:
+ if ( !error )
+ error = FT_THROW( Invalid_Table );
+
+ /* Set pointer of sfnt stream to its correct value. */
+ *sfnt_bytes = sfnt;
+
+ FT_Stream_Close( stream );
+ FT_FREE( stream );
+
+ return error;
+ }
+
+
+ /* Replace `face->root.stream' with a stream containing the extracted */
+ /* SFNT of a WOFF2 font. */
+
+ FT_LOCAL_DEF( FT_Error )
+ woff2_open_font( FT_Stream stream,
+ TT_Face face,
+ FT_Int* face_instance_index,
+ FT_Long* num_faces )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Int face_index;
+
+ WOFF2_HeaderRec woff2;
+ WOFF2_InfoRec info = { 0, 0, 0, NULL, NULL, NULL, NULL };
+ WOFF2_Table tables = NULL;
+ WOFF2_Table* indices = NULL;
+ WOFF2_Table* temp_indices = NULL;
+ WOFF2_Table last_table;
+
+ FT_Int nn;
+ FT_ULong j;
+ FT_ULong flags;
+ FT_UShort xform_version;
+ FT_ULong src_offset = 0;
+
+ FT_UInt glyf_index;
+ FT_UInt loca_index;
+ FT_UInt32 file_offset;
+
+ FT_Byte* sfnt = NULL;
+ FT_Stream sfnt_stream = NULL;
+ FT_Byte* sfnt_header;
+ FT_ULong sfnt_size;
+
+ FT_Byte* uncompressed_buf = NULL;
+
+ static const FT_Frame_Field woff2_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WOFF2_HeaderRec
+
+ FT_FRAME_START( 48 ),
+ FT_FRAME_ULONG ( signature ),
+ FT_FRAME_ULONG ( flavor ),
+ FT_FRAME_ULONG ( length ),
+ FT_FRAME_USHORT ( num_tables ),
+ FT_FRAME_SKIP_BYTES( 2 ),
+ FT_FRAME_ULONG ( totalSfntSize ),
+ FT_FRAME_ULONG ( totalCompressedSize ),
+ FT_FRAME_SKIP_BYTES( 2 * 2 ),
+ FT_FRAME_ULONG ( metaOffset ),
+ FT_FRAME_ULONG ( metaLength ),
+ FT_FRAME_ULONG ( metaOrigLength ),
+ FT_FRAME_ULONG ( privOffset ),
+ FT_FRAME_ULONG ( privLength ),
+ FT_FRAME_END
+ };
+
+
+ FT_ASSERT( stream == face->root.stream );
+ FT_ASSERT( FT_STREAM_POS() == 0 );
+
+ face_index = FT_ABS( *face_instance_index ) & 0xFFFF;
+
+ /* Read WOFF2 Header. */
+ if ( FT_STREAM_READ_FIELDS( woff2_header_fields, &woff2 ) )
+ return error;
+
+ FT_TRACE4(( "signature -> 0x%lX\n", woff2.signature ));
+ FT_TRACE2(( "flavor -> 0x%08lx\n", woff2.flavor ));
+ FT_TRACE4(( "length -> %lu\n", woff2.length ));
+ FT_TRACE2(( "num_tables -> %hu\n", woff2.num_tables ));
+ FT_TRACE4(( "totalSfntSize -> %lu\n", woff2.totalSfntSize ));
+ FT_TRACE4(( "metaOffset -> %lu\n", woff2.metaOffset ));
+ FT_TRACE4(( "metaLength -> %lu\n", woff2.metaLength ));
+ FT_TRACE4(( "privOffset -> %lu\n", woff2.privOffset ));
+ FT_TRACE4(( "privLength -> %lu\n", woff2.privLength ));
+
+ /* Make sure we don't recurse back here. */
+ if ( woff2.flavor == TTAG_wOF2 )
+ return FT_THROW( Invalid_Table );
+
+ /* Miscellaneous checks. */
+ if ( woff2.length != stream->size ||
+ woff2.num_tables == 0 ||
+ 48 + woff2.num_tables * 20UL >= woff2.length ||
+ ( woff2.metaOffset == 0 && ( woff2.metaLength != 0 ||
+ woff2.metaOrigLength != 0 ) ) ||
+ ( woff2.metaLength != 0 && woff2.metaOrigLength == 0 ) ||
+ ( woff2.metaOffset >= woff2.length ) ||
+ ( woff2.length - woff2.metaOffset < woff2.metaLength ) ||
+ ( woff2.privOffset == 0 && woff2.privLength != 0 ) ||
+ ( woff2.privOffset >= woff2.length ) ||
+ ( woff2.length - woff2.privOffset < woff2.privLength ) )
+ {
+ FT_ERROR(( "woff2_open_font: invalid WOFF2 header\n" ));
+ return FT_THROW( Invalid_Table );
+ }
+
+ FT_TRACE2(( "woff2_open_font: WOFF2 Header is valid.\n" ));
+
+ woff2.ttc_fonts = NULL;
+
+ /* Read table directory. */
+ if ( FT_QNEW_ARRAY( tables, woff2.num_tables ) ||
+ FT_QNEW_ARRAY( indices, woff2.num_tables ) )
+ goto Exit;
+
+ FT_TRACE2(( "\n" ));
+ FT_TRACE2(( " tag flags transform origLen transformLen offset\n" ));
+ FT_TRACE2(( " -----------------------------------------------------------\n" ));
+ /* " XXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX" */
+
+ for ( nn = 0; nn < woff2.num_tables; nn++ )
+ {
+ WOFF2_Table table = tables + nn;
+
+
+ if ( FT_READ_BYTE( table->FlagByte ) )
+ goto Exit;
+
+ if ( ( table->FlagByte & 0x3f ) == 0x3f )
+ {
+ if ( FT_READ_ULONG( table->Tag ) )
+ goto Exit;
+ }
+ else
+ {
+ table->Tag = woff2_known_tags( table->FlagByte & 0x3f );
+ if ( !table->Tag )
+ {
+ FT_ERROR(( "woff2_open_font: Unknown table tag." ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+ }
+
+ flags = 0;
+ xform_version = ( table->FlagByte >> 6 ) & 0x03;
+
+ /* 0 means xform for glyph/loca, non-0 for others. */
+ if ( table->Tag == TTAG_glyf || table->Tag == TTAG_loca )
+ {
+ if ( xform_version == 0 )
+ flags |= WOFF2_FLAGS_TRANSFORM;
+ }
+ else if ( xform_version != 0 )
+ flags |= WOFF2_FLAGS_TRANSFORM;
+
+ flags |= xform_version;
+
+ if ( READ_BASE128( table->dst_length ) )
+ goto Exit;
+
+ table->TransformLength = table->dst_length;
+
+ if ( ( flags & WOFF2_FLAGS_TRANSFORM ) != 0 )
+ {
+ if ( READ_BASE128( table->TransformLength ) )
+ goto Exit;
+
+ if ( table->Tag == TTAG_loca && table->TransformLength )
+ {
+ FT_ERROR(( "woff2_open_font: Invalid loca `transformLength'.\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+ }
+
+ if ( src_offset + table->TransformLength < src_offset )
+ {
+ FT_ERROR(( "woff2_open_font: invalid WOFF2 table directory.\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ table->flags = flags;
+ table->src_offset = src_offset;
+ table->src_length = table->TransformLength;
+ src_offset += table->TransformLength;
+ table->dst_offset = 0;
+
+ FT_TRACE2(( " %c%c%c%c %08d %08d %08ld %08ld %08ld\n",
+ (FT_Char)( table->Tag >> 24 ),
+ (FT_Char)( table->Tag >> 16 ),
+ (FT_Char)( table->Tag >> 8 ),
+ (FT_Char)( table->Tag ),
+ table->FlagByte & 0x3f,
+ ( table->FlagByte >> 6 ) & 0x03,
+ table->dst_length,
+ table->TransformLength,
+ table->src_offset ));
+
+ indices[nn] = table;
+ }
+
+ /* End of last table is uncompressed size. */
+ last_table = indices[woff2.num_tables - 1];
+
+ woff2.uncompressed_size = last_table->src_offset +
+ last_table->src_length;
+ if ( woff2.uncompressed_size < last_table->src_offset )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ FT_TRACE2(( "Table directory parsed.\n" ));
+
+ /* Check for and read collection directory. */
+ woff2.num_fonts = 1;
+ woff2.header_version = 0;
+
+ if ( woff2.flavor == TTAG_ttcf )
+ {
+ FT_TRACE2(( "Font is a TTC, reading collection directory.\n" ));
+
+ if ( FT_READ_ULONG( woff2.header_version ) )
+ goto Exit;
+
+ if ( woff2.header_version != 0x00010000 &&
+ woff2.header_version != 0x00020000 )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ if ( READ_255USHORT( woff2.num_fonts ) )
+ goto Exit;
+
+ if ( !woff2.num_fonts )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ FT_TRACE4(( "Number of fonts in TTC: %d\n", woff2.num_fonts ));
+
+ /* pre-zero pointers within in case of failure */
+ if ( FT_NEW_ARRAY( woff2.ttc_fonts, woff2.num_fonts ) )
+ goto Exit;
+
+ for ( nn = 0; nn < woff2.num_fonts; nn++ )
+ {
+ WOFF2_TtcFont ttc_font = woff2.ttc_fonts + nn;
+
+
+ if ( READ_255USHORT( ttc_font->num_tables ) )
+ goto Exit;
+ if ( FT_READ_ULONG( ttc_font->flavor ) )
+ goto Exit;
+
+ if ( FT_QNEW_ARRAY( ttc_font->table_indices, ttc_font->num_tables ) )
+ goto Exit;
+
+ FT_TRACE5(( "Number of tables in font %d: %d\n",
+ nn, ttc_font->num_tables ));
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( ttc_font->num_tables )
+ FT_TRACE6(( " Indices: " ));
+#endif
+
+ glyf_index = 0;
+ loca_index = 0;
+
+ for ( j = 0; j < ttc_font->num_tables; j++ )
+ {
+ FT_UShort table_index;
+ WOFF2_Table table;
+
+
+ if ( READ_255USHORT( table_index ) )
+ goto Exit;
+
+ FT_TRACE6(( "%hu ", table_index ));
+ if ( table_index >= woff2.num_tables )
+ {
+ FT_ERROR(( "woff2_open_font: invalid table index\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ ttc_font->table_indices[j] = table_index;
+
+ table = indices[table_index];
+ if ( table->Tag == TTAG_loca )
+ loca_index = table_index;
+ if ( table->Tag == TTAG_glyf )
+ glyf_index = table_index;
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( ttc_font->num_tables )
+ FT_TRACE6(( "\n" ));
+#endif
+
+ /* glyf and loca must be consecutive */
+ if ( glyf_index > 0 || loca_index > 0 )
+ {
+ if ( glyf_index > loca_index ||
+ loca_index - glyf_index != 1 )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+ }
+ }
+
+ /* Collection directory reading complete. */
+ FT_TRACE2(( "WOFF2 collection directory is valid.\n" ));
+ }
+ else
+ woff2.ttc_fonts = NULL;
+
+ woff2.compressed_offset = FT_STREAM_POS();
+ file_offset = ROUND4( woff2.compressed_offset +
+ woff2.totalCompressedSize );
+
+ /* Some more checks before we start reading the tables. */
+ if ( file_offset > woff2.length )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ if ( woff2.metaOffset )
+ {
+ if ( file_offset != woff2.metaOffset )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+ file_offset = ROUND4( woff2.metaOffset + woff2.metaLength );
+ }
+
+ if ( woff2.privOffset )
+ {
+ if ( file_offset != woff2.privOffset )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+ file_offset = ROUND4( woff2.privOffset + woff2.privLength );
+ }
+
+ if ( file_offset != ( ROUND4( woff2.length ) ) )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ /* Validate requested face index. */
+ *num_faces = woff2.num_fonts;
+ /* value -(N+1) requests information on index N */
+ if ( *face_instance_index < 0 && face_index > 0 )
+ face_index--;
+
+ if ( face_index >= woff2.num_fonts )
+ {
+ if ( *face_instance_index >= 0 )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+ else
+ face_index = 0;
+ }
+
+ /* Only retain tables of the requested face in a TTC. */
+ if ( woff2.header_version )
+ {
+ WOFF2_TtcFont ttc_font = woff2.ttc_fonts + face_index;
+
+
+ /* Create a temporary array. */
+ if ( FT_QNEW_ARRAY( temp_indices,
+ ttc_font->num_tables ) )
+ goto Exit;
+
+ FT_TRACE4(( "Storing tables for TTC face index %d.\n", face_index ));
+ for ( nn = 0; nn < ttc_font->num_tables; nn++ )
+ temp_indices[nn] = indices[ttc_font->table_indices[nn]];
+
+ /* Resize array to required size. */
+ if ( FT_QRENEW_ARRAY( indices,
+ woff2.num_tables,
+ ttc_font->num_tables ) )
+ goto Exit;
+
+ for ( nn = 0; nn < ttc_font->num_tables; nn++ )
+ indices[nn] = temp_indices[nn];
+
+ FT_FREE( temp_indices );
+
+ /* Change header values. */
+ woff2.flavor = ttc_font->flavor;
+ woff2.num_tables = ttc_font->num_tables;
+ }
+
+ /* We need to allocate this much at the minimum. */
+ sfnt_size = 12 + woff2.num_tables * 16UL;
+ /* This is what we normally expect. */
+ /* Initially trust `totalSfntSize' and change later as required. */
+ if ( woff2.totalSfntSize > sfnt_size )
+ {
+ /* However, adjust the value to something reasonable. */
+
+ /* Factor 64 is heuristic. */
+ if ( ( woff2.totalSfntSize >> 6 ) > woff2.length )
+ sfnt_size = woff2.length << 6;
+ else
+ sfnt_size = woff2.totalSfntSize;
+
+ /* Value 1<<26 = 67108864 is heuristic. */
+ if (sfnt_size >= (1 << 26))
+ sfnt_size = 1 << 26;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( sfnt_size != woff2.totalSfntSize )
+ FT_TRACE4(( "adjusting estimate of uncompressed font size"
+ " to %lu bytes\n",
+ sfnt_size ));
+#endif
+ }
+
+ /* Write sfnt header. */
+ if ( FT_QALLOC( sfnt, sfnt_size ) ||
+ FT_NEW( sfnt_stream ) )
+ goto Exit;
+
+ sfnt_header = sfnt;
+
+ WRITE_ULONG( sfnt_header, woff2.flavor );
+
+ if ( woff2.num_tables )
+ {
+ FT_UInt searchRange, entrySelector, rangeShift, x;
+
+
+ x = woff2.num_tables;
+ entrySelector = 0;
+ while ( x )
+ {
+ x >>= 1;
+ entrySelector += 1;
+ }
+ entrySelector--;
+
+ searchRange = ( 1 << entrySelector ) * 16;
+ rangeShift = ( woff2.num_tables * 16 ) - searchRange;
+
+ WRITE_USHORT( sfnt_header, woff2.num_tables );
+ WRITE_USHORT( sfnt_header, searchRange );
+ WRITE_USHORT( sfnt_header, entrySelector );
+ WRITE_USHORT( sfnt_header, rangeShift );
+ }
+
+ info.header_checksum = compute_ULong_sum( sfnt, 12 );
+
+ /* Sort tables by tag. */
+ ft_qsort( indices,
+ woff2.num_tables,
+ sizeof ( WOFF2_Table ),
+ compare_tags );
+
+ /* reject fonts that have multiple tables with the same tag */
+ for ( nn = 1; nn < woff2.num_tables; nn++ )
+ {
+ FT_Tag tag = indices[nn]->Tag;
+
+
+ if ( tag == indices[nn - 1]->Tag )
+ {
+ FT_ERROR(( "woff2_open_font:"
+ " multiple tables with tag `%c%c%c%c'.\n",
+ (FT_Char)( tag >> 24 ),
+ (FT_Char)( tag >> 16 ),
+ (FT_Char)( tag >> 8 ),
+ (FT_Char)( tag ) ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+ }
+
+ if ( woff2.uncompressed_size < 1 )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ if ( woff2.uncompressed_size > sfnt_size )
+ {
+ FT_ERROR(( "woff2_open_font: SFNT table lengths are too large.\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ /* Allocate memory for uncompressed table data. */
+ if ( FT_QALLOC( uncompressed_buf, woff2.uncompressed_size ) ||
+ FT_FRAME_ENTER( woff2.totalCompressedSize ) )
+ goto Exit;
+
+ /* Uncompress the stream. */
+ error = woff2_decompress( uncompressed_buf,
+ woff2.uncompressed_size,
+ stream->cursor,
+ woff2.totalCompressedSize );
+
+ FT_FRAME_EXIT();
+
+ if ( error )
+ goto Exit;
+
+ error = reconstruct_font( uncompressed_buf,
+ woff2.uncompressed_size,
+ indices,
+ &woff2,
+ &info,
+ &sfnt,
+ &sfnt_size,
+ memory );
+
+ if ( error )
+ goto Exit;
+
+ /* Resize `sfnt' to actual size of sfnt stream. */
+ if ( woff2.actual_sfnt_size < sfnt_size )
+ {
+ FT_TRACE5(( "Trimming sfnt stream from %lu to %lu.\n",
+ sfnt_size, woff2.actual_sfnt_size ));
+ if ( FT_QREALLOC( sfnt,
+ (FT_ULong)( sfnt_size ),
+ (FT_ULong)( woff2.actual_sfnt_size ) ) )
+ goto Exit;
+ }
+
+ /* `reconstruct_font' has done all the work. */
+ /* Swap out stream and return. */
+ FT_Stream_OpenMemory( sfnt_stream, sfnt, woff2.actual_sfnt_size );
+ sfnt_stream->memory = stream->memory;
+ sfnt_stream->close = stream_close;
+
+ FT_Stream_Free(
+ face->root.stream,
+ ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 );
+
+ face->root.stream = sfnt_stream;
+ face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
+
+ /* Set face_index to 0 or -1. */
+ if ( *face_instance_index >= 0 )
+ *face_instance_index = 0;
+ else
+ *face_instance_index = -1;
+
+ FT_TRACE2(( "woff2_open_font: SFNT synthesized.\n" ));
+
+ Exit:
+ FT_FREE( tables );
+ FT_FREE( indices );
+ FT_FREE( uncompressed_buf );
+ FT_FREE( info.x_mins );
+
+ if ( woff2.ttc_fonts )
+ {
+ WOFF2_TtcFont ttc_font = woff2.ttc_fonts;
+
+
+ for ( nn = 0; nn < woff2.num_fonts; nn++ )
+ {
+ FT_FREE( ttc_font->table_indices );
+ ttc_font++;
+ }
+
+ FT_FREE( woff2.ttc_fonts );
+ }
+
+ if ( error )
+ {
+ FT_FREE( sfnt );
+ if ( sfnt_stream )
+ {
+ FT_Stream_Close( sfnt_stream );
+ FT_FREE( sfnt_stream );
+ }
+ }
+
+ return error;
+ }
+
+
+#undef READ_255USHORT
+#undef READ_BASE128
+#undef ROUND4
+#undef WRITE_USHORT
+#undef WRITE_ULONG
+#undef WRITE_SHORT
+#undef WRITE_SFNT_BUF
+#undef WRITE_SFNT_BUF_AT
+
+#undef N_CONTOUR_STREAM
+#undef N_POINTS_STREAM
+#undef FLAG_STREAM
+#undef GLYPH_STREAM
+#undef COMPOSITE_STREAM
+#undef BBOX_STREAM
+#undef INSTRUCTION_STREAM
+
+#else /* !FT_CONFIG_OPTION_USE_BROTLI */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _sfwoff2_dummy;
+
+#endif /* !FT_CONFIG_OPTION_USE_BROTLI */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/sfwoff2.h b/modules/freetype2/src/sfnt/sfwoff2.h
new file mode 100644
index 0000000000..4901286ee0
--- /dev/null
+++ b/modules/freetype2/src/sfnt/sfwoff2.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+ *
+ * sfwoff2.h
+ *
+ * WOFFF2 format management (specification).
+ *
+ * Copyright (C) 2019-2023 by
+ * Nikhil Ramakrishnan, 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.
+ *
+ */
+
+
+#ifndef SFWOFF2_H_
+#define SFWOFF2_H_
+
+
+#include <freetype/internal/sfnt.h>
+#include <freetype/internal/ftobjs.h>
+
+
+FT_BEGIN_HEADER
+
+#ifdef FT_CONFIG_OPTION_USE_BROTLI
+
+ /* Leave the first byte open to store `flag_byte'. */
+#define WOFF2_FLAGS_TRANSFORM 1 << 8
+
+#define WOFF2_SFNT_HEADER_SIZE 12
+#define WOFF2_SFNT_ENTRY_SIZE 16
+
+ /* Suggested maximum size for output. */
+#define WOFF2_DEFAULT_MAX_SIZE 30 * 1024 * 1024
+
+ /* 98% of Google Fonts have no glyph above 5k bytes. */
+#define WOFF2_DEFAULT_GLYPH_BUF 5120
+
+ /* Composite glyph flags. */
+ /* See `CompositeGlyph.java' in `sfntly' for full definitions. */
+#define FLAG_ARG_1_AND_2_ARE_WORDS 1 << 0
+#define FLAG_WE_HAVE_A_SCALE 1 << 3
+#define FLAG_MORE_COMPONENTS 1 << 5
+#define FLAG_WE_HAVE_AN_X_AND_Y_SCALE 1 << 6
+#define FLAG_WE_HAVE_A_TWO_BY_TWO 1 << 7
+#define FLAG_WE_HAVE_INSTRUCTIONS 1 << 8
+
+ /* Simple glyph flags */
+#define GLYF_ON_CURVE 1 << 0
+#define GLYF_X_SHORT 1 << 1
+#define GLYF_Y_SHORT 1 << 2
+#define GLYF_REPEAT 1 << 3
+#define GLYF_THIS_X_IS_SAME 1 << 4
+#define GLYF_THIS_Y_IS_SAME 1 << 5
+#define GLYF_OVERLAP_SIMPLE 1 << 6
+
+ /* Other constants */
+#define CONTOUR_OFFSET_END_POINT 10
+
+
+ FT_LOCAL( FT_Error )
+ woff2_open_font( FT_Stream stream,
+ TT_Face face,
+ FT_Int* face_index,
+ FT_Long* num_faces );
+
+#endif /* FT_CONFIG_OPTION_USE_BROTLI */
+
+FT_END_HEADER
+
+#endif /* SFWOFF2_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttbdf.c b/modules/freetype2/src/sfnt/ttbdf.c
new file mode 100644
index 0000000000..118f475e7f
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttbdf.c
@@ -0,0 +1,256 @@
+/****************************************************************************
+ *
+ * ttbdf.c
+ *
+ * TrueType and OpenType embedded BDF properties (body).
+ *
+ * Copyright (C) 2005-2023 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 <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+#include "ttbdf.h"
+
+#include "sferrors.h"
+
+
+#ifdef TT_CONFIG_OPTION_BDF
+
+ /**************************************************************************
+ *
+ * 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 ttbdf
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_bdf_props( TT_Face face )
+ {
+ TT_BDF bdf = &face->bdf;
+
+
+ if ( bdf->loaded )
+ {
+ FT_Stream stream = FT_FACE( face )->stream;
+
+
+ if ( bdf->table )
+ FT_FRAME_RELEASE( bdf->table );
+
+ bdf->table_end = NULL;
+ bdf->strings = NULL;
+ bdf->strings_size = 0;
+ }
+ }
+
+
+ static FT_Error
+ tt_face_load_bdf_props( TT_Face face,
+ FT_Stream stream )
+ {
+ TT_BDF bdf = &face->bdf;
+ FT_ULong length;
+ FT_Error error;
+
+
+ FT_ZERO( bdf );
+
+ error = tt_face_goto_table( face, TTAG_BDF, stream, &length );
+ if ( error ||
+ length < 8 ||
+ FT_FRAME_EXTRACT( length, bdf->table ) )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ bdf->table_end = bdf->table + length;
+
+ {
+ FT_Byte* p = bdf->table;
+ FT_UInt version = FT_NEXT_USHORT( p );
+ FT_UInt num_strikes = FT_NEXT_USHORT( p );
+ FT_ULong strings = FT_NEXT_ULONG ( p );
+ FT_UInt count;
+ FT_Byte* strike;
+
+
+ if ( version != 0x0001 ||
+ strings < 8 ||
+ ( strings - 8 ) / 4 < num_strikes ||
+ strings + 1 > length )
+ {
+ goto BadTable;
+ }
+
+ bdf->num_strikes = num_strikes;
+ bdf->strings = bdf->table + strings;
+ bdf->strings_size = length - strings;
+
+ count = bdf->num_strikes;
+ p = bdf->table + 8;
+ strike = p + count * 4;
+
+
+ for ( ; count > 0; count-- )
+ {
+ FT_UInt num_items = FT_PEEK_USHORT( p + 2 );
+
+ /*
+ * We don't need to check the value sets themselves, since this
+ * is done later.
+ */
+ strike += 10 * num_items;
+
+ p += 4;
+ }
+
+ if ( strike > bdf->strings )
+ goto BadTable;
+ }
+
+ bdf->loaded = 1;
+
+ Exit:
+ return error;
+
+ BadTable:
+ FT_FRAME_RELEASE( bdf->table );
+ FT_ZERO( bdf );
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_find_bdf_prop( TT_Face face,
+ const char* property_name,
+ BDF_PropertyRec *aprop )
+ {
+ TT_BDF bdf = &face->bdf;
+ FT_Size size = FT_FACE( face )->size;
+ FT_Error error = FT_Err_Ok;
+ FT_Byte* p;
+ FT_UInt count;
+ FT_Byte* strike;
+ FT_Offset property_len;
+
+
+ aprop->type = BDF_PROPERTY_TYPE_NONE;
+
+ if ( bdf->loaded == 0 )
+ {
+ error = tt_face_load_bdf_props( face, FT_FACE( face )->stream );
+ if ( error )
+ goto Exit;
+ }
+
+ count = bdf->num_strikes;
+ p = bdf->table + 8;
+ strike = p + 4 * count;
+
+ error = FT_ERR( Invalid_Argument );
+
+ if ( !size || !property_name )
+ goto Exit;
+
+ property_len = ft_strlen( property_name );
+ if ( property_len == 0 )
+ goto Exit;
+
+ for ( ; count > 0; count-- )
+ {
+ FT_UInt _ppem = FT_NEXT_USHORT( p );
+ FT_UInt _count = FT_NEXT_USHORT( p );
+
+
+ if ( _ppem == size->metrics.y_ppem )
+ {
+ count = _count;
+ goto FoundStrike;
+ }
+
+ strike += 10 * _count;
+ }
+ goto Exit;
+
+ FoundStrike:
+ p = strike;
+ for ( ; count > 0; count-- )
+ {
+ FT_UInt type = FT_PEEK_USHORT( p + 4 );
+
+
+ if ( ( type & 0x10 ) != 0 )
+ {
+ FT_UInt32 name_offset = FT_PEEK_ULONG( p );
+ FT_UInt32 value = FT_PEEK_ULONG( p + 6 );
+
+ /* be a bit paranoid for invalid entries here */
+ if ( name_offset < bdf->strings_size &&
+ property_len < bdf->strings_size - name_offset &&
+ ft_strncmp( property_name,
+ (const char*)bdf->strings + name_offset,
+ bdf->strings_size - name_offset ) == 0 )
+ {
+ switch ( type & 0x0F )
+ {
+ case 0x00: /* string */
+ case 0x01: /* atoms */
+ /* check that the content is really 0-terminated */
+ if ( value < bdf->strings_size &&
+ ft_memchr( bdf->strings + value, 0, bdf->strings_size ) )
+ {
+ aprop->type = BDF_PROPERTY_TYPE_ATOM;
+ aprop->u.atom = (const char*)bdf->strings + value;
+ error = FT_Err_Ok;
+ goto Exit;
+ }
+ break;
+
+ case 0x02:
+ aprop->type = BDF_PROPERTY_TYPE_INTEGER;
+ aprop->u.integer = (FT_Int32)value;
+ error = FT_Err_Ok;
+ goto Exit;
+
+ case 0x03:
+ aprop->type = BDF_PROPERTY_TYPE_CARDINAL;
+ aprop->u.cardinal = value;
+ error = FT_Err_Ok;
+ goto Exit;
+
+ default:
+ ;
+ }
+ }
+ }
+ p += 10;
+ }
+
+ Exit:
+ return error;
+ }
+
+#else /* !TT_CONFIG_OPTION_BDF */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _tt_bdf_dummy;
+
+#endif /* !TT_CONFIG_OPTION_BDF */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttbdf.h b/modules/freetype2/src/sfnt/ttbdf.h
new file mode 100644
index 0000000000..595aeb76c2
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttbdf.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+ *
+ * ttbdf.h
+ *
+ * TrueType and OpenType embedded BDF properties (specification).
+ *
+ * Copyright (C) 2005-2023 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.
+ *
+ */
+
+
+#ifndef TTBDF_H_
+#define TTBDF_H_
+
+
+#include "ttload.h"
+#include <freetype/ftbdf.h>
+
+
+FT_BEGIN_HEADER
+
+
+#ifdef TT_CONFIG_OPTION_BDF
+
+ FT_LOCAL( void )
+ tt_face_free_bdf_props( TT_Face face );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_find_bdf_prop( TT_Face face,
+ const char* property_name,
+ BDF_PropertyRec *aprop );
+
+#endif /* TT_CONFIG_OPTION_BDF */
+
+
+FT_END_HEADER
+
+#endif /* TTBDF_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttcmap.c b/modules/freetype2/src/sfnt/ttcmap.c
new file mode 100644
index 0000000000..820cd08e6d
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttcmap.c
@@ -0,0 +1,3897 @@
+/****************************************************************************
+ *
+ * ttcmap.c
+ *
+ * TrueType character mapping table (cmap) support (body).
+ *
+ * Copyright (C) 2002-2023 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 <freetype/internal/ftdebug.h>
+
+#include "sferrors.h" /* must come before `ftvalid.h' */
+
+#include <freetype/internal/ftvalid.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/services/svpscmap.h>
+#include "ttload.h"
+#include "ttcmap.h"
+#include "ttpost.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 ttcmap
+
+
+#define TT_PEEK_SHORT FT_PEEK_SHORT
+#define TT_PEEK_USHORT FT_PEEK_USHORT
+#define TT_PEEK_UINT24 FT_PEEK_UOFF3
+#define TT_PEEK_LONG FT_PEEK_LONG
+#define TT_PEEK_ULONG FT_PEEK_ULONG
+
+#define TT_NEXT_SHORT FT_NEXT_SHORT
+#define TT_NEXT_USHORT FT_NEXT_USHORT
+#define TT_NEXT_UINT24 FT_NEXT_UOFF3
+#define TT_NEXT_LONG FT_NEXT_LONG
+#define TT_NEXT_ULONG FT_NEXT_ULONG
+
+
+ /* Too large glyph index return values are caught in `FT_Get_Char_Index' */
+ /* and `FT_Get_Next_Char' (the latter calls the internal `next' function */
+ /* again in this case). To mark character code return values as invalid */
+ /* it is sufficient to set the corresponding glyph index return value to */
+ /* zero. */
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap_init( TT_CMap cmap,
+ FT_Byte* table )
+ {
+ cmap->data = table;
+ return FT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 0 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 0
+ * length 2 USHORT table length in bytes
+ * language 4 USHORT Mac language code
+ * glyph_ids 6 BYTE[256] array of glyph indices
+ * 262
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_0
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap0_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_UInt length;
+
+
+ if ( table + 2 + 2 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 2; /* skip format */
+ length = TT_NEXT_USHORT( p );
+
+ if ( table + length > valid->limit || length < 262 )
+ FT_INVALID_TOO_SHORT;
+
+ /* check glyph indices whenever necessary */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt n, idx;
+
+
+ p = table + 6;
+ for ( n = 0; n < 256; n++ )
+ {
+ idx = *p++;
+ if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap0_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+
+
+ return char_code < 256 ? table[6 + char_code] : 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap0_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt32 charcode = *pchar_code;
+ FT_UInt32 result = 0;
+ FT_UInt gindex = 0;
+
+
+ table += 6; /* go to glyph IDs */
+ while ( ++charcode < 256 )
+ {
+ gindex = table[charcode];
+ if ( gindex != 0 )
+ {
+ result = charcode;
+ break;
+ }
+ }
+
+ *pchar_code = result;
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap0_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 4;
+
+
+ cmap_info->format = 0;
+ cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap0_class_rec,
+
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init, /* init */
+ (FT_CMap_DoneFunc) NULL, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap0_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap0_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ 0,
+ (TT_CMap_ValidateFunc)tt_cmap0_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap0_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_0 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 2 *****/
+ /***** *****/
+ /***** This is used for certain CJK encodings that encode text in a *****/
+ /***** mixed 8/16 bits encoding along the following lines. *****/
+ /***** *****/
+ /***** * Certain byte values correspond to an 8-bit character code *****/
+ /***** (typically in the range 0..127 for ASCII compatibility). *****/
+ /***** *****/
+ /***** * Certain byte values signal the first byte of a 2-byte *****/
+ /***** character code (but these values are also valid as the *****/
+ /***** second byte of a 2-byte character). *****/
+ /***** *****/
+ /***** The following charmap lookup and iteration functions all *****/
+ /***** assume that the value `charcode' fulfills the following. *****/
+ /***** *****/
+ /***** - For one-byte characters, `charcode' is simply the *****/
+ /***** character code. *****/
+ /***** *****/
+ /***** - For two-byte characters, `charcode' is the 2-byte *****/
+ /***** character code in big endian format. More precisely: *****/
+ /***** *****/
+ /***** (charcode >> 8) is the first byte value *****/
+ /***** (charcode & 0xFF) is the second byte value *****/
+ /***** *****/
+ /***** Note that not all values of `charcode' are valid according *****/
+ /***** to these rules, and the function moderately checks the *****/
+ /***** arguments. *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 2
+ * length 2 USHORT table length in bytes
+ * language 4 USHORT Mac language code
+ * keys 6 USHORT[256] sub-header keys
+ * subs 518 SUBHEAD[NSUBS] sub-headers array
+ * glyph_ids 518+NSUB*8 USHORT[] glyph ID array
+ *
+ * The `keys' table is used to map charcode high bytes to sub-headers.
+ * The value of `NSUBS' is the number of sub-headers defined in the
+ * table and is computed by finding the maximum of the `keys' table.
+ *
+ * Note that for any `n', `keys[n]' is a byte offset within the `subs'
+ * table, i.e., it is the corresponding sub-header index multiplied
+ * by 8.
+ *
+ * Each sub-header has the following format.
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * first 0 USHORT first valid low-byte
+ * count 2 USHORT number of valid low-bytes
+ * delta 4 SHORT see below
+ * offset 6 USHORT see below
+ *
+ * A sub-header defines, for each high byte, the range of valid
+ * low bytes within the charmap. Note that the range defined by `first'
+ * and `count' must be completely included in the interval [0..255]
+ * according to the specification.
+ *
+ * If a character code is contained within a given sub-header, then
+ * mapping it to a glyph index is done as follows.
+ *
+ * - The value of `offset' is read. This is a _byte_ distance from the
+ * location of the `offset' field itself into a slice of the
+ * `glyph_ids' table. Let's call it `slice' (it is a USHORT[], too).
+ *
+ * - The value `slice[char.lo - first]' is read. If it is 0, there is
+ * no glyph for the charcode. Otherwise, the value of `delta' is
+ * added to it (modulo 65536) to form a new glyph index.
+ *
+ * It is up to the validation routine to check that all offsets fall
+ * within the glyph IDs table (and not within the `subs' table itself or
+ * outside of the CMap).
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_2
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap2_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_UInt length;
+
+ FT_UInt n, max_subs;
+ FT_Byte* keys; /* keys table */
+ FT_Byte* subs; /* sub-headers */
+ FT_Byte* glyph_ids; /* glyph ID array */
+
+
+ if ( table + 2 + 2 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 2; /* skip format */
+ length = TT_NEXT_USHORT( p );
+
+ if ( table + length > valid->limit || length < 6 + 512 )
+ FT_INVALID_TOO_SHORT;
+
+ keys = table + 6;
+
+ /* parse keys to compute sub-headers count */
+ p = keys;
+ max_subs = 0;
+ for ( n = 0; n < 256; n++ )
+ {
+ FT_UInt idx = TT_NEXT_USHORT( p );
+
+
+ /* value must be multiple of 8 */
+ if ( valid->level >= FT_VALIDATE_PARANOID && ( idx & 7 ) != 0 )
+ FT_INVALID_DATA;
+
+ idx >>= 3;
+
+ if ( idx > max_subs )
+ max_subs = idx;
+ }
+
+ FT_ASSERT( p == table + 518 );
+
+ subs = p;
+ glyph_ids = subs + ( max_subs + 1 ) * 8;
+ if ( glyph_ids > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ /* parse sub-headers */
+ for ( n = 0; n <= max_subs; n++ )
+ {
+ FT_UInt first_code, code_count, offset;
+ FT_Int delta;
+
+
+ first_code = TT_NEXT_USHORT( p );
+ code_count = TT_NEXT_USHORT( p );
+ delta = TT_NEXT_SHORT( p );
+ offset = TT_NEXT_USHORT( p );
+
+ /* many Dynalab fonts have empty sub-headers */
+ if ( code_count == 0 )
+ continue;
+
+ /* check range within 0..255 */
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ {
+ if ( first_code >= 256 || code_count > 256 - first_code )
+ FT_INVALID_DATA;
+ }
+
+ /* check offset */
+ if ( offset != 0 )
+ {
+ FT_Byte* ids;
+
+
+ ids = p - 2 + offset;
+ if ( ids < glyph_ids || ids + code_count * 2 > table + length )
+ FT_INVALID_OFFSET;
+
+ /* check glyph IDs */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_Byte* limit = p + code_count * 2;
+ FT_UInt idx;
+
+
+ for ( ; p < limit; )
+ {
+ idx = TT_NEXT_USHORT( p );
+ if ( idx != 0 )
+ {
+ idx = (FT_UInt)( (FT_Int)idx + delta ) & 0xFFFFU;
+ if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+ }
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ /* return sub header corresponding to a given character code */
+ /* NULL on invalid charcode */
+ static FT_Byte*
+ tt_cmap2_get_subheader( FT_Byte* table,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* result = NULL;
+
+
+ if ( char_code < 0x10000UL )
+ {
+ FT_UInt char_lo = (FT_UInt)( char_code & 0xFF );
+ FT_UInt char_hi = (FT_UInt)( char_code >> 8 );
+ FT_Byte* p = table + 6; /* keys table */
+ FT_Byte* subs = table + 518; /* subheaders table */
+ FT_Byte* sub;
+
+
+ if ( char_hi == 0 )
+ {
+ /* an 8-bit character code -- we use subHeader 0 in this case */
+ /* to test whether the character code is in the charmap */
+ /* */
+ sub = subs; /* jump to first sub-header */
+
+ /* check that the sub-header for this byte is 0, which */
+ /* indicates that it is really a valid one-byte value; */
+ /* otherwise, return 0 */
+ /* */
+ p += char_lo * 2;
+ if ( TT_PEEK_USHORT( p ) != 0 )
+ goto Exit;
+ }
+ else
+ {
+ /* a 16-bit character code */
+
+ /* jump to key entry */
+ p += char_hi * 2;
+ /* jump to sub-header */
+ sub = subs + ( FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 8 ) );
+
+ /* check that the high byte isn't a valid one-byte value */
+ if ( sub == subs )
+ goto Exit;
+ }
+
+ result = sub;
+ }
+
+ Exit:
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap2_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt result = 0;
+ FT_Byte* subheader;
+
+
+ subheader = tt_cmap2_get_subheader( table, char_code );
+ if ( subheader )
+ {
+ FT_Byte* p = subheader;
+ FT_UInt idx = (FT_UInt)( char_code & 0xFF );
+ FT_UInt start, count;
+ FT_Int delta;
+ FT_UInt offset;
+
+
+ start = TT_NEXT_USHORT( p );
+ count = TT_NEXT_USHORT( p );
+ delta = TT_NEXT_SHORT ( p );
+ offset = TT_PEEK_USHORT( p );
+
+ idx -= start;
+ if ( idx < count && offset != 0 )
+ {
+ p += offset + 2 * idx;
+ idx = TT_PEEK_USHORT( p );
+
+ if ( idx != 0 )
+ result = (FT_UInt)( (FT_Int)idx + delta ) & 0xFFFFU;
+ }
+ }
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap2_char_next( TT_CMap cmap,
+ FT_UInt32 *pcharcode )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt gindex = 0;
+ FT_UInt32 result = 0;
+ FT_UInt32 charcode = *pcharcode + 1;
+ FT_Byte* subheader;
+
+
+ while ( charcode < 0x10000UL )
+ {
+ subheader = tt_cmap2_get_subheader( table, charcode );
+ if ( subheader )
+ {
+ FT_Byte* p = subheader;
+ FT_UInt start = TT_NEXT_USHORT( p );
+ FT_UInt count = TT_NEXT_USHORT( p );
+ FT_Int delta = TT_NEXT_SHORT ( p );
+ FT_UInt offset = TT_PEEK_USHORT( p );
+ FT_UInt char_lo = (FT_UInt)( charcode & 0xFF );
+ FT_UInt pos, idx;
+
+
+ if ( char_lo >= start + count && charcode <= 0xFF )
+ {
+ /* this happens only for a malformed cmap */
+ charcode = 0x100;
+ continue;
+ }
+
+ if ( offset == 0 )
+ {
+ if ( charcode == 0x100 )
+ goto Exit; /* this happens only for a malformed cmap */
+ goto Next_SubHeader;
+ }
+
+ if ( char_lo < start )
+ {
+ char_lo = start;
+ pos = 0;
+ }
+ else
+ pos = (FT_UInt)( char_lo - start );
+
+ p += offset + pos * 2;
+ charcode = FT_PAD_FLOOR( charcode, 256 ) + char_lo;
+
+ for ( ; pos < count; pos++, charcode++ )
+ {
+ idx = TT_NEXT_USHORT( p );
+
+ if ( idx != 0 )
+ {
+ gindex = (FT_UInt)( (FT_Int)idx + delta ) & 0xFFFFU;
+ if ( gindex != 0 )
+ {
+ result = charcode;
+ goto Exit;
+ }
+ }
+ }
+
+ /* if unsuccessful, avoid `charcode' leaving */
+ /* the current 256-character block */
+ if ( count )
+ charcode--;
+ }
+
+ /* If `charcode' is <= 0xFF, retry with `charcode + 1'. */
+ /* Otherwise jump to the next 256-character block and retry. */
+ Next_SubHeader:
+ if ( charcode <= 0xFF )
+ charcode++;
+ else
+ charcode = FT_PAD_FLOOR( charcode, 0x100 ) + 0x100;
+ }
+
+ Exit:
+ *pcharcode = result;
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap2_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 4;
+
+
+ cmap_info->format = 2;
+ cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap2_class_rec,
+
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init, /* init */
+ (FT_CMap_DoneFunc) NULL, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap2_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap2_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ 2,
+ (TT_CMap_ValidateFunc)tt_cmap2_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap2_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_2 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 4 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 4
+ * length 2 USHORT table length
+ * in bytes
+ * language 4 USHORT Mac language code
+ *
+ * segCountX2 6 USHORT 2*NUM_SEGS
+ * searchRange 8 USHORT 2*(1 << LOG_SEGS)
+ * entrySelector 10 USHORT LOG_SEGS
+ * rangeShift 12 USHORT segCountX2 -
+ * searchRange
+ *
+ * endCount 14 USHORT[NUM_SEGS] end charcode for
+ * each segment; last
+ * is 0xFFFF
+ *
+ * pad 14+NUM_SEGS*2 USHORT padding
+ *
+ * startCount 16+NUM_SEGS*2 USHORT[NUM_SEGS] first charcode for
+ * each segment
+ *
+ * idDelta 16+NUM_SEGS*4 SHORT[NUM_SEGS] delta for each
+ * segment
+ * idOffset 16+NUM_SEGS*6 SHORT[NUM_SEGS] range offset for
+ * each segment; can be
+ * zero
+ *
+ * glyphIds 16+NUM_SEGS*8 USHORT[] array of glyph ID
+ * ranges
+ *
+ * Character codes are modelled by a series of ordered (increasing)
+ * intervals called segments. Each segment has start and end codes,
+ * provided by the `startCount' and `endCount' arrays. Segments must
+ * not overlap, and the last segment should always contain the value
+ * 0xFFFF for `endCount'.
+ *
+ * The fields `searchRange', `entrySelector' and `rangeShift' are better
+ * ignored (they are traces of over-engineering in the TrueType
+ * specification).
+ *
+ * Each segment also has a signed `delta', as well as an optional offset
+ * within the `glyphIds' table.
+ *
+ * If a segment's idOffset is 0, the glyph index corresponding to any
+ * charcode within the segment is obtained by adding the value of
+ * `idDelta' directly to the charcode, modulo 65536.
+ *
+ * Otherwise, a glyph index is taken from the glyph IDs sub-array for
+ * the segment, and the value of `idDelta' is added to it.
+ *
+ *
+ * Finally, note that a lot of fonts contain an invalid last segment,
+ * where `start' and `end' are correctly set to 0xFFFF but both `delta'
+ * and `offset' are incorrect (e.g., `opens___.ttf' which comes with
+ * OpenOffice.org). We need special code to deal with them correctly.
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_4
+
+ typedef struct TT_CMap4Rec_
+ {
+ TT_CMapRec cmap;
+ FT_UInt32 cur_charcode; /* current charcode */
+ FT_UInt cur_gindex; /* current glyph index */
+
+ FT_UInt num_ranges;
+ FT_UInt cur_range;
+ FT_UInt cur_start;
+ FT_UInt cur_end;
+ FT_Int cur_delta;
+ FT_Byte* cur_values;
+
+ } TT_CMap4Rec, *TT_CMap4;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap4_init( TT_CMap4 cmap,
+ FT_Byte* table )
+ {
+ FT_Byte* p;
+
+
+ cmap->cmap.data = table;
+
+ p = table + 6;
+ cmap->num_ranges = FT_PEEK_USHORT( p ) >> 1;
+ cmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL;
+ cmap->cur_gindex = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Int
+ tt_cmap4_set_range( TT_CMap4 cmap,
+ FT_UInt range_index )
+ {
+ FT_Byte* table = cmap->cmap.data;
+ FT_Byte* p;
+ FT_UInt num_ranges = cmap->num_ranges;
+
+
+ while ( range_index < num_ranges )
+ {
+ FT_UInt offset;
+
+
+ p = table + 14 + range_index * 2;
+ cmap->cur_end = FT_PEEK_USHORT( p );
+
+ p += 2 + num_ranges * 2;
+ cmap->cur_start = FT_PEEK_USHORT( p );
+
+ p += num_ranges * 2;
+ cmap->cur_delta = FT_PEEK_SHORT( p );
+
+ p += num_ranges * 2;
+ offset = FT_PEEK_USHORT( p );
+
+ /* some fonts have an incorrect last segment; */
+ /* we have to catch it */
+ if ( range_index >= num_ranges - 1 &&
+ cmap->cur_start == 0xFFFFU &&
+ cmap->cur_end == 0xFFFFU )
+ {
+ TT_Face face = (TT_Face)cmap->cmap.cmap.charmap.face;
+ FT_Byte* limit = face->cmap_table + face->cmap_size;
+
+
+ if ( offset && p + offset + 2 > limit )
+ {
+ cmap->cur_delta = 1;
+ offset = 0;
+ }
+ }
+
+ if ( offset != 0xFFFFU )
+ {
+ cmap->cur_values = offset ? p + offset : NULL;
+ cmap->cur_range = range_index;
+ return 0;
+ }
+
+ /* we skip empty segments */
+ range_index++;
+ }
+
+ return -1;
+ }
+
+
+ /* search the index of the charcode next to cmap->cur_charcode; */
+ /* caller should call tt_cmap4_set_range with proper range */
+ /* before calling this function */
+ /* */
+ static void
+ tt_cmap4_next( TT_CMap4 cmap )
+ {
+ TT_Face face = (TT_Face)cmap->cmap.cmap.charmap.face;
+ FT_Byte* limit = face->cmap_table + face->cmap_size;
+
+ FT_UInt charcode;
+
+
+ if ( cmap->cur_charcode >= 0xFFFFUL )
+ goto Fail;
+
+ charcode = (FT_UInt)cmap->cur_charcode + 1;
+
+ if ( charcode < cmap->cur_start )
+ charcode = cmap->cur_start;
+
+ for (;;)
+ {
+ FT_Byte* values = cmap->cur_values;
+ FT_UInt end = cmap->cur_end;
+ FT_Int delta = cmap->cur_delta;
+
+
+ if ( charcode <= end )
+ {
+ if ( values )
+ {
+ FT_Byte* p = values + 2 * ( charcode - cmap->cur_start );
+
+
+ /* if p > limit, the whole segment is invalid */
+ if ( p > limit )
+ goto Next_Segment;
+
+ do
+ {
+ FT_UInt gindex = FT_NEXT_USHORT( p );
+
+
+ if ( gindex )
+ {
+ gindex = (FT_UInt)( (FT_Int)gindex + delta ) & 0xFFFFU;
+ if ( gindex )
+ {
+ cmap->cur_charcode = charcode;
+ cmap->cur_gindex = gindex;
+ return;
+ }
+ }
+ } while ( ++charcode <= end );
+ }
+ else
+ {
+ do
+ {
+ FT_UInt gindex = (FT_UInt)( (FT_Int)charcode + delta ) & 0xFFFFU;
+
+
+ if ( gindex >= (FT_UInt)face->root.num_glyphs )
+ {
+ /* we have an invalid glyph index; if there is an overflow, */
+ /* we can adjust `charcode', otherwise the whole segment is */
+ /* invalid */
+ gindex = 0;
+
+ if ( (FT_Int)charcode + delta < 0 &&
+ (FT_Int)end + delta >= 0 )
+ charcode = (FT_UInt)( -delta );
+
+ else if ( (FT_Int)charcode + delta < 0x10000L &&
+ (FT_Int)end + delta >= 0x10000L )
+ charcode = (FT_UInt)( 0x10000L - delta );
+
+ else
+ goto Next_Segment;
+ }
+
+ if ( gindex )
+ {
+ cmap->cur_charcode = charcode;
+ cmap->cur_gindex = gindex;
+ return;
+ }
+ } while ( ++charcode <= end );
+ }
+ }
+
+ Next_Segment:
+ /* we need to find another range */
+ if ( tt_cmap4_set_range( cmap, cmap->cur_range + 1 ) < 0 )
+ break;
+
+ if ( charcode < cmap->cur_start )
+ charcode = cmap->cur_start;
+ }
+
+ Fail:
+ cmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL;
+ cmap->cur_gindex = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap4_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_UInt length;
+
+ FT_Byte *ends, *starts, *offsets, *deltas, *glyph_ids;
+ FT_UInt num_segs;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( table + 2 + 2 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 2; /* skip format */
+ length = TT_NEXT_USHORT( p );
+
+ /* in certain fonts, the `length' field is invalid and goes */
+ /* out of bound. We try to correct this here... */
+ if ( table + length > valid->limit )
+ {
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ FT_INVALID_TOO_SHORT;
+
+ length = (FT_UInt)( valid->limit - table );
+ }
+
+ /* it also happens that the `length' field is too small; */
+ /* this is easy to correct */
+ if ( length < (FT_UInt)( valid->limit - table ) )
+ {
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+
+ length = (FT_UInt)( valid->limit - table );
+ }
+
+ if ( length < 16 )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 6;
+ num_segs = TT_NEXT_USHORT( p ); /* read segCountX2 */
+
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ {
+ /* check that we have an even value here */
+ if ( num_segs & 1 )
+ FT_INVALID_DATA;
+ }
+
+ num_segs /= 2;
+
+ if ( length < 16 + num_segs * 2 * 4 )
+ FT_INVALID_TOO_SHORT;
+
+ /* check the search parameters - even though we never use them */
+ /* */
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ {
+ /* check the values of `searchRange', `entrySelector', `rangeShift' */
+ FT_UInt search_range = TT_NEXT_USHORT( p );
+ FT_UInt entry_selector = TT_NEXT_USHORT( p );
+ FT_UInt range_shift = TT_NEXT_USHORT( p );
+
+
+ if ( ( search_range | range_shift ) & 1 ) /* must be even values */
+ FT_INVALID_DATA;
+
+ search_range /= 2;
+ range_shift /= 2;
+
+ /* `search range' is the greatest power of 2 that is <= num_segs */
+
+ if ( search_range > num_segs ||
+ search_range * 2 < num_segs ||
+ search_range + range_shift != num_segs ||
+ search_range != ( 1U << entry_selector ) )
+ FT_INVALID_DATA;
+ }
+
+ ends = table + 14;
+ starts = table + 16 + num_segs * 2;
+ deltas = starts + num_segs * 2;
+ offsets = deltas + num_segs * 2;
+ glyph_ids = offsets + num_segs * 2;
+
+ /* check last segment; its end count value must be 0xFFFF */
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ {
+ p = ends + ( num_segs - 1 ) * 2;
+ if ( TT_PEEK_USHORT( p ) != 0xFFFFU )
+ FT_INVALID_DATA;
+ }
+
+ {
+ FT_UInt start, end, offset, n;
+ FT_UInt last_start = 0, last_end = 0;
+ FT_Int delta;
+ FT_Byte* p_start = starts;
+ FT_Byte* p_end = ends;
+ FT_Byte* p_delta = deltas;
+ FT_Byte* p_offset = offsets;
+
+
+ for ( n = 0; n < num_segs; n++ )
+ {
+ p = p_offset;
+ start = TT_NEXT_USHORT( p_start );
+ end = TT_NEXT_USHORT( p_end );
+ delta = TT_NEXT_SHORT( p_delta );
+ offset = TT_NEXT_USHORT( p_offset );
+
+ if ( start > end )
+ FT_INVALID_DATA;
+
+ /* this test should be performed at default validation level; */
+ /* unfortunately, some popular Asian fonts have overlapping */
+ /* ranges in their charmaps */
+ /* */
+ if ( start <= last_end && n > 0 )
+ {
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ FT_INVALID_DATA;
+ else
+ {
+ /* allow overlapping segments, provided their start points */
+ /* and end points, respectively, are in ascending order */
+ /* */
+ if ( last_start > start || last_end > end )
+ error |= TT_CMAP_FLAG_UNSORTED;
+ else
+ error |= TT_CMAP_FLAG_OVERLAPPING;
+ }
+ }
+
+ if ( offset && offset != 0xFFFFU )
+ {
+ p += offset; /* start of glyph ID array */
+
+ /* check that we point within the glyph IDs table only */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ if ( p < glyph_ids ||
+ p + ( end - start + 1 ) * 2 > table + length )
+ FT_INVALID_DATA;
+ }
+ /* Some fonts handle the last segment incorrectly. In */
+ /* theory, 0xFFFF might point to an ordinary glyph -- */
+ /* a cmap 4 is versatile and could be used for any */
+ /* encoding, not only Unicode. However, reality shows */
+ /* that far too many fonts are sloppy and incorrectly */
+ /* set all fields but `start' and `end' for the last */
+ /* segment if it contains only a single character. */
+ /* */
+ /* We thus omit the test here, delaying it to the */
+ /* routines that actually access the cmap. */
+ else if ( n != num_segs - 1 ||
+ !( start == 0xFFFFU && end == 0xFFFFU ) )
+ {
+ if ( p < glyph_ids ||
+ p + ( end - start + 1 ) * 2 > valid->limit )
+ FT_INVALID_DATA;
+ }
+
+ /* check glyph indices within the segment range */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt i, idx;
+
+
+ for ( i = start; i < end; i++ )
+ {
+ idx = FT_NEXT_USHORT( p );
+ if ( idx != 0 )
+ {
+ idx = (FT_UInt)( (FT_Int)idx + delta ) & 0xFFFFU;
+
+ if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+ }
+ }
+ else if ( offset == 0xFFFFU )
+ {
+ /* some fonts (erroneously?) use a range offset of 0xFFFF */
+ /* to mean missing glyph in cmap table */
+ /* */
+ if ( valid->level >= FT_VALIDATE_PARANOID ||
+ n != num_segs - 1 ||
+ !( start == 0xFFFFU && end == 0xFFFFU ) )
+ FT_INVALID_DATA;
+ }
+
+ last_start = start;
+ last_end = end;
+ }
+ }
+
+ return error;
+ }
+
+
+ static FT_UInt
+ tt_cmap4_char_map_linear( TT_CMap cmap,
+ FT_UInt32* pcharcode,
+ FT_Bool next )
+ {
+ TT_Face face = (TT_Face)cmap->cmap.charmap.face;
+ FT_Byte* limit = face->cmap_table + face->cmap_size;
+
+
+ FT_UInt num_segs2, start, end, offset;
+ FT_Int delta;
+ FT_UInt i, num_segs;
+ FT_UInt32 charcode = *pcharcode;
+ FT_UInt gindex = 0;
+ FT_Byte* p;
+ FT_Byte* q;
+
+
+ p = cmap->data + 6;
+ num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 );
+
+ num_segs = num_segs2 >> 1;
+
+ if ( !num_segs )
+ return 0;
+
+ if ( next )
+ charcode++;
+
+ if ( charcode > 0xFFFFU )
+ return 0;
+
+ /* linear search */
+ p = cmap->data + 14; /* ends table */
+ q = cmap->data + 16 + num_segs2; /* starts table */
+
+ for ( i = 0; i < num_segs; i++ )
+ {
+ end = TT_NEXT_USHORT( p );
+ start = TT_NEXT_USHORT( q );
+
+ if ( charcode < start )
+ {
+ if ( next )
+ charcode = start;
+ else
+ break;
+ }
+
+ Again:
+ if ( charcode <= end )
+ {
+ FT_Byte* r;
+
+
+ r = q - 2 + num_segs2;
+ delta = TT_PEEK_SHORT( r );
+ r += num_segs2;
+ offset = TT_PEEK_USHORT( r );
+
+ /* some fonts have an incorrect last segment; */
+ /* we have to catch it */
+ if ( i >= num_segs - 1 &&
+ start == 0xFFFFU && end == 0xFFFFU )
+ {
+ if ( offset && r + offset + 2 > limit )
+ {
+ delta = 1;
+ offset = 0;
+ }
+ }
+
+ if ( offset == 0xFFFFU )
+ continue;
+
+ if ( offset )
+ {
+ r += offset + ( charcode - start ) * 2;
+
+ /* if r > limit, the whole segment is invalid */
+ if ( next && r > limit )
+ continue;
+
+ gindex = TT_PEEK_USHORT( r );
+ if ( gindex )
+ {
+ gindex = (FT_UInt)( (FT_Int)gindex + delta ) & 0xFFFFU;
+ if ( gindex >= (FT_UInt)face->root.num_glyphs )
+ gindex = 0;
+ }
+ }
+ else
+ {
+ gindex = (FT_UInt)( (FT_Int)charcode + delta ) & 0xFFFFU;
+
+ if ( next && gindex >= (FT_UInt)face->root.num_glyphs )
+ {
+ /* we have an invalid glyph index; if there is an overflow, */
+ /* we can adjust `charcode', otherwise the whole segment is */
+ /* invalid */
+ gindex = 0;
+
+ if ( (FT_Int)charcode + delta < 0 &&
+ (FT_Int)end + delta >= 0 )
+ charcode = (FT_UInt)( -delta );
+
+ else if ( (FT_Int)charcode + delta < 0x10000L &&
+ (FT_Int)end + delta >= 0x10000L )
+ charcode = (FT_UInt)( 0x10000L - delta );
+
+ else
+ continue;
+ }
+ }
+
+ if ( next && !gindex )
+ {
+ if ( charcode >= 0xFFFFU )
+ break;
+
+ charcode++;
+ goto Again;
+ }
+
+ break;
+ }
+ }
+
+ if ( next )
+ *pcharcode = charcode;
+
+ return gindex;
+ }
+
+
+ static FT_UInt
+ tt_cmap4_char_map_binary( TT_CMap cmap,
+ FT_UInt32* pcharcode,
+ FT_Bool next )
+ {
+ TT_Face face = (TT_Face)cmap->cmap.charmap.face;
+ FT_Byte* limit = face->cmap_table + face->cmap_size;
+
+ FT_UInt num_segs2, start, end, offset;
+ FT_Int delta;
+ FT_UInt max, min, mid, num_segs;
+ FT_UInt charcode = (FT_UInt)*pcharcode;
+ FT_UInt gindex = 0;
+ FT_Byte* p;
+
+
+ p = cmap->data + 6;
+ num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 );
+
+ if ( !num_segs2 )
+ return 0;
+
+ num_segs = num_segs2 >> 1;
+
+ /* make compiler happy */
+ mid = num_segs;
+ end = 0xFFFFU;
+
+ if ( next )
+ charcode++;
+
+ min = 0;
+ max = num_segs;
+
+ /* binary search */
+ while ( min < max )
+ {
+ mid = ( min + max ) >> 1;
+ p = cmap->data + 14 + mid * 2;
+ end = TT_PEEK_USHORT( p );
+ p += 2 + num_segs2;
+ start = TT_PEEK_USHORT( p );
+
+ if ( charcode < start )
+ max = mid;
+ else if ( charcode > end )
+ min = mid + 1;
+ else
+ {
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+
+ /* some fonts have an incorrect last segment; */
+ /* we have to catch it */
+ if ( mid >= num_segs - 1 &&
+ start == 0xFFFFU && end == 0xFFFFU )
+ {
+ if ( offset && p + offset + 2 > limit )
+ {
+ delta = 1;
+ offset = 0;
+ }
+ }
+
+ /* search the first segment containing `charcode' */
+ if ( cmap->flags & TT_CMAP_FLAG_OVERLAPPING )
+ {
+ FT_UInt i;
+
+
+ /* call the current segment `max' */
+ max = mid;
+
+ if ( offset == 0xFFFFU )
+ mid = max + 1;
+
+ /* search in segments before the current segment */
+ for ( i = max; i > 0; i-- )
+ {
+ FT_UInt prev_end;
+ FT_Byte* old_p;
+
+
+ old_p = p;
+ p = cmap->data + 14 + ( i - 1 ) * 2;
+ prev_end = TT_PEEK_USHORT( p );
+
+ if ( charcode > prev_end )
+ {
+ p = old_p;
+ break;
+ }
+
+ end = prev_end;
+ p += 2 + num_segs2;
+ start = TT_PEEK_USHORT( p );
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+
+ if ( offset != 0xFFFFU )
+ mid = i - 1;
+ }
+
+ /* no luck */
+ if ( mid == max + 1 )
+ {
+ if ( i != max )
+ {
+ p = cmap->data + 14 + max * 2;
+ end = TT_PEEK_USHORT( p );
+ p += 2 + num_segs2;
+ start = TT_PEEK_USHORT( p );
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+ }
+
+ mid = max;
+
+ /* search in segments after the current segment */
+ for ( i = max + 1; i < num_segs; i++ )
+ {
+ FT_UInt next_end, next_start;
+
+
+ p = cmap->data + 14 + i * 2;
+ next_end = TT_PEEK_USHORT( p );
+ p += 2 + num_segs2;
+ next_start = TT_PEEK_USHORT( p );
+
+ if ( charcode < next_start )
+ break;
+
+ end = next_end;
+ start = next_start;
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+
+ if ( offset != 0xFFFFU )
+ mid = i;
+ }
+ i--;
+
+ /* still no luck */
+ if ( mid == max )
+ {
+ mid = i;
+
+ break;
+ }
+ }
+
+ /* end, start, delta, and offset are for the i'th segment */
+ if ( mid != i )
+ {
+ p = cmap->data + 14 + mid * 2;
+ end = TT_PEEK_USHORT( p );
+ p += 2 + num_segs2;
+ start = TT_PEEK_USHORT( p );
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+ }
+ }
+ else
+ {
+ if ( offset == 0xFFFFU )
+ break;
+ }
+
+ if ( offset )
+ {
+ p += offset + ( charcode - start ) * 2;
+
+ /* if p > limit, the whole segment is invalid */
+ if ( next && p > limit )
+ break;
+
+ gindex = TT_PEEK_USHORT( p );
+ if ( gindex )
+ {
+ gindex = (FT_UInt)( (FT_Int)gindex + delta ) & 0xFFFFU;
+ if ( gindex >= (FT_UInt)face->root.num_glyphs )
+ gindex = 0;
+ }
+ }
+ else
+ {
+ gindex = (FT_UInt)( (FT_Int)charcode + delta ) & 0xFFFFU;
+
+ if ( next && gindex >= (FT_UInt)face->root.num_glyphs )
+ {
+ /* we have an invalid glyph index; if there is an overflow, */
+ /* we can adjust `charcode', otherwise the whole segment is */
+ /* invalid */
+ gindex = 0;
+
+ if ( (FT_Int)charcode + delta < 0 &&
+ (FT_Int)end + delta >= 0 )
+ charcode = (FT_UInt)( -delta );
+
+ else if ( (FT_Int)charcode + delta < 0x10000L &&
+ (FT_Int)end + delta >= 0x10000L )
+ charcode = (FT_UInt)( 0x10000L - delta );
+ }
+ }
+
+ break;
+ }
+ }
+
+ if ( next )
+ {
+ TT_CMap4 cmap4 = (TT_CMap4)cmap;
+
+
+ /* if `charcode' is not in any segment, then `mid' is */
+ /* the segment nearest to `charcode' */
+
+ if ( charcode > end )
+ {
+ mid++;
+ if ( mid == num_segs )
+ return 0;
+ }
+
+ if ( tt_cmap4_set_range( cmap4, mid ) )
+ {
+ if ( gindex )
+ *pcharcode = charcode;
+ }
+ else
+ {
+ cmap4->cur_charcode = charcode;
+
+ if ( gindex )
+ cmap4->cur_gindex = gindex;
+ else
+ {
+ cmap4->cur_charcode = charcode;
+ tt_cmap4_next( cmap4 );
+ gindex = cmap4->cur_gindex;
+ }
+
+ if ( gindex )
+ *pcharcode = cmap4->cur_charcode;
+ }
+ }
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap4_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ if ( char_code >= 0x10000UL )
+ return 0;
+
+ if ( cmap->flags & TT_CMAP_FLAG_UNSORTED )
+ return tt_cmap4_char_map_linear( cmap, &char_code, 0 );
+ else
+ return tt_cmap4_char_map_binary( cmap, &char_code, 0 );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap4_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt gindex;
+
+
+ if ( *pchar_code >= 0xFFFFU )
+ return 0;
+
+ if ( cmap->flags & TT_CMAP_FLAG_UNSORTED )
+ gindex = tt_cmap4_char_map_linear( cmap, pchar_code, 1 );
+ else
+ {
+ TT_CMap4 cmap4 = (TT_CMap4)cmap;
+
+
+ /* no need to search */
+ if ( *pchar_code == cmap4->cur_charcode )
+ {
+ tt_cmap4_next( cmap4 );
+ gindex = cmap4->cur_gindex;
+ if ( gindex )
+ *pchar_code = cmap4->cur_charcode;
+ }
+ else
+ gindex = tt_cmap4_char_map_binary( cmap, pchar_code, 1 );
+ }
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap4_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 4;
+
+
+ cmap_info->format = 4;
+ cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap4_class_rec,
+
+ sizeof ( TT_CMap4Rec ),
+
+ (FT_CMap_InitFunc) tt_cmap4_init, /* init */
+ (FT_CMap_DoneFunc) NULL, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap4_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap4_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ 4,
+ (TT_CMap_ValidateFunc)tt_cmap4_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap4_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_4 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 6 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 6
+ * length 2 USHORT table length in bytes
+ * language 4 USHORT Mac language code
+ *
+ * first 6 USHORT first segment code
+ * count 8 USHORT segment size in chars
+ * glyphIds 10 USHORT[count] glyph IDs
+ *
+ * A very simplified segment mapping.
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_6
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap6_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_UInt length, count;
+
+
+ if ( table + 10 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 2;
+ length = TT_NEXT_USHORT( p );
+
+ p = table + 8; /* skip language and start index */
+ count = TT_NEXT_USHORT( p );
+
+ if ( table + length > valid->limit || length < 10 + count * 2 )
+ FT_INVALID_TOO_SHORT;
+
+ /* check glyph indices */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt gindex;
+
+
+ for ( ; count > 0; count-- )
+ {
+ gindex = TT_NEXT_USHORT( p );
+ if ( gindex >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap6_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt result = 0;
+ FT_Byte* p = table + 6;
+ FT_UInt start = TT_NEXT_USHORT( p );
+ FT_UInt count = TT_NEXT_USHORT( p );
+ FT_UInt idx = (FT_UInt)( char_code - start );
+
+
+ if ( idx < count )
+ {
+ p += 2 * idx;
+ result = TT_PEEK_USHORT( p );
+ }
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap6_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt32 result = 0;
+ FT_UInt32 char_code = *pchar_code + 1;
+ FT_UInt gindex = 0;
+
+ FT_Byte* p = table + 6;
+ FT_UInt start = TT_NEXT_USHORT( p );
+ FT_UInt count = TT_NEXT_USHORT( p );
+ FT_UInt idx;
+
+
+ if ( char_code >= 0x10000UL )
+ return 0;
+
+ if ( char_code < start )
+ char_code = start;
+
+ idx = (FT_UInt)( char_code - start );
+ p += 2 * idx;
+
+ for ( ; idx < count; idx++ )
+ {
+ gindex = TT_NEXT_USHORT( p );
+ if ( gindex != 0 )
+ {
+ result = char_code;
+ break;
+ }
+
+ if ( char_code >= 0xFFFFU )
+ return 0;
+
+ char_code++;
+ }
+
+ *pchar_code = result;
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap6_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 4;
+
+
+ cmap_info->format = 6;
+ cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap6_class_rec,
+
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init, /* init */
+ (FT_CMap_DoneFunc) NULL, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap6_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap6_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ 6,
+ (TT_CMap_ValidateFunc)tt_cmap6_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap6_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_6 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 8 *****/
+ /***** *****/
+ /***** It is hard to completely understand what the OpenType spec *****/
+ /***** says about this format, but here is my conclusion. *****/
+ /***** *****/
+ /***** The purpose of this format is to easily map UTF-16 text to *****/
+ /***** glyph indices. Basically, the `char_code' must be in one of *****/
+ /***** the following formats. *****/
+ /***** *****/
+ /***** - A 16-bit value that isn't part of the Unicode Surrogates *****/
+ /***** Area (i.e. U+D800-U+DFFF). *****/
+ /***** *****/
+ /***** - A 32-bit value, made of two surrogate values, i.e.. if *****/
+ /***** `char_code = (char_hi << 16) | char_lo', then both *****/
+ /***** `char_hi' and `char_lo' must be in the Surrogates Area. *****/
+ /***** Area. *****/
+ /***** *****/
+ /***** The `is32' table embedded in the charmap indicates whether a *****/
+ /***** given 16-bit value is in the surrogates area or not. *****/
+ /***** *****/
+ /***** So, for any given `char_code', we can assert the following. *****/
+ /***** *****/
+ /***** If `char_hi == 0' then we must have `is32[char_lo] == 0'. *****/
+ /***** *****/
+ /***** If `char_hi != 0' then we must have both *****/
+ /***** `is32[char_hi] != 0' and `is32[char_lo] != 0'. *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 8
+ * reserved 2 USHORT reserved
+ * length 4 ULONG length in bytes
+ * language 8 ULONG Mac language code
+ * is32 12 BYTE[8192] 32-bitness bitmap
+ * count 8204 ULONG number of groups
+ *
+ * This header is followed by `count' groups of the following format:
+ *
+ * start 0 ULONG first charcode
+ * end 4 ULONG last charcode
+ * startId 8 ULONG start glyph ID for the group
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_8
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap8_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p = table + 4;
+ FT_Byte* is32;
+ FT_UInt32 length;
+ FT_UInt32 num_groups;
+
+
+ if ( table + 16 + 8192 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ length = TT_NEXT_ULONG( p );
+ if ( length > (FT_UInt32)( valid->limit - table ) || length < 8192 + 16 )
+ FT_INVALID_TOO_SHORT;
+
+ is32 = table + 12;
+ p = is32 + 8192; /* skip `is32' array */
+ num_groups = TT_NEXT_ULONG( p );
+
+ /* p + num_groups * 12 > valid->limit ? */
+ if ( num_groups > (FT_UInt32)( valid->limit - p ) / 12 )
+ FT_INVALID_TOO_SHORT;
+
+ /* check groups, they must be in increasing order */
+ {
+ FT_UInt32 n, start, end, start_id, count, last = 0;
+
+
+ for ( n = 0; n < num_groups; n++ )
+ {
+ FT_UInt hi, lo;
+
+
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_NEXT_ULONG( p );
+
+ if ( start > end )
+ FT_INVALID_DATA;
+
+ if ( n > 0 && start <= last )
+ FT_INVALID_DATA;
+
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt32 d = end - start;
+
+
+ /* start_id + end - start >= TT_VALID_GLYPH_COUNT( valid ) ? */
+ if ( d > TT_VALID_GLYPH_COUNT( valid ) ||
+ start_id >= TT_VALID_GLYPH_COUNT( valid ) - d )
+ FT_INVALID_GLYPH_ID;
+
+ count = (FT_UInt32)( end - start + 1 );
+
+ if ( start & ~0xFFFFU )
+ {
+ /* start_hi != 0; check that is32[i] is 1 for each i in */
+ /* the `hi' and `lo' of the range [start..end] */
+ for ( ; count > 0; count--, start++ )
+ {
+ hi = (FT_UInt)( start >> 16 );
+ lo = (FT_UInt)( start & 0xFFFFU );
+
+ if ( (is32[hi >> 3] & ( 0x80 >> ( hi & 7 ) ) ) == 0 )
+ FT_INVALID_DATA;
+
+ if ( (is32[lo >> 3] & ( 0x80 >> ( lo & 7 ) ) ) == 0 )
+ FT_INVALID_DATA;
+ }
+ }
+ else
+ {
+ /* start_hi == 0; check that is32[i] is 0 for each i in */
+ /* the range [start..end] */
+
+ /* end_hi cannot be != 0! */
+ if ( end & ~0xFFFFU )
+ FT_INVALID_DATA;
+
+ for ( ; count > 0; count--, start++ )
+ {
+ lo = (FT_UInt)( start & 0xFFFFU );
+
+ if ( (is32[lo >> 3] & ( 0x80 >> ( lo & 7 ) ) ) != 0 )
+ FT_INVALID_DATA;
+ }
+ }
+ }
+
+ last = end;
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap8_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt result = 0;
+ FT_Byte* p = table + 8204;
+ FT_UInt32 num_groups = TT_NEXT_ULONG( p );
+ FT_UInt32 start, end, start_id;
+
+
+ for ( ; num_groups > 0; num_groups-- )
+ {
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_NEXT_ULONG( p );
+
+ if ( char_code < start )
+ break;
+
+ if ( char_code <= end )
+ {
+ if ( start_id > 0xFFFFFFFFUL - ( char_code - start ) )
+ return 0;
+
+ result = (FT_UInt)( start_id + ( char_code - start ) );
+ break;
+ }
+ }
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap8_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_Face face = cmap->cmap.charmap.face;
+ FT_UInt32 result = 0;
+ FT_UInt32 char_code;
+ FT_UInt gindex = 0;
+ FT_Byte* table = cmap->data;
+ FT_Byte* p = table + 8204;
+ FT_UInt32 num_groups = TT_NEXT_ULONG( p );
+ FT_UInt32 start, end, start_id;
+
+
+ if ( *pchar_code >= 0xFFFFFFFFUL )
+ return 0;
+
+ char_code = *pchar_code + 1;
+
+ p = table + 8208;
+
+ for ( ; num_groups > 0; num_groups-- )
+ {
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_NEXT_ULONG( p );
+
+ if ( char_code < start )
+ char_code = start;
+
+ Again:
+ if ( char_code <= end )
+ {
+ /* ignore invalid group */
+ if ( start_id > 0xFFFFFFFFUL - ( char_code - start ) )
+ continue;
+
+ gindex = (FT_UInt)( start_id + ( char_code - start ) );
+
+ /* does first element of group point to `.notdef' glyph? */
+ if ( gindex == 0 )
+ {
+ if ( char_code >= 0xFFFFFFFFUL )
+ break;
+
+ char_code++;
+ goto Again;
+ }
+
+ /* if `gindex' is invalid, the remaining values */
+ /* in this group are invalid, too */
+ if ( gindex >= (FT_UInt)face->num_glyphs )
+ {
+ gindex = 0;
+ continue;
+ }
+
+ result = char_code;
+ break;
+ }
+ }
+
+ *pchar_code = result;
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap8_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 8;
+
+
+ cmap_info->format = 8;
+ cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap8_class_rec,
+
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init, /* init */
+ (FT_CMap_DoneFunc) NULL, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap8_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap8_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ 8,
+ (TT_CMap_ValidateFunc)tt_cmap8_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap8_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_8 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 10 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 10
+ * reserved 2 USHORT reserved
+ * length 4 ULONG length in bytes
+ * language 8 ULONG Mac language code
+ *
+ * start 12 ULONG first char in range
+ * count 16 ULONG number of chars in range
+ * glyphIds 20 USHORT[count] glyph indices covered
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_10
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap10_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p = table + 4;
+ FT_ULong length, count;
+
+
+ if ( table + 20 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ length = TT_NEXT_ULONG( p );
+ p = table + 16;
+ count = TT_NEXT_ULONG( p );
+
+ if ( length > (FT_ULong)( valid->limit - table ) ||
+ /* length < 20 + count * 2 ? */
+ length < 20 ||
+ ( length - 20 ) / 2 < count )
+ FT_INVALID_TOO_SHORT;
+
+ /* check glyph indices */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt gindex;
+
+
+ for ( ; count > 0; count-- )
+ {
+ gindex = TT_NEXT_USHORT( p );
+ if ( gindex >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap10_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt result = 0;
+ FT_Byte* p = table + 12;
+ FT_UInt32 start = TT_NEXT_ULONG( p );
+ FT_UInt32 count = TT_NEXT_ULONG( p );
+ FT_UInt32 idx;
+
+
+ if ( char_code < start )
+ return 0;
+
+ idx = char_code - start;
+
+ if ( idx < count )
+ {
+ p += 2 * idx;
+ result = TT_PEEK_USHORT( p );
+ }
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap10_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt32 char_code;
+ FT_UInt gindex = 0;
+ FT_Byte* p = table + 12;
+ FT_UInt32 start = TT_NEXT_ULONG( p );
+ FT_UInt32 count = TT_NEXT_ULONG( p );
+ FT_UInt32 idx;
+
+
+ if ( *pchar_code >= 0xFFFFFFFFUL )
+ return 0;
+
+ char_code = *pchar_code + 1;
+
+ if ( char_code < start )
+ char_code = start;
+
+ idx = char_code - start;
+ p += 2 * idx;
+
+ for ( ; idx < count; idx++ )
+ {
+ gindex = TT_NEXT_USHORT( p );
+ if ( gindex != 0 )
+ break;
+
+ if ( char_code >= 0xFFFFFFFFUL )
+ return 0;
+
+ char_code++;
+ }
+
+ *pchar_code = char_code;
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap10_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 8;
+
+
+ cmap_info->format = 10;
+ cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap10_class_rec,
+
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init, /* init */
+ (FT_CMap_DoneFunc) NULL, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap10_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap10_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ 10,
+ (TT_CMap_ValidateFunc)tt_cmap10_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap10_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_10 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 12 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 12
+ * reserved 2 USHORT reserved
+ * length 4 ULONG length in bytes
+ * language 8 ULONG Mac language code
+ * count 12 ULONG number of groups
+ * 16
+ *
+ * This header is followed by `count' groups of the following format:
+ *
+ * start 0 ULONG first charcode
+ * end 4 ULONG last charcode
+ * startId 8 ULONG start glyph ID for the group
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_12
+
+ typedef struct TT_CMap12Rec_
+ {
+ TT_CMapRec cmap;
+ FT_Bool valid;
+ FT_ULong cur_charcode;
+ FT_UInt cur_gindex;
+ FT_ULong cur_group;
+ FT_ULong num_groups;
+
+ } TT_CMap12Rec, *TT_CMap12;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap12_init( TT_CMap12 cmap,
+ FT_Byte* table )
+ {
+ cmap->cmap.data = table;
+
+ table += 12;
+ cmap->num_groups = FT_PEEK_ULONG( table );
+
+ cmap->valid = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap12_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_ULong length;
+ FT_ULong num_groups;
+
+
+ if ( table + 16 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 4;
+ length = TT_NEXT_ULONG( p );
+
+ p = table + 12;
+ num_groups = TT_NEXT_ULONG( p );
+
+ if ( length > (FT_ULong)( valid->limit - table ) ||
+ /* length < 16 + 12 * num_groups ? */
+ length < 16 ||
+ ( length - 16 ) / 12 < num_groups )
+ FT_INVALID_TOO_SHORT;
+
+ /* check groups, they must be in increasing order */
+ {
+ FT_ULong n, start, end, start_id, last = 0;
+
+
+ for ( n = 0; n < num_groups; n++ )
+ {
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_NEXT_ULONG( p );
+
+ if ( start > end )
+ FT_INVALID_DATA;
+
+ if ( n > 0 && start <= last )
+ FT_INVALID_DATA;
+
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt32 d = end - start;
+
+
+ /* start_id + end - start >= TT_VALID_GLYPH_COUNT( valid ) ? */
+ if ( d > TT_VALID_GLYPH_COUNT( valid ) ||
+ start_id >= TT_VALID_GLYPH_COUNT( valid ) - d )
+ FT_INVALID_GLYPH_ID;
+ }
+
+ last = end;
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ /* search the index of the charcode next to cmap->cur_charcode */
+ /* cmap->cur_group should be set up properly by caller */
+ /* */
+ static void
+ tt_cmap12_next( TT_CMap12 cmap )
+ {
+ FT_Face face = cmap->cmap.cmap.charmap.face;
+ FT_Byte* p;
+ FT_ULong start, end, start_id, char_code;
+ FT_ULong n;
+ FT_UInt gindex;
+
+
+ if ( cmap->cur_charcode >= 0xFFFFFFFFUL )
+ goto Fail;
+
+ char_code = cmap->cur_charcode + 1;
+
+ for ( n = cmap->cur_group; n < cmap->num_groups; n++ )
+ {
+ p = cmap->cmap.data + 16 + 12 * n;
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_PEEK_ULONG( p );
+
+ if ( char_code < start )
+ char_code = start;
+
+ Again:
+ if ( char_code <= end )
+ {
+ /* ignore invalid group */
+ if ( start_id > 0xFFFFFFFFUL - ( char_code - start ) )
+ continue;
+
+ gindex = (FT_UInt)( start_id + ( char_code - start ) );
+
+ /* does first element of group point to `.notdef' glyph? */
+ if ( gindex == 0 )
+ {
+ if ( char_code >= 0xFFFFFFFFUL )
+ goto Fail;
+
+ char_code++;
+ goto Again;
+ }
+
+ /* if `gindex' is invalid, the remaining values */
+ /* in this group are invalid, too */
+ if ( gindex >= (FT_UInt)face->num_glyphs )
+ continue;
+
+ cmap->cur_charcode = char_code;
+ cmap->cur_gindex = gindex;
+ cmap->cur_group = n;
+
+ return;
+ }
+ }
+
+ Fail:
+ cmap->valid = 0;
+ }
+
+
+ static FT_UInt
+ tt_cmap12_char_map_binary( TT_CMap cmap,
+ FT_UInt32* pchar_code,
+ FT_Bool next )
+ {
+ FT_UInt gindex = 0;
+ FT_Byte* p = cmap->data + 12;
+ FT_UInt32 num_groups = TT_PEEK_ULONG( p );
+ FT_UInt32 char_code = *pchar_code;
+ FT_UInt32 start, end, start_id;
+ FT_UInt32 max, min, mid;
+
+
+ if ( !num_groups )
+ return 0;
+
+ /* make compiler happy */
+ mid = num_groups;
+ end = 0xFFFFFFFFUL;
+
+ if ( next )
+ {
+ if ( char_code >= 0xFFFFFFFFUL )
+ return 0;
+
+ char_code++;
+ }
+
+ min = 0;
+ max = num_groups;
+
+ /* binary search */
+ while ( min < max )
+ {
+ mid = ( min + max ) >> 1;
+ p = cmap->data + 16 + 12 * mid;
+
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+
+ if ( char_code < start )
+ max = mid;
+ else if ( char_code > end )
+ min = mid + 1;
+ else
+ {
+ start_id = TT_PEEK_ULONG( p );
+
+ /* reject invalid glyph index */
+ if ( start_id > 0xFFFFFFFFUL - ( char_code - start ) )
+ gindex = 0;
+ else
+ gindex = (FT_UInt)( start_id + ( char_code - start ) );
+ break;
+ }
+ }
+
+ if ( next )
+ {
+ FT_Face face = cmap->cmap.charmap.face;
+ TT_CMap12 cmap12 = (TT_CMap12)cmap;
+
+
+ /* if `char_code' is not in any group, then `mid' is */
+ /* the group nearest to `char_code' */
+
+ if ( char_code > end )
+ {
+ mid++;
+ if ( mid == num_groups )
+ return 0;
+ }
+
+ cmap12->valid = 1;
+ cmap12->cur_charcode = char_code;
+ cmap12->cur_group = mid;
+
+ if ( gindex >= (FT_UInt)face->num_glyphs )
+ gindex = 0;
+
+ if ( !gindex )
+ {
+ tt_cmap12_next( cmap12 );
+
+ if ( cmap12->valid )
+ gindex = cmap12->cur_gindex;
+ }
+ else
+ cmap12->cur_gindex = gindex;
+
+ *pchar_code = cmap12->cur_charcode;
+ }
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap12_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ return tt_cmap12_char_map_binary( cmap, &char_code, 0 );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap12_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ TT_CMap12 cmap12 = (TT_CMap12)cmap;
+ FT_UInt gindex;
+
+
+ /* no need to search */
+ if ( cmap12->valid && cmap12->cur_charcode == *pchar_code )
+ {
+ tt_cmap12_next( cmap12 );
+ if ( cmap12->valid )
+ {
+ gindex = cmap12->cur_gindex;
+ *pchar_code = (FT_UInt32)cmap12->cur_charcode;
+ }
+ else
+ gindex = 0;
+ }
+ else
+ gindex = tt_cmap12_char_map_binary( cmap, pchar_code, 1 );
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap12_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 8;
+
+
+ cmap_info->format = 12;
+ cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap12_class_rec,
+
+ sizeof ( TT_CMap12Rec ),
+
+ (FT_CMap_InitFunc) tt_cmap12_init, /* init */
+ (FT_CMap_DoneFunc) NULL, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap12_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap12_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ 12,
+ (TT_CMap_ValidateFunc)tt_cmap12_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap12_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_12 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 13 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 13
+ * reserved 2 USHORT reserved
+ * length 4 ULONG length in bytes
+ * language 8 ULONG Mac language code
+ * count 12 ULONG number of groups
+ * 16
+ *
+ * This header is followed by `count' groups of the following format:
+ *
+ * start 0 ULONG first charcode
+ * end 4 ULONG last charcode
+ * glyphId 8 ULONG glyph ID for the whole group
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_13
+
+ typedef struct TT_CMap13Rec_
+ {
+ TT_CMapRec cmap;
+ FT_Bool valid;
+ FT_ULong cur_charcode;
+ FT_UInt cur_gindex;
+ FT_ULong cur_group;
+ FT_ULong num_groups;
+
+ } TT_CMap13Rec, *TT_CMap13;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap13_init( TT_CMap13 cmap,
+ FT_Byte* table )
+ {
+ cmap->cmap.data = table;
+
+ table += 12;
+ cmap->num_groups = FT_PEEK_ULONG( table );
+
+ cmap->valid = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap13_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_ULong length;
+ FT_ULong num_groups;
+
+
+ if ( table + 16 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 4;
+ length = TT_NEXT_ULONG( p );
+
+ p = table + 12;
+ num_groups = TT_NEXT_ULONG( p );
+
+ if ( length > (FT_ULong)( valid->limit - table ) ||
+ /* length < 16 + 12 * num_groups ? */
+ length < 16 ||
+ ( length - 16 ) / 12 < num_groups )
+ FT_INVALID_TOO_SHORT;
+
+ /* check groups, they must be in increasing order */
+ {
+ FT_ULong n, start, end, glyph_id, last = 0;
+
+
+ for ( n = 0; n < num_groups; n++ )
+ {
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ glyph_id = TT_NEXT_ULONG( p );
+
+ if ( start > end )
+ FT_INVALID_DATA;
+
+ if ( n > 0 && start <= last )
+ FT_INVALID_DATA;
+
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ if ( glyph_id >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+
+ last = end;
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ /* search the index of the charcode next to cmap->cur_charcode */
+ /* cmap->cur_group should be set up properly by caller */
+ /* */
+ static void
+ tt_cmap13_next( TT_CMap13 cmap )
+ {
+ FT_Face face = cmap->cmap.cmap.charmap.face;
+ FT_Byte* p;
+ FT_ULong start, end, glyph_id, char_code;
+ FT_ULong n;
+ FT_UInt gindex;
+
+
+ if ( cmap->cur_charcode >= 0xFFFFFFFFUL )
+ goto Fail;
+
+ char_code = cmap->cur_charcode + 1;
+
+ for ( n = cmap->cur_group; n < cmap->num_groups; n++ )
+ {
+ p = cmap->cmap.data + 16 + 12 * n;
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ glyph_id = TT_PEEK_ULONG( p );
+
+ if ( char_code < start )
+ char_code = start;
+
+ if ( char_code <= end )
+ {
+ gindex = (FT_UInt)glyph_id;
+
+ if ( gindex && gindex < (FT_UInt)face->num_glyphs )
+ {
+ cmap->cur_charcode = char_code;
+ cmap->cur_gindex = gindex;
+ cmap->cur_group = n;
+
+ return;
+ }
+ }
+ }
+
+ Fail:
+ cmap->valid = 0;
+ }
+
+
+ static FT_UInt
+ tt_cmap13_char_map_binary( TT_CMap cmap,
+ FT_UInt32* pchar_code,
+ FT_Bool next )
+ {
+ FT_UInt gindex = 0;
+ FT_Byte* p = cmap->data + 12;
+ FT_UInt32 num_groups = TT_PEEK_ULONG( p );
+ FT_UInt32 char_code = *pchar_code;
+ FT_UInt32 start, end;
+ FT_UInt32 max, min, mid;
+
+
+ if ( !num_groups )
+ return 0;
+
+ /* make compiler happy */
+ mid = num_groups;
+ end = 0xFFFFFFFFUL;
+
+ if ( next )
+ {
+ if ( char_code >= 0xFFFFFFFFUL )
+ return 0;
+
+ char_code++;
+ }
+
+ min = 0;
+ max = num_groups;
+
+ /* binary search */
+ while ( min < max )
+ {
+ mid = ( min + max ) >> 1;
+ p = cmap->data + 16 + 12 * mid;
+
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+
+ if ( char_code < start )
+ max = mid;
+ else if ( char_code > end )
+ min = mid + 1;
+ else
+ {
+ gindex = (FT_UInt)TT_PEEK_ULONG( p );
+
+ break;
+ }
+ }
+
+ if ( next )
+ {
+ FT_Face face = cmap->cmap.charmap.face;
+ TT_CMap13 cmap13 = (TT_CMap13)cmap;
+
+
+ /* if `char_code' is not in any group, then `mid' is */
+ /* the group nearest to `char_code' */
+
+ if ( char_code > end )
+ {
+ mid++;
+ if ( mid == num_groups )
+ return 0;
+ }
+
+ cmap13->valid = 1;
+ cmap13->cur_charcode = char_code;
+ cmap13->cur_group = mid;
+
+ if ( gindex >= (FT_UInt)face->num_glyphs )
+ gindex = 0;
+
+ if ( !gindex )
+ {
+ tt_cmap13_next( cmap13 );
+
+ if ( cmap13->valid )
+ gindex = cmap13->cur_gindex;
+ }
+ else
+ cmap13->cur_gindex = gindex;
+
+ *pchar_code = cmap13->cur_charcode;
+ }
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap13_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ return tt_cmap13_char_map_binary( cmap, &char_code, 0 );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap13_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ TT_CMap13 cmap13 = (TT_CMap13)cmap;
+ FT_UInt gindex;
+
+
+ /* no need to search */
+ if ( cmap13->valid && cmap13->cur_charcode == *pchar_code )
+ {
+ tt_cmap13_next( cmap13 );
+ if ( cmap13->valid )
+ {
+ gindex = cmap13->cur_gindex;
+ *pchar_code = cmap13->cur_charcode;
+ }
+ else
+ gindex = 0;
+ }
+ else
+ gindex = tt_cmap13_char_map_binary( cmap, pchar_code, 1 );
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap13_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 8;
+
+
+ cmap_info->format = 13;
+ cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap13_class_rec,
+
+ sizeof ( TT_CMap13Rec ),
+
+ (FT_CMap_InitFunc) tt_cmap13_init, /* init */
+ (FT_CMap_DoneFunc) NULL, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap13_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap13_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ 13,
+ (TT_CMap_ValidateFunc)tt_cmap13_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap13_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_13 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 14 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * TABLE OVERVIEW
+ * --------------
+ *
+ * NAME OFFSET TYPE DESCRIPTION
+ *
+ * format 0 USHORT must be 14
+ * length 2 ULONG table length in bytes
+ * numSelector 6 ULONG number of variation sel. records
+ *
+ * Followed by numSelector records, each of which looks like
+ *
+ * varSelector 0 UINT24 Unicode codepoint of sel.
+ * defaultOff 3 ULONG offset to a default UVS table
+ * describing any variants to be found in
+ * the normal Unicode subtable.
+ * nonDefOff 7 ULONG offset to a non-default UVS table
+ * describing any variants not in the
+ * standard cmap, with GIDs here
+ * (either offset may be 0 NULL)
+ *
+ * Selectors are sorted by code point.
+ *
+ * A default Unicode Variation Selector (UVS) subtable is just a list of
+ * ranges of code points which are to be found in the standard cmap. No
+ * glyph IDs (GIDs) here.
+ *
+ * numRanges 0 ULONG number of ranges following
+ *
+ * A range looks like
+ *
+ * uniStart 0 UINT24 code point of the first character in
+ * this range
+ * additionalCnt 3 UBYTE count of additional characters in this
+ * range (zero means a range of a single
+ * character)
+ *
+ * Ranges are sorted by `uniStart'.
+ *
+ * A non-default Unicode Variation Selector (UVS) subtable is a list of
+ * mappings from codepoint to GID.
+ *
+ * numMappings 0 ULONG number of mappings
+ *
+ * A range looks like
+ *
+ * uniStart 0 UINT24 code point of the first character in
+ * this range
+ * GID 3 USHORT and its GID
+ *
+ * Ranges are sorted by `uniStart'.
+ */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_14
+
+ typedef struct TT_CMap14Rec_
+ {
+ TT_CMapRec cmap;
+ FT_ULong num_selectors;
+
+ /* This array is used to store the results of various
+ * cmap 14 query functions. The data is overwritten
+ * on each call to these functions.
+ */
+ FT_UInt32 max_results;
+ FT_UInt32* results;
+ FT_Memory memory;
+
+ } TT_CMap14Rec, *TT_CMap14;
+
+
+ FT_CALLBACK_DEF( void )
+ tt_cmap14_done( TT_CMap14 cmap )
+ {
+ FT_Memory memory = cmap->memory;
+
+
+ cmap->max_results = 0;
+ if ( memory && cmap->results )
+ FT_FREE( cmap->results );
+ }
+
+
+ static FT_Error
+ tt_cmap14_ensure( TT_CMap14 cmap,
+ FT_UInt32 num_results,
+ FT_Memory memory )
+ {
+ FT_UInt32 old_max = cmap->max_results;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( num_results > cmap->max_results )
+ {
+ cmap->memory = memory;
+
+ if ( FT_QRENEW_ARRAY( cmap->results, old_max, num_results ) )
+ return error;
+
+ cmap->max_results = num_results;
+ }
+
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap14_init( TT_CMap14 cmap,
+ FT_Byte* table )
+ {
+ cmap->cmap.data = table;
+
+ table += 6;
+ cmap->num_selectors = FT_PEEK_ULONG( table );
+ cmap->max_results = 0;
+ cmap->results = NULL;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap14_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_ULong length;
+ FT_ULong num_selectors;
+
+
+ if ( table + 2 + 4 + 4 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 2;
+ length = TT_NEXT_ULONG( p );
+ num_selectors = TT_NEXT_ULONG( p );
+
+ if ( length > (FT_ULong)( valid->limit - table ) ||
+ /* length < 10 + 11 * num_selectors ? */
+ length < 10 ||
+ ( length - 10 ) / 11 < num_selectors )
+ FT_INVALID_TOO_SHORT;
+
+ /* check selectors, they must be in increasing order */
+ {
+ /* we start lastVarSel at 1 because a variant selector value of 0
+ * isn't valid.
+ */
+ FT_ULong n, lastVarSel = 1;
+
+
+ for ( n = 0; n < num_selectors; n++ )
+ {
+ FT_ULong varSel = TT_NEXT_UINT24( p );
+ FT_ULong defOff = TT_NEXT_ULONG( p );
+ FT_ULong nondefOff = TT_NEXT_ULONG( p );
+
+
+ if ( defOff >= length || nondefOff >= length )
+ FT_INVALID_TOO_SHORT;
+
+ if ( varSel < lastVarSel )
+ FT_INVALID_DATA;
+
+ lastVarSel = varSel + 1;
+
+ /* check the default table (these glyphs should be reached */
+ /* through the normal Unicode cmap, no GIDs, just check order) */
+ if ( defOff != 0 )
+ {
+ FT_Byte* defp = table + defOff;
+ FT_ULong numRanges;
+ FT_ULong i;
+ FT_ULong lastBase = 0;
+
+
+ if ( defp + 4 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ numRanges = TT_NEXT_ULONG( defp );
+
+ /* defp + numRanges * 4 > valid->limit ? */
+ if ( numRanges > (FT_ULong)( valid->limit - defp ) / 4 )
+ FT_INVALID_TOO_SHORT;
+
+ for ( i = 0; i < numRanges; i++ )
+ {
+ FT_ULong base = TT_NEXT_UINT24( defp );
+ FT_ULong cnt = FT_NEXT_BYTE( defp );
+
+
+ if ( base + cnt >= 0x110000UL ) /* end of Unicode */
+ FT_INVALID_DATA;
+
+ if ( base < lastBase )
+ FT_INVALID_DATA;
+
+ lastBase = base + cnt + 1U;
+ }
+ }
+
+ /* and the non-default table (these glyphs are specified here) */
+ if ( nondefOff != 0 )
+ {
+ FT_Byte* ndp = table + nondefOff;
+ FT_ULong numMappings;
+ FT_ULong i, lastUni = 0;
+
+
+ if ( ndp + 4 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ numMappings = TT_NEXT_ULONG( ndp );
+
+ /* numMappings * 5 > (FT_ULong)( valid->limit - ndp ) ? */
+ if ( numMappings > ( (FT_ULong)( valid->limit - ndp ) ) / 5 )
+ FT_INVALID_TOO_SHORT;
+
+ for ( i = 0; i < numMappings; i++ )
+ {
+ FT_ULong uni = TT_NEXT_UINT24( ndp );
+ FT_ULong gid = TT_NEXT_USHORT( ndp );
+
+
+ if ( uni >= 0x110000UL ) /* end of Unicode */
+ FT_INVALID_DATA;
+
+ if ( uni < lastUni )
+ FT_INVALID_DATA;
+
+ lastUni = uni + 1U;
+
+ if ( valid->level >= FT_VALIDATE_TIGHT &&
+ gid >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap14_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_UNUSED( cmap );
+ FT_UNUSED( char_code );
+
+ /* This can't happen */
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap14_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UNUSED( cmap );
+
+ /* This can't happen */
+ *pchar_code = 0;
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap14_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_UNUSED( cmap );
+
+ cmap_info->format = 14;
+ /* subtable 14 does not define a language field */
+ cmap_info->language = 0xFFFFFFFFUL;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_UInt
+ tt_cmap14_char_map_def_binary( FT_Byte *base,
+ FT_UInt32 char_code )
+ {
+ FT_UInt32 numRanges = TT_PEEK_ULONG( base );
+ FT_UInt32 max, min;
+
+
+ min = 0;
+ max = numRanges;
+
+ base += 4;
+
+ /* binary search */
+ while ( min < max )
+ {
+ FT_UInt32 mid = ( min + max ) >> 1;
+ FT_Byte* p = base + 4 * mid;
+ FT_ULong start = TT_NEXT_UINT24( p );
+ FT_UInt cnt = FT_NEXT_BYTE( p );
+
+
+ if ( char_code < start )
+ max = mid;
+ else if ( char_code > start + cnt )
+ min = mid + 1;
+ else
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+ static FT_UInt
+ tt_cmap14_char_map_nondef_binary( FT_Byte *base,
+ FT_UInt32 char_code )
+ {
+ FT_UInt32 numMappings = TT_PEEK_ULONG( base );
+ FT_UInt32 max, min;
+
+
+ min = 0;
+ max = numMappings;
+
+ base += 4;
+
+ /* binary search */
+ while ( min < max )
+ {
+ FT_UInt32 mid = ( min + max ) >> 1;
+ FT_Byte* p = base + 5 * mid;
+ FT_UInt32 uni = (FT_UInt32)TT_NEXT_UINT24( p );
+
+
+ if ( char_code < uni )
+ max = mid;
+ else if ( char_code > uni )
+ min = mid + 1;
+ else
+ return TT_PEEK_USHORT( p );
+ }
+
+ return 0;
+ }
+
+
+ static FT_Byte*
+ tt_cmap14_find_variant( FT_Byte *base,
+ FT_UInt32 variantCode )
+ {
+ FT_UInt32 numVar = TT_PEEK_ULONG( base );
+ FT_UInt32 max, min;
+
+
+ min = 0;
+ max = numVar;
+
+ base += 4;
+
+ /* binary search */
+ while ( min < max )
+ {
+ FT_UInt32 mid = ( min + max ) >> 1;
+ FT_Byte* p = base + 11 * mid;
+ FT_ULong varSel = TT_NEXT_UINT24( p );
+
+
+ if ( variantCode < varSel )
+ max = mid;
+ else if ( variantCode > varSel )
+ min = mid + 1;
+ else
+ return p;
+ }
+
+ return NULL;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap14_char_var_index( TT_CMap cmap,
+ TT_CMap ucmap,
+ FT_UInt32 charcode,
+ FT_UInt32 variantSelector )
+ {
+ FT_Byte* p = tt_cmap14_find_variant( cmap->data + 6, variantSelector );
+ FT_ULong defOff;
+ FT_ULong nondefOff;
+
+
+ if ( !p )
+ return 0;
+
+ defOff = TT_NEXT_ULONG( p );
+ nondefOff = TT_PEEK_ULONG( p );
+
+ if ( defOff != 0 &&
+ tt_cmap14_char_map_def_binary( cmap->data + defOff, charcode ) )
+ {
+ /* This is the default variant of this charcode. GID not stored */
+ /* here; stored in the normal Unicode charmap instead. */
+ return ucmap->cmap.clazz->char_index( &ucmap->cmap, charcode );
+ }
+
+ if ( nondefOff != 0 )
+ return tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,
+ charcode );
+
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Int )
+ tt_cmap14_char_var_isdefault( TT_CMap cmap,
+ FT_UInt32 charcode,
+ FT_UInt32 variantSelector )
+ {
+ FT_Byte* p = tt_cmap14_find_variant( cmap->data + 6, variantSelector );
+ FT_ULong defOff;
+ FT_ULong nondefOff;
+
+
+ if ( !p )
+ return -1;
+
+ defOff = TT_NEXT_ULONG( p );
+ nondefOff = TT_NEXT_ULONG( p );
+
+ if ( defOff != 0 &&
+ tt_cmap14_char_map_def_binary( cmap->data + defOff, charcode ) )
+ return 1;
+
+ if ( nondefOff != 0 &&
+ tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,
+ charcode ) != 0 )
+ return 0;
+
+ return -1;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32* )
+ tt_cmap14_variants( TT_CMap cmap,
+ FT_Memory memory )
+ {
+ TT_CMap14 cmap14 = (TT_CMap14)cmap;
+ FT_UInt32 count = cmap14->num_selectors;
+ FT_Byte* p = cmap->data + 10;
+ FT_UInt32* result;
+ FT_UInt32 i;
+
+
+ if ( tt_cmap14_ensure( cmap14, ( count + 1 ), memory ) )
+ return NULL;
+
+ result = cmap14->results;
+ for ( i = 0; i < count; i++ )
+ {
+ result[i] = (FT_UInt32)TT_NEXT_UINT24( p );
+ p += 8;
+ }
+ result[i] = 0;
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 * )
+ tt_cmap14_char_variants( TT_CMap cmap,
+ FT_Memory memory,
+ FT_UInt32 charCode )
+ {
+ TT_CMap14 cmap14 = (TT_CMap14) cmap;
+ FT_UInt32 count = cmap14->num_selectors;
+ FT_Byte* p = cmap->data + 10;
+ FT_UInt32* q;
+
+
+ if ( tt_cmap14_ensure( cmap14, ( count + 1 ), memory ) )
+ return NULL;
+
+ for ( q = cmap14->results; count > 0; count-- )
+ {
+ FT_UInt32 varSel = TT_NEXT_UINT24( p );
+ FT_ULong defOff = TT_NEXT_ULONG( p );
+ FT_ULong nondefOff = TT_NEXT_ULONG( p );
+
+
+ if ( ( defOff != 0 &&
+ tt_cmap14_char_map_def_binary( cmap->data + defOff,
+ charCode ) ) ||
+ ( nondefOff != 0 &&
+ tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,
+ charCode ) != 0 ) )
+ {
+ q[0] = varSel;
+ q++;
+ }
+ }
+ q[0] = 0;
+
+ return cmap14->results;
+ }
+
+
+ static FT_UInt
+ tt_cmap14_def_char_count( FT_Byte *p )
+ {
+ FT_UInt32 numRanges = (FT_UInt32)TT_NEXT_ULONG( p );
+ FT_UInt tot = 0;
+
+
+ p += 3; /* point to the first `cnt' field */
+ for ( ; numRanges > 0; numRanges-- )
+ {
+ tot += 1 + p[0];
+ p += 4;
+ }
+
+ return tot;
+ }
+
+
+ static FT_UInt32*
+ tt_cmap14_get_def_chars( TT_CMap cmap,
+ FT_Byte* p,
+ FT_Memory memory )
+ {
+ TT_CMap14 cmap14 = (TT_CMap14) cmap;
+ FT_UInt32 numRanges;
+ FT_UInt cnt;
+ FT_UInt32* q;
+
+
+ cnt = tt_cmap14_def_char_count( p );
+ numRanges = (FT_UInt32)TT_NEXT_ULONG( p );
+
+ if ( tt_cmap14_ensure( cmap14, ( cnt + 1 ), memory ) )
+ return NULL;
+
+ for ( q = cmap14->results; numRanges > 0; numRanges-- )
+ {
+ FT_UInt32 uni = (FT_UInt32)TT_NEXT_UINT24( p );
+
+
+ cnt = FT_NEXT_BYTE( p ) + 1;
+ do
+ {
+ q[0] = uni;
+ uni += 1;
+ q += 1;
+
+ } while ( --cnt != 0 );
+ }
+ q[0] = 0;
+
+ return cmap14->results;
+ }
+
+
+ static FT_UInt32*
+ tt_cmap14_get_nondef_chars( TT_CMap cmap,
+ FT_Byte *p,
+ FT_Memory memory )
+ {
+ TT_CMap14 cmap14 = (TT_CMap14) cmap;
+ FT_UInt32 numMappings;
+ FT_UInt i;
+ FT_UInt32 *ret;
+
+
+ numMappings = (FT_UInt32)TT_NEXT_ULONG( p );
+
+ if ( tt_cmap14_ensure( cmap14, ( numMappings + 1 ), memory ) )
+ return NULL;
+
+ ret = cmap14->results;
+ for ( i = 0; i < numMappings; i++ )
+ {
+ ret[i] = (FT_UInt32)TT_NEXT_UINT24( p );
+ p += 2;
+ }
+ ret[i] = 0;
+
+ return ret;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 * )
+ tt_cmap14_variant_chars( TT_CMap cmap,
+ FT_Memory memory,
+ FT_UInt32 variantSelector )
+ {
+ FT_Byte *p = tt_cmap14_find_variant( cmap->data + 6,
+ variantSelector );
+ FT_Int i;
+ FT_ULong defOff;
+ FT_ULong nondefOff;
+
+
+ if ( !p )
+ return NULL;
+
+ defOff = TT_NEXT_ULONG( p );
+ nondefOff = TT_NEXT_ULONG( p );
+
+ if ( defOff == 0 && nondefOff == 0 )
+ return NULL;
+
+ if ( defOff == 0 )
+ return tt_cmap14_get_nondef_chars( cmap, cmap->data + nondefOff,
+ memory );
+ else if ( nondefOff == 0 )
+ return tt_cmap14_get_def_chars( cmap, cmap->data + defOff,
+ memory );
+ else
+ {
+ /* Both a default and a non-default glyph set? That's probably not */
+ /* good font design, but the spec allows for it... */
+ TT_CMap14 cmap14 = (TT_CMap14) cmap;
+ FT_UInt32 numRanges;
+ FT_UInt32 numMappings;
+ FT_UInt32 duni;
+ FT_UInt32 dcnt;
+ FT_UInt32 nuni;
+ FT_Byte* dp;
+ FT_UInt di, ni, k;
+
+ FT_UInt32 *ret;
+
+
+ p = cmap->data + nondefOff;
+ dp = cmap->data + defOff;
+
+ numMappings = (FT_UInt32)TT_NEXT_ULONG( p );
+ dcnt = tt_cmap14_def_char_count( dp );
+ numRanges = (FT_UInt32)TT_NEXT_ULONG( dp );
+
+ if ( numMappings == 0 )
+ return tt_cmap14_get_def_chars( cmap, cmap->data + defOff,
+ memory );
+ if ( dcnt == 0 )
+ return tt_cmap14_get_nondef_chars( cmap, cmap->data + nondefOff,
+ memory );
+
+ if ( tt_cmap14_ensure( cmap14, ( dcnt + numMappings + 1 ), memory ) )
+ return NULL;
+
+ ret = cmap14->results;
+ duni = (FT_UInt32)TT_NEXT_UINT24( dp );
+ dcnt = FT_NEXT_BYTE( dp );
+ di = 1;
+ nuni = (FT_UInt32)TT_NEXT_UINT24( p );
+ p += 2;
+ ni = 1;
+ i = 0;
+
+ for (;;)
+ {
+ if ( nuni > duni + dcnt )
+ {
+ for ( k = 0; k <= dcnt; k++ )
+ ret[i++] = duni + k;
+
+ di++;
+
+ if ( di > numRanges )
+ break;
+
+ duni = (FT_UInt32)TT_NEXT_UINT24( dp );
+ dcnt = FT_NEXT_BYTE( dp );
+ }
+ else
+ {
+ if ( nuni < duni )
+ ret[i++] = nuni;
+ /* If it is within the default range then ignore it -- */
+ /* that should not have happened */
+ ni++;
+ if ( ni > numMappings )
+ break;
+
+ nuni = (FT_UInt32)TT_NEXT_UINT24( p );
+ p += 2;
+ }
+ }
+
+ if ( ni <= numMappings )
+ {
+ /* If we get here then we have run out of all default ranges. */
+ /* We have read one non-default mapping which we haven't stored */
+ /* and there may be others that need to be read. */
+ ret[i++] = nuni;
+ while ( ni < numMappings )
+ {
+ ret[i++] = (FT_UInt32)TT_NEXT_UINT24( p );
+ p += 2;
+ ni++;
+ }
+ }
+ else if ( di <= numRanges )
+ {
+ /* If we get here then we have run out of all non-default */
+ /* mappings. We have read one default range which we haven't */
+ /* stored and there may be others that need to be read. */
+ for ( k = 0; k <= dcnt; k++ )
+ ret[i++] = duni + k;
+
+ while ( di < numRanges )
+ {
+ duni = (FT_UInt32)TT_NEXT_UINT24( dp );
+ dcnt = FT_NEXT_BYTE( dp );
+
+ for ( k = 0; k <= dcnt; k++ )
+ ret[i++] = duni + k;
+ di++;
+ }
+ }
+
+ ret[i] = 0;
+
+ return ret;
+ }
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap14_class_rec,
+
+ sizeof ( TT_CMap14Rec ),
+
+ (FT_CMap_InitFunc) tt_cmap14_init, /* init */
+ (FT_CMap_DoneFunc) tt_cmap14_done, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap14_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap14_char_next, /* char_next */
+
+ /* Format 14 extension functions */
+ (FT_CMap_CharVarIndexFunc) tt_cmap14_char_var_index,
+ (FT_CMap_CharVarIsDefaultFunc)tt_cmap14_char_var_isdefault,
+ (FT_CMap_VariantListFunc) tt_cmap14_variants,
+ (FT_CMap_CharVariantListFunc) tt_cmap14_char_variants,
+ (FT_CMap_VariantCharListFunc) tt_cmap14_variant_chars,
+
+ 14,
+ (TT_CMap_ValidateFunc)tt_cmap14_validate, /* validate */
+ (TT_CMap_Info_GetFunc)tt_cmap14_get_info /* get_cmap_info */
+ )
+
+#endif /* TT_CONFIG_CMAP_FORMAT_14 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SYNTHETIC UNICODE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* This charmap is generated using postscript glyph names. */
+
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+ FT_CALLBACK_DEF( const char * )
+ tt_get_glyph_name( TT_Face face,
+ FT_UInt idx )
+ {
+ FT_String* PSname = NULL;
+
+
+ tt_face_get_ps_name( face, idx, &PSname );
+
+ return PSname;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap_unicode_init( PS_Unicodes unicodes,
+ FT_Pointer pointer )
+ {
+ TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+ FT_UNUSED( pointer );
+
+
+ if ( !psnames->unicodes_init )
+ return FT_THROW( Unimplemented_Feature );
+
+ return psnames->unicodes_init( memory,
+ unicodes,
+ face->root.num_glyphs,
+ (PS_GetGlyphNameFunc)&tt_get_glyph_name,
+ (PS_FreeGlyphNameFunc)NULL,
+ (FT_Pointer)face );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ tt_cmap_unicode_done( PS_Unicodes unicodes )
+ {
+ FT_Face face = FT_CMAP_FACE( unicodes );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( unicodes->maps );
+ unicodes->num_maps = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap_unicode_char_index( PS_Unicodes unicodes,
+ FT_UInt32 char_code )
+ {
+ TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ return psnames->unicodes_char_index( unicodes, char_code );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ tt_cmap_unicode_char_next( PS_Unicodes unicodes,
+ FT_UInt32 *pchar_code )
+ {
+ TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ return psnames->unicodes_char_next( unicodes, pchar_code );
+ }
+
+
+ FT_DEFINE_TT_CMAP(
+ tt_cmap_unicode_class_rec,
+
+ sizeof ( PS_UnicodesRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_unicode_init, /* init */
+ (FT_CMap_DoneFunc) tt_cmap_unicode_done, /* done */
+ (FT_CMap_CharIndexFunc)tt_cmap_unicode_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) tt_cmap_unicode_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL, /* variantchar_list */
+
+ ~0U,
+ (TT_CMap_ValidateFunc)NULL, /* validate */
+ (TT_CMap_Info_GetFunc)NULL /* get_cmap_info */
+ )
+
+#endif /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+
+ static const TT_CMap_Class tt_cmap_classes[] =
+ {
+#undef TTCMAPCITEM
+#define TTCMAPCITEM( a ) &a,
+#include "ttcmapc.h"
+ NULL,
+ };
+
+
+ /* parse the `cmap' table and build the corresponding TT_CMap objects */
+ /* in the current face */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_build_cmaps( TT_Face face )
+ {
+ FT_Byte* const table = face->cmap_table;
+ FT_Byte* limit;
+ FT_UInt volatile num_cmaps;
+ FT_Byte* volatile p = table;
+ FT_Library library = FT_FACE_LIBRARY( face );
+
+ FT_UNUSED( library );
+
+
+ if ( !p || face->cmap_size < 4 )
+ return FT_THROW( Invalid_Table );
+
+ /* Version 1.8.3 of the OpenType specification contains the following */
+ /* (https://docs.microsoft.com/en-us/typography/opentype/spec/cmap): */
+ /* */
+ /* The 'cmap' table version number remains at 0x0000 for fonts that */
+ /* make use of the newer subtable formats. */
+ /* */
+ /* This essentially means that a version format test is useless. */
+
+ /* ignore format */
+ p += 2;
+
+ num_cmaps = TT_NEXT_USHORT( p );
+ FT_TRACE4(( "tt_face_build_cmaps: %d cmaps\n", num_cmaps ));
+
+ limit = table + face->cmap_size;
+ for ( ; num_cmaps > 0 && p + 8 <= limit; num_cmaps-- )
+ {
+ FT_CharMapRec charmap;
+ FT_UInt32 offset;
+
+
+ charmap.platform_id = TT_NEXT_USHORT( p );
+ charmap.encoding_id = TT_NEXT_USHORT( p );
+ charmap.face = FT_FACE( face );
+ charmap.encoding = FT_ENCODING_NONE; /* will be filled later */
+ offset = TT_NEXT_ULONG( p );
+
+ if ( offset && offset <= face->cmap_size - 2 )
+ {
+ FT_Byte* volatile cmap = table + offset;
+ volatile FT_UInt format = TT_PEEK_USHORT( cmap );
+ const TT_CMap_Class* volatile pclazz = tt_cmap_classes;
+ TT_CMap_Class volatile clazz;
+
+
+ for ( ; *pclazz; pclazz++ )
+ {
+ clazz = *pclazz;
+ if ( clazz->format == format )
+ {
+ volatile TT_ValidatorRec valid;
+ volatile FT_Error error = FT_Err_Ok;
+
+
+ ft_validator_init( FT_VALIDATOR( &valid ), cmap, limit,
+ FT_VALIDATE_DEFAULT );
+
+ valid.num_glyphs = (FT_UInt)face->max_profile.numGlyphs;
+
+ if ( ft_setjmp( FT_VALIDATOR( &valid )->jump_buffer) == 0 )
+ {
+ /* validate this cmap sub-table */
+ error = clazz->validate( cmap, FT_VALIDATOR( &valid ) );
+ }
+
+ if ( !valid.validator.error )
+ {
+ FT_CMap ttcmap;
+
+
+ /* It might make sense to store the single variation */
+ /* selector cmap somewhere special. But it would have to be */
+ /* in the public FT_FaceRec, and we can't change that. */
+
+ if ( !FT_CMap_New( (FT_CMap_Class)clazz,
+ cmap, &charmap, &ttcmap ) )
+ {
+ /* it is simpler to directly set `flags' than adding */
+ /* a parameter to FT_CMap_New */
+ ((TT_CMap)ttcmap)->flags = (FT_Int)error;
+ }
+ }
+ else
+ {
+ FT_TRACE0(( "tt_face_build_cmaps:"
+ " broken cmap sub-table ignored\n" ));
+ }
+ break;
+ }
+ }
+
+ if ( !*pclazz )
+ {
+ FT_TRACE0(( "tt_face_build_cmaps:"
+ " unsupported cmap sub-table ignored\n" ));
+ }
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_get_cmap_info( FT_CharMap charmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_CMap cmap = (FT_CMap)charmap;
+ TT_CMap_Class clazz = (TT_CMap_Class)cmap->clazz;
+
+
+ if ( clazz->get_cmap_info )
+ return clazz->get_cmap_info( charmap, cmap_info );
+ else
+ return FT_THROW( Invalid_CharMap_Format );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttcmap.h b/modules/freetype2/src/sfnt/ttcmap.h
new file mode 100644
index 0000000000..ff52917ed5
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttcmap.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+ *
+ * ttcmap.h
+ *
+ * TrueType character mapping table (cmap) support (specification).
+ *
+ * Copyright (C) 2002-2023 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.
+ *
+ */
+
+
+#ifndef TTCMAP_H_
+#define TTCMAP_H_
+
+
+#include <freetype/internal/tttypes.h>
+#include <freetype/internal/ftvalid.h>
+#include <freetype/internal/services/svttcmap.h>
+
+FT_BEGIN_HEADER
+
+
+#define TT_CMAP_FLAG_UNSORTED 1
+#define TT_CMAP_FLAG_OVERLAPPING 2
+
+ typedef struct TT_CMapRec_
+ {
+ FT_CMapRec cmap;
+ FT_Byte* data; /* pointer to in-memory cmap table */
+ FT_Int flags; /* for format 4 only */
+
+ } TT_CMapRec, *TT_CMap;
+
+ typedef const struct TT_CMap_ClassRec_* TT_CMap_Class;
+
+
+ typedef FT_Error
+ (*TT_CMap_ValidateFunc)( FT_Byte* data,
+ FT_Validator valid );
+
+ typedef struct TT_CMap_ClassRec_
+ {
+ FT_CMap_ClassRec clazz;
+ FT_UInt format;
+ TT_CMap_ValidateFunc validate;
+ TT_CMap_Info_GetFunc get_cmap_info;
+
+ } TT_CMap_ClassRec;
+
+
+#define FT_DEFINE_TT_CMAP( class_, \
+ size_, \
+ init_, \
+ done_, \
+ char_index_, \
+ char_next_, \
+ char_var_index_, \
+ char_var_default_, \
+ variant_list_, \
+ charvariant_list_, \
+ variantchar_list_, \
+ format_, \
+ validate_, \
+ get_cmap_info_ ) \
+ FT_CALLBACK_TABLE_DEF \
+ const TT_CMap_ClassRec class_ = \
+ { \
+ { size_, \
+ init_, \
+ done_, \
+ char_index_, \
+ char_next_, \
+ char_var_index_, \
+ char_var_default_, \
+ variant_list_, \
+ charvariant_list_, \
+ variantchar_list_ \
+ }, \
+ \
+ format_, \
+ validate_, \
+ get_cmap_info_ \
+ };
+
+
+#undef TTCMAPCITEM
+#define TTCMAPCITEM( a ) FT_CALLBACK_TABLE const TT_CMap_ClassRec a;
+#include "ttcmapc.h"
+
+
+ typedef struct TT_ValidatorRec_
+ {
+ FT_ValidatorRec validator;
+ FT_UInt num_glyphs;
+
+ } TT_ValidatorRec, *TT_Validator;
+
+
+#define TT_VALIDATOR( x ) ( (TT_Validator)( x ) )
+#define TT_VALID_GLYPH_COUNT( x ) TT_VALIDATOR( x )->num_glyphs
+
+
+ FT_CALLBACK_TABLE const TT_CMap_ClassRec tt_cmap_unicode_class_rec;
+
+ FT_LOCAL( FT_Error )
+ tt_face_build_cmaps( TT_Face face );
+
+ /* used in tt-cmaps service */
+ FT_LOCAL( FT_Error )
+ tt_get_cmap_info( FT_CharMap charmap,
+ TT_CMapInfo *cmap_info );
+
+
+FT_END_HEADER
+
+#endif /* TTCMAP_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttcmapc.h b/modules/freetype2/src/sfnt/ttcmapc.h
new file mode 100644
index 0000000000..0af48c2478
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttcmapc.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+ *
+ * ttcmapc.h
+ *
+ * TT CMAP classes definitions (specification only).
+ *
+ * Copyright (C) 2009-2023 by
+ * Oran Agra and Mickey Gabel.
+ *
+ * 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.
+ *
+ */
+
+
+#ifdef TT_CONFIG_CMAP_FORMAT_0
+ TTCMAPCITEM( tt_cmap0_class_rec )
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_2
+ TTCMAPCITEM( tt_cmap2_class_rec )
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_4
+ TTCMAPCITEM( tt_cmap4_class_rec )
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_6
+ TTCMAPCITEM( tt_cmap6_class_rec )
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_8
+ TTCMAPCITEM( tt_cmap8_class_rec )
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_10
+ TTCMAPCITEM( tt_cmap10_class_rec )
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_12
+ TTCMAPCITEM( tt_cmap12_class_rec )
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_13
+ TTCMAPCITEM( tt_cmap13_class_rec )
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_14
+ TTCMAPCITEM( tt_cmap14_class_rec )
+#endif
+
+
+ /* END */
diff --git a/modules/freetype2/src/sfnt/ttcolr.c b/modules/freetype2/src/sfnt/ttcolr.c
new file mode 100644
index 0000000000..5d98dcab8f
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttcolr.c
@@ -0,0 +1,1921 @@
+/****************************************************************************
+ *
+ * ttcolr.c
+ *
+ * TrueType and OpenType colored glyph layer support (body).
+ *
+ * Copyright (C) 2018-2023 by
+ * David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg.
+ *
+ * Originally written by Shao Yu Zhang <shaozhang@fb.com>.
+ *
+ * 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.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * `COLR' table specification:
+ *
+ * https://www.microsoft.com/typography/otspec/colr.htm
+ *
+ */
+
+
+#include <freetype/internal/ftcalc.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+#include <freetype/ftcolor.h>
+#include <freetype/config/integer-types.h>
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include <freetype/internal/services/svmm.h>
+#endif
+
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+
+#include "ttcolr.h"
+
+
+ /* NOTE: These are the table sizes calculated through the specs. */
+#define BASE_GLYPH_SIZE 6U
+#define BASE_GLYPH_PAINT_RECORD_SIZE 6U
+#define LAYER_V1_LIST_PAINT_OFFSET_SIZE 4U
+#define LAYER_V1_LIST_NUM_LAYERS_SIZE 4U
+#define COLOR_STOP_SIZE 6U
+#define VAR_IDX_BASE_SIZE 4U
+#define LAYER_SIZE 4U
+/* https://docs.microsoft.com/en-us/typography/opentype/spec/colr#colr-header */
+/* 3 * uint16 + 2 * Offset32 */
+#define COLRV0_HEADER_SIZE 14U
+/* COLRV0_HEADER_SIZE + 5 * Offset32 */
+#define COLRV1_HEADER_SIZE 34U
+
+
+#define ENSURE_READ_BYTES( byte_size ) \
+ if ( p < colr->paints_start_v1 || \
+ p > (FT_Byte*)colr->table + colr->table_size - byte_size ) \
+ return 0
+
+
+ typedef enum FT_PaintFormat_Internal_
+ {
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SOLID = 3,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_LINEAR_GRADIENT = 5,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_RADIAL_GRADIENT = 7,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SWEEP_GRADIENT = 9,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_TRANSFORM = 13,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_TRANSLATE = 15,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE = 17,
+ FT_COLR_PAINTFORMAT_INTERNAL_SCALE_CENTER = 18,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_CENTER = 19,
+ FT_COLR_PAINTFORMAT_INTERNAL_SCALE_UNIFORM = 20,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_UNIFORM = 21,
+ FT_COLR_PAINTFORMAT_INTERNAL_SCALE_UNIFORM_CENTER = 22,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_UNIFORM_CENTER = 23,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE = 25,
+ FT_COLR_PAINTFORMAT_INTERNAL_ROTATE_CENTER = 26,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE_CENTER = 27,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW = 29,
+ FT_COLR_PAINTFORMAT_INTERNAL_SKEW_CENTER = 30,
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW_CENTER = 31,
+
+ } FT_PaintFormat_Internal;
+
+
+ typedef struct BaseGlyphRecord_
+ {
+ FT_UShort gid;
+ FT_UShort first_layer_index;
+ FT_UShort num_layers;
+
+ } BaseGlyphRecord;
+
+
+ typedef struct BaseGlyphV1Record_
+ {
+ FT_UShort gid;
+ /* Offset from start of BaseGlyphV1List, i.e., from base_glyphs_v1. */
+ FT_ULong paint_offset;
+
+ } BaseGlyphV1Record;
+
+
+ typedef struct Colr_
+ {
+ FT_UShort version;
+ FT_UShort num_base_glyphs;
+ FT_UShort num_layers;
+
+ FT_Byte* base_glyphs;
+ FT_Byte* layers;
+
+ FT_ULong num_base_glyphs_v1;
+ /* Points at beginning of BaseGlyphV1List. */
+ FT_Byte* base_glyphs_v1;
+
+ FT_ULong num_layers_v1;
+ FT_Byte* layers_v1;
+
+ FT_Byte* clip_list;
+
+ /*
+ * Paint tables start at the minimum of the end of the LayerList and the
+ * end of the BaseGlyphList. Record this location in a field here for
+ * safety checks when accessing paint tables.
+ */
+ FT_Byte* paints_start_v1;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ /* Item Variation Store for variable 'COLR' v1. */
+ GX_ItemVarStoreRec var_store;
+ GX_DeltaSetIdxMapRec delta_set_idx_map;
+#endif
+
+ /* The memory that backs up the `COLR' table. */
+ void* table;
+ FT_ULong table_size;
+
+ } Colr;
+
+
+ /**************************************************************************
+ *
+ * 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 ttcolr
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_colr( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = face->root.memory;
+
+ FT_Byte* table = NULL;
+ FT_Byte* p = NULL;
+ /* Needed for reading array lengths in referenced tables. */
+ FT_Byte* p1 = NULL;
+
+ Colr* colr = NULL;
+
+ FT_ULong base_glyph_offset, layer_offset;
+ FT_ULong base_glyphs_offset_v1, num_base_glyphs_v1;
+ FT_ULong layer_offset_v1, num_layers_v1, clip_list_offset;
+ FT_ULong table_size;
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ FT_ULong colr_offset_in_stream;
+#endif
+
+
+ /* `COLR' always needs `CPAL' */
+ if ( !face->cpal )
+ return FT_THROW( Invalid_File_Format );
+
+ error = face->goto_table( face, TTAG_COLR, stream, &table_size );
+ if ( error )
+ goto NoColr;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ colr_offset_in_stream = FT_STREAM_POS();
+#endif
+
+ if ( table_size < COLRV0_HEADER_SIZE )
+ goto NoColr;
+
+ if ( FT_FRAME_EXTRACT( table_size, table ) )
+ goto NoColr;
+
+ p = table;
+
+ if ( FT_NEW( colr ) )
+ goto NoColr;
+
+ colr->version = FT_NEXT_USHORT( p );
+ if ( colr->version != 0 && colr->version != 1 )
+ goto InvalidTable;
+
+ colr->num_base_glyphs = FT_NEXT_USHORT( p );
+ base_glyph_offset = FT_NEXT_ULONG( p );
+
+ if ( base_glyph_offset >= table_size )
+ goto InvalidTable;
+ if ( colr->num_base_glyphs * BASE_GLYPH_SIZE >
+ table_size - base_glyph_offset )
+ goto InvalidTable;
+
+ layer_offset = FT_NEXT_ULONG( p );
+ colr->num_layers = FT_NEXT_USHORT( p );
+
+ if ( layer_offset >= table_size )
+ goto InvalidTable;
+ if ( colr->num_layers * LAYER_SIZE > table_size - layer_offset )
+ goto InvalidTable;
+
+ if ( colr->version == 1 )
+ {
+ if ( table_size < COLRV1_HEADER_SIZE )
+ goto InvalidTable;
+
+ base_glyphs_offset_v1 = FT_NEXT_ULONG( p );
+
+ if ( base_glyphs_offset_v1 + 4 >= table_size )
+ goto InvalidTable;
+
+ p1 = (FT_Byte*)( table + base_glyphs_offset_v1 );
+ num_base_glyphs_v1 = FT_PEEK_ULONG( p1 );
+
+ if ( num_base_glyphs_v1 * BASE_GLYPH_PAINT_RECORD_SIZE >
+ table_size - base_glyphs_offset_v1 )
+ goto InvalidTable;
+
+ colr->num_base_glyphs_v1 = num_base_glyphs_v1;
+ colr->base_glyphs_v1 = p1;
+
+ layer_offset_v1 = FT_NEXT_ULONG( p );
+
+ if ( layer_offset_v1 >= table_size )
+ goto InvalidTable;
+
+ if ( layer_offset_v1 )
+ {
+ if ( layer_offset_v1 + 4 >= table_size )
+ goto InvalidTable;
+
+ p1 = (FT_Byte*)( table + layer_offset_v1 );
+ num_layers_v1 = FT_PEEK_ULONG( p1 );
+
+ if ( num_layers_v1 * LAYER_V1_LIST_PAINT_OFFSET_SIZE >
+ table_size - layer_offset_v1 )
+ goto InvalidTable;
+
+ colr->num_layers_v1 = num_layers_v1;
+ colr->layers_v1 = p1;
+
+ colr->paints_start_v1 =
+ FT_MIN( colr->base_glyphs_v1 +
+ colr->num_base_glyphs_v1 * BASE_GLYPH_PAINT_RECORD_SIZE,
+ colr->layers_v1 +
+ colr->num_layers_v1 * LAYER_V1_LIST_PAINT_OFFSET_SIZE );
+ }
+ else
+ {
+ colr->num_layers_v1 = 0;
+ colr->layers_v1 = 0;
+ colr->paints_start_v1 =
+ colr->base_glyphs_v1 +
+ colr->num_base_glyphs_v1 * BASE_GLYPH_PAINT_RECORD_SIZE;
+ }
+
+ clip_list_offset = FT_NEXT_ULONG( p );
+
+ if ( clip_list_offset >= table_size )
+ goto InvalidTable;
+
+ if ( clip_list_offset )
+ colr->clip_list = (FT_Byte*)( table + clip_list_offset );
+ else
+ colr->clip_list = 0;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ colr->var_store.dataCount = 0;
+ colr->var_store.varData = NULL;
+ colr->var_store.axisCount = 0;
+ colr->var_store.regionCount = 0;
+ colr->var_store.varRegionList = 0;
+
+ colr->delta_set_idx_map.mapCount = 0;
+ colr->delta_set_idx_map.outerIndex = NULL;
+ colr->delta_set_idx_map.innerIndex = NULL;
+
+ if ( face->variation_support & TT_FACE_FLAG_VAR_FVAR )
+ {
+ FT_ULong var_idx_map_offset, var_store_offset;
+
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+
+
+ var_idx_map_offset = FT_NEXT_ULONG( p );
+
+ if ( var_idx_map_offset >= table_size )
+ goto InvalidTable;
+
+ var_store_offset = FT_NEXT_ULONG( p );
+ if ( var_store_offset >= table_size )
+ goto InvalidTable;
+
+ if ( var_store_offset )
+ {
+ /* If variation info has not been initialized yet, try doing so, */
+ /* otherwise loading the variation store will fail as it */
+ /* requires access to `blend` for checking the number of axes. */
+ if ( !face->blend )
+ if ( mm->get_mm_var( FT_FACE( face ), NULL ) )
+ goto InvalidTable;
+
+ /* Try loading `VarIdxMap` and `VarStore`. */
+ error = mm->load_item_var_store(
+ FT_FACE( face ),
+ colr_offset_in_stream + var_store_offset,
+ &colr->var_store );
+ if ( error != FT_Err_Ok )
+ goto InvalidTable;
+ }
+
+ if ( colr->var_store.axisCount && var_idx_map_offset )
+ {
+ error = mm->load_delta_set_idx_map(
+ FT_FACE( face ),
+ colr_offset_in_stream + var_idx_map_offset,
+ &colr->delta_set_idx_map,
+ &colr->var_store,
+ table_size );
+ if ( error != FT_Err_Ok )
+ goto InvalidTable;
+ }
+ }
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+ }
+
+ colr->base_glyphs = (FT_Byte*)( table + base_glyph_offset );
+ colr->layers = (FT_Byte*)( table + layer_offset );
+ colr->table = table;
+ colr->table_size = table_size;
+
+ face->colr = colr;
+
+ return FT_Err_Ok;
+
+ InvalidTable:
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ {
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+
+
+ mm->done_delta_set_idx_map( FT_FACE( face ),
+ &colr->delta_set_idx_map );
+ mm->done_item_var_store( FT_FACE( face ),
+ &colr->var_store );
+ }
+#endif
+
+ error = FT_THROW( Invalid_Table );
+
+ NoColr:
+ FT_FRAME_RELEASE( table );
+ FT_FREE( colr );
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_colr( TT_Face face )
+ {
+ FT_Stream stream = face->root.stream;
+ FT_Memory memory = face->root.memory;
+
+ Colr* colr = (Colr*)face->colr;
+
+
+ if ( colr )
+ {
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ {
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+
+
+ mm->done_delta_set_idx_map( FT_FACE( face ),
+ &colr->delta_set_idx_map );
+ mm->done_item_var_store( FT_FACE( face ),
+ &colr->var_store );
+ }
+#endif
+ FT_FRAME_RELEASE( colr->table );
+ FT_FREE( colr );
+ }
+ }
+
+
+ static FT_Bool
+ find_base_glyph_record( FT_Byte* base_glyph_begin,
+ FT_UInt num_base_glyph,
+ FT_UInt glyph_id,
+ BaseGlyphRecord* record )
+ {
+ FT_UInt min = 0;
+ FT_UInt max = num_base_glyph;
+
+
+ while ( min < max )
+ {
+ FT_UInt mid = min + ( max - min ) / 2;
+ FT_Byte* p = base_glyph_begin + mid * BASE_GLYPH_SIZE;
+
+ FT_UShort gid = FT_NEXT_USHORT( p );
+
+
+ if ( gid < glyph_id )
+ min = mid + 1;
+ else if (gid > glyph_id )
+ max = mid;
+ else
+ {
+ record->gid = gid;
+ record->first_layer_index = FT_NEXT_USHORT( p );
+ record->num_layers = FT_NEXT_USHORT( p );
+
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ tt_face_get_colr_layer( TT_Face face,
+ FT_UInt base_glyph,
+ FT_UInt *aglyph_index,
+ FT_UInt *acolor_index,
+ FT_LayerIterator* iterator )
+ {
+ Colr* colr = (Colr*)face->colr;
+ BaseGlyphRecord glyph_record;
+
+
+ if ( !colr )
+ return 0;
+
+ if ( !iterator->p )
+ {
+ FT_ULong offset;
+
+
+ /* first call to function */
+ iterator->layer = 0;
+
+ if ( !find_base_glyph_record( colr->base_glyphs,
+ colr->num_base_glyphs,
+ base_glyph,
+ &glyph_record ) )
+ return 0;
+
+ if ( glyph_record.num_layers )
+ iterator->num_layers = glyph_record.num_layers;
+ else
+ return 0;
+
+ offset = LAYER_SIZE * glyph_record.first_layer_index;
+ if ( offset + LAYER_SIZE * glyph_record.num_layers > colr->table_size )
+ return 0;
+
+ iterator->p = colr->layers + offset;
+ }
+
+ if ( iterator->layer >= iterator->num_layers ||
+ iterator->p < colr->layers ||
+ iterator->p >= ( (FT_Byte*)colr->table + colr->table_size ) )
+ return 0;
+
+ *aglyph_index = FT_NEXT_USHORT( iterator->p );
+ *acolor_index = FT_NEXT_USHORT( iterator->p );
+
+ if ( *aglyph_index >= (FT_UInt)( FT_FACE( face )->num_glyphs ) ||
+ ( *acolor_index != 0xFFFF &&
+ *acolor_index >= face->palette_data.num_palette_entries ) )
+ return 0;
+
+ iterator->layer++;
+
+ return 1;
+ }
+
+
+ static FT_Bool
+ read_color_line( Colr* colr,
+ FT_Byte* color_line_p,
+ FT_ColorLine* colorline,
+ FT_Bool read_variable )
+ {
+ FT_Byte* p = color_line_p;
+ FT_PaintExtend paint_extend;
+
+
+ ENSURE_READ_BYTES( 3 );
+
+ paint_extend = (FT_PaintExtend)FT_NEXT_BYTE( p );
+ if ( paint_extend > FT_COLR_PAINT_EXTEND_REFLECT )
+ return 0;
+
+ colorline->extend = paint_extend;
+
+ colorline->color_stop_iterator.num_color_stops = FT_NEXT_USHORT( p );
+ colorline->color_stop_iterator.p = p;
+ colorline->color_stop_iterator.current_color_stop = 0;
+ colorline->color_stop_iterator.read_variable = read_variable;
+
+ return 1;
+ }
+
+
+ /*
+ * Read a paint offset for `FT_Paint*` objects that have them and check
+ * whether it is within reasonable limits within the font and the COLR
+ * table.
+ *
+ * Return 1 on success, 0 on failure.
+ */
+ static FT_Bool
+ get_child_table_pointer ( Colr* colr,
+ FT_Byte* paint_base,
+ FT_Byte** p,
+ FT_Byte** child_table_pointer )
+ {
+ FT_UInt32 paint_offset;
+ FT_Byte* child_table_p;
+
+
+ if ( !child_table_pointer )
+ return 0;
+
+ if ( *p < colr->paints_start_v1 ||
+ *p > (FT_Byte*)colr->table + colr->table_size - 1 - 3 )
+ return 0;
+
+ paint_offset = FT_NEXT_UOFF3( *p );
+ if ( !paint_offset )
+ return 0;
+
+ child_table_p = (FT_Byte*)( paint_base + paint_offset );
+
+ if ( child_table_p < colr->paints_start_v1 ||
+ child_table_p >= ( (FT_Byte*)colr->table + colr->table_size ) )
+ return 0;
+
+ *child_table_pointer = child_table_p;
+ return 1;
+ }
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+ static FT_Bool
+ get_deltas_for_var_index_base ( TT_Face face,
+ Colr* colr,
+ FT_ULong var_index_base,
+ FT_UInt num_deltas,
+ FT_ItemVarDelta* deltas )
+ {
+ FT_UInt outer_index = 0;
+ FT_UInt inner_index = 0;
+ FT_ULong loop_var_index = var_index_base;
+
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+
+ FT_UInt i = 0;
+
+
+ if ( var_index_base == 0xFFFFFFFF )
+ {
+ for ( i = 0; i < num_deltas; ++i )
+ deltas[i] = 0;
+ return 1;
+ }
+
+ for ( i = 0; i < num_deltas; ++i )
+ {
+ loop_var_index = var_index_base + i;
+
+ if ( colr->delta_set_idx_map.innerIndex )
+ {
+ if ( loop_var_index >= colr->delta_set_idx_map.mapCount )
+ loop_var_index = colr->delta_set_idx_map.mapCount - 1;
+
+ outer_index = colr->delta_set_idx_map.outerIndex[loop_var_index];
+ inner_index = colr->delta_set_idx_map.innerIndex[loop_var_index];
+ }
+ else
+ {
+ outer_index = 0;
+ inner_index = loop_var_index;
+ }
+
+ deltas[i] = mm->get_item_delta( FT_FACE( face ), &colr->var_store,
+ outer_index, inner_index );
+ }
+
+ return 1;
+ }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+ static FT_Bool
+ read_paint( TT_Face face,
+ Colr* colr,
+ FT_Byte* p,
+ FT_COLR_Paint* apaint )
+ {
+ FT_Byte* paint_base = p;
+ FT_Byte* child_table_p = NULL;
+ FT_Bool do_read_var = FALSE;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ FT_ULong var_index_base = 0;
+ /* Longest varIndexBase offset is 5 in the spec. */
+ FT_ItemVarDelta item_deltas[6] = { 0, 0, 0, 0, 0, 0 };
+#else
+ FT_UNUSED( face );
+#endif
+
+
+ if ( !p || !colr || !colr->table )
+ return 0;
+
+ /* The last byte of the 'COLR' table is at 'size-1'; subtract 1 of */
+ /* that to account for the expected format byte we are going to read. */
+ if ( p < colr->paints_start_v1 ||
+ p > (FT_Byte*)colr->table + colr->table_size - 2 )
+ return 0;
+
+ apaint->format = (FT_PaintFormat)FT_NEXT_BYTE( p );
+
+ if ( apaint->format >= FT_COLR_PAINT_FORMAT_MAX )
+ return 0;
+
+ if ( apaint->format == FT_COLR_PAINTFORMAT_COLR_LAYERS )
+ {
+ /* Initialize layer iterator/ */
+ FT_Byte num_layers;
+ FT_UInt32 first_layer_index;
+
+
+ num_layers = FT_NEXT_BYTE( p );
+ if ( num_layers > colr->num_layers_v1 )
+ return 0;
+
+ first_layer_index = FT_NEXT_ULONG( p );
+ if ( first_layer_index + num_layers > colr->num_layers_v1 )
+ return 0;
+
+ apaint->u.colr_layers.layer_iterator.num_layers = num_layers;
+ apaint->u.colr_layers.layer_iterator.layer = 0;
+ /* TODO: Check whether pointer is outside colr? */
+ apaint->u.colr_layers.layer_iterator.p =
+ colr->layers_v1 +
+ LAYER_V1_LIST_NUM_LAYERS_SIZE +
+ LAYER_V1_LIST_PAINT_OFFSET_SIZE * first_layer_index;
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_SOLID ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SOLID )
+ {
+ ENSURE_READ_BYTES( 4 );
+ apaint->u.solid.color.palette_index = FT_NEXT_USHORT( p );
+ apaint->u.solid.color.alpha = FT_NEXT_SHORT( p );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SOLID )
+ {
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG( p );
+
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 1,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.solid.color.alpha += item_deltas[0];
+ }
+#endif
+
+ apaint->format = FT_COLR_PAINTFORMAT_SOLID;
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_COLR_GLYPH )
+ {
+ ENSURE_READ_BYTES(2);
+ apaint->u.colr_glyph.glyphID = FT_NEXT_USHORT( p );
+
+ return 1;
+ }
+
+ /*
+ * Grouped below here are all paint formats that have an offset to a
+ * child paint table as the first entry (for example, a color line or a
+ * child paint table). Retrieve that and determine whether that paint
+ * offset is valid first.
+ */
+
+ if ( !get_child_table_pointer( colr, paint_base, &p, &child_table_p ) )
+ return 0;
+
+ if ( apaint->format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT ||
+ ( do_read_var =
+ ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_LINEAR_GRADIENT ) ) )
+ {
+ if ( !read_color_line( colr,
+ child_table_p,
+ &apaint->u.linear_gradient.colorline,
+ do_read_var ) )
+ return 0;
+
+ /*
+ * In order to support variations expose these as FT_Fixed 16.16
+ * values so that we can support fractional values after
+ * interpolation.
+ */
+ ENSURE_READ_BYTES( 12 );
+ apaint->u.linear_gradient.p0.x = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.linear_gradient.p0.y = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.linear_gradient.p1.x = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.linear_gradient.p1.y = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.linear_gradient.p2.x = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.linear_gradient.p2.y = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( do_read_var )
+ {
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG ( p );
+
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 6,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.linear_gradient.p0.x += INT_TO_FIXED( item_deltas[0] );
+ apaint->u.linear_gradient.p0.y += INT_TO_FIXED( item_deltas[1] );
+ apaint->u.linear_gradient.p1.x += INT_TO_FIXED( item_deltas[2] );
+ apaint->u.linear_gradient.p1.y += INT_TO_FIXED( item_deltas[3] );
+ apaint->u.linear_gradient.p2.x += INT_TO_FIXED( item_deltas[4] );
+ apaint->u.linear_gradient.p2.y += INT_TO_FIXED( item_deltas[5] );
+ }
+#endif
+
+ apaint->format = FT_COLR_PAINTFORMAT_LINEAR_GRADIENT;
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT ||
+ ( do_read_var =
+ ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_RADIAL_GRADIENT ) ) )
+ {
+ FT_Pos tmp;
+
+
+ if ( !read_color_line( colr,
+ child_table_p,
+ &apaint->u.radial_gradient.colorline,
+ do_read_var ) )
+ return 0;
+
+
+ /* In the OpenType specification, `r0` and `r1` are defined as */
+ /* `UFWORD`. Since FreeType doesn't have a corresponding 16.16 */
+ /* format we convert to `FWORD` and replace negative values with */
+ /* (32bit) `FT_INT_MAX`. */
+
+ ENSURE_READ_BYTES( 12 );
+
+ apaint->u.radial_gradient.c0.x = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.radial_gradient.c0.y = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+ tmp = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.radial_gradient.r0 = tmp < 0 ? FT_INT_MAX : tmp;
+
+ apaint->u.radial_gradient.c1.x = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.radial_gradient.c1.y = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+ tmp = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.radial_gradient.r1 = tmp < 0 ? FT_INT_MAX : tmp;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( do_read_var )
+ {
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG ( p );
+
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 6,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.radial_gradient.c0.x += INT_TO_FIXED( item_deltas[0] );
+ apaint->u.radial_gradient.c0.y += INT_TO_FIXED( item_deltas[1] );
+
+ // TODO: Anything to be done about UFWORD deltas here?
+ apaint->u.radial_gradient.r0 += INT_TO_FIXED( item_deltas[2] );
+
+ apaint->u.radial_gradient.c1.x += INT_TO_FIXED( item_deltas[3] );
+ apaint->u.radial_gradient.c1.y += INT_TO_FIXED( item_deltas[4] );
+
+ apaint->u.radial_gradient.r1 += INT_TO_FIXED( item_deltas[5] );
+ }
+#endif
+
+ apaint->format = FT_COLR_PAINTFORMAT_RADIAL_GRADIENT;
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT ||
+ ( do_read_var =
+ ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SWEEP_GRADIENT ) ) )
+ {
+ if ( !read_color_line( colr,
+ child_table_p,
+ &apaint->u.sweep_gradient.colorline,
+ do_read_var) )
+ return 0;
+
+ ENSURE_READ_BYTES( 8 );
+
+ apaint->u.sweep_gradient.center.x =
+ INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.sweep_gradient.center.y =
+ INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+ apaint->u.sweep_gradient.start_angle =
+ F2DOT14_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.sweep_gradient.end_angle =
+ F2DOT14_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( do_read_var )
+ {
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG ( p );
+
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 4,
+ item_deltas ) )
+ return 0;
+
+ // TODO: Handle overflow?
+ apaint->u.sweep_gradient.center.x += INT_TO_FIXED( item_deltas[0] );
+ apaint->u.sweep_gradient.center.y += INT_TO_FIXED( item_deltas[1] );
+
+ apaint->u.sweep_gradient.start_angle +=
+ F2DOT14_TO_FIXED( item_deltas[2] );
+ apaint->u.sweep_gradient.end_angle +=
+ F2DOT14_TO_FIXED( item_deltas[3] );
+ }
+#endif
+ apaint->format = FT_COLR_PAINTFORMAT_SWEEP_GRADIENT;
+
+ return 1;
+ }
+
+ if ( apaint->format == FT_COLR_PAINTFORMAT_GLYPH )
+ {
+ ENSURE_READ_BYTES( 2 );
+ apaint->u.glyph.paint.p = child_table_p;
+ apaint->u.glyph.paint.insert_root_transform = 0;
+ apaint->u.glyph.glyphID = FT_NEXT_USHORT( p );
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_TRANSFORM ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_TRANSFORM )
+ {
+ apaint->u.transform.paint.p = child_table_p;
+ apaint->u.transform.paint.insert_root_transform = 0;
+
+ if ( !get_child_table_pointer( colr, paint_base, &p, &child_table_p ) )
+ return 0;
+
+ p = child_table_p;
+
+ /*
+ * The following matrix coefficients are encoded as
+ * OpenType 16.16 fixed-point values.
+ */
+ ENSURE_READ_BYTES( 24 );
+ apaint->u.transform.affine.xx = FT_NEXT_LONG( p );
+ apaint->u.transform.affine.yx = FT_NEXT_LONG( p );
+ apaint->u.transform.affine.xy = FT_NEXT_LONG( p );
+ apaint->u.transform.affine.yy = FT_NEXT_LONG( p );
+ apaint->u.transform.affine.dx = FT_NEXT_LONG( p );
+ apaint->u.transform.affine.dy = FT_NEXT_LONG( p );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_TRANSFORM )
+ {
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG( p );
+
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 6,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.transform.affine.xx += (FT_Fixed)item_deltas[0];
+ apaint->u.transform.affine.yx += (FT_Fixed)item_deltas[1];
+ apaint->u.transform.affine.xy += (FT_Fixed)item_deltas[2];
+ apaint->u.transform.affine.yy += (FT_Fixed)item_deltas[3];
+ apaint->u.transform.affine.dx += (FT_Fixed)item_deltas[4];
+ apaint->u.transform.affine.dy += (FT_Fixed)item_deltas[5];
+ }
+#endif
+
+ apaint->format = FT_COLR_PAINTFORMAT_TRANSFORM;
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_TRANSLATE ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_TRANSLATE )
+ {
+ apaint->u.translate.paint.p = child_table_p;
+ apaint->u.translate.paint.insert_root_transform = 0;
+
+ ENSURE_READ_BYTES( 4 );
+ apaint->u.translate.dx = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.translate.dy = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_TRANSLATE )
+ {
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG( p );
+
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 2,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.translate.dx += INT_TO_FIXED( item_deltas[0] );
+ apaint->u.translate.dy += INT_TO_FIXED( item_deltas[1] );
+ }
+#endif
+
+ apaint->format = FT_COLR_PAINTFORMAT_TRANSLATE;
+
+ return 1;
+ }
+
+ else if ( apaint->format >= FT_COLR_PAINTFORMAT_SCALE &&
+ (FT_PaintFormat_Internal)apaint->format <=
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_UNIFORM_CENTER )
+ {
+ apaint->u.scale.paint.p = child_table_p;
+ apaint->u.scale.paint.insert_root_transform = 0;
+
+ /* All scale paints get at least one scale value. */
+ ENSURE_READ_BYTES( 2 );
+ apaint->u.scale.scale_x = F2DOT14_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+ /* Non-uniform ones read an extra y value. */
+ if ( apaint->format == FT_COLR_PAINTFORMAT_SCALE ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_SCALE_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_CENTER )
+ {
+ ENSURE_READ_BYTES( 2 );
+ apaint->u.scale.scale_y = F2DOT14_TO_FIXED( FT_NEXT_SHORT( p ) );
+ }
+ else
+ apaint->u.scale.scale_y = apaint->u.scale.scale_x;
+
+ /* Scale paints that have a center read center coordinates, */
+ /* otherwise the center is (0,0). */
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_SCALE_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_SCALE_UNIFORM_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_UNIFORM_CENTER )
+ {
+ ENSURE_READ_BYTES( 4 );
+ apaint->u.scale.center_x = INT_TO_FIXED( FT_NEXT_SHORT ( p ) );
+ apaint->u.scale.center_y = INT_TO_FIXED( FT_NEXT_SHORT ( p ) );
+ }
+ else
+ {
+ apaint->u.scale.center_x = 0;
+ apaint->u.scale.center_y = 0;
+ }
+
+ /* Base values set, now handle variations. */
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_UNIFORM ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_UNIFORM_CENTER )
+ {
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG( p );
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE )
+ {
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 2,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.scale.scale_x += F2DOT14_TO_FIXED( item_deltas[0] );
+ apaint->u.scale.scale_y += F2DOT14_TO_FIXED( item_deltas[1] );
+ }
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_CENTER )
+ {
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 4,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.scale.scale_x += F2DOT14_TO_FIXED( item_deltas[0] );
+ apaint->u.scale.scale_y += F2DOT14_TO_FIXED( item_deltas[1] );
+ apaint->u.scale.center_x += INT_TO_FIXED( item_deltas[2] );
+ apaint->u.scale.center_y += INT_TO_FIXED( item_deltas[3] );
+ }
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_UNIFORM )
+ {
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 1,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.scale.scale_x += F2DOT14_TO_FIXED( item_deltas[0] );
+ apaint->u.scale.scale_y += F2DOT14_TO_FIXED( item_deltas[0] );
+ }
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SCALE_UNIFORM_CENTER )
+ {
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 3,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.scale.scale_x += F2DOT14_TO_FIXED( item_deltas[0] );
+ apaint->u.scale.scale_y += F2DOT14_TO_FIXED( item_deltas[0] );
+ apaint->u.scale.center_x += INT_TO_FIXED( item_deltas[1] );
+ apaint->u.scale.center_y += INT_TO_FIXED( item_deltas[2] );
+ }
+ }
+#endif
+
+ /* FT 'COLR' v1 API output format always returns fully defined */
+ /* structs; we thus set the format to the public API value. */
+ apaint->format = FT_COLR_PAINTFORMAT_SCALE;
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_ROTATE ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_ROTATE_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE_CENTER )
+ {
+ apaint->u.rotate.paint.p = child_table_p;
+ apaint->u.rotate.paint.insert_root_transform = 0;
+
+ ENSURE_READ_BYTES( 2 );
+ apaint->u.rotate.angle = F2DOT14_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_ROTATE_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE_CENTER )
+ {
+ ENSURE_READ_BYTES( 4 );
+ apaint->u.rotate.center_x = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.rotate.center_y = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ }
+ else
+ {
+ apaint->u.rotate.center_x = 0;
+ apaint->u.rotate.center_y = 0;
+ }
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE_CENTER )
+ {
+ FT_UInt num_deltas = 0;
+
+
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG( p );
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE_CENTER )
+ num_deltas = 3;
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_ROTATE )
+ num_deltas = 1;
+
+ if ( num_deltas > 0 )
+ {
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base,
+ num_deltas, item_deltas ) )
+ return 0;
+
+ apaint->u.rotate.angle += F2DOT14_TO_FIXED( item_deltas[0] );
+
+ if ( num_deltas == 3 )
+ {
+ apaint->u.rotate.center_x += INT_TO_FIXED( item_deltas[1] );
+ apaint->u.rotate.center_y += INT_TO_FIXED( item_deltas[2] );
+ }
+ }
+ }
+#endif
+
+ apaint->format = FT_COLR_PAINTFORMAT_ROTATE;
+
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_SKEW ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_SKEW_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW_CENTER )
+ {
+ apaint->u.skew.paint.p = child_table_p;
+ apaint->u.skew.paint.insert_root_transform = 0;
+
+ ENSURE_READ_BYTES( 4 );
+ apaint->u.skew.x_skew_angle = F2DOT14_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.skew.y_skew_angle = F2DOT14_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_SKEW_CENTER ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW_CENTER )
+ {
+ ENSURE_READ_BYTES( 4 );
+ apaint->u.skew.center_x = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ apaint->u.skew.center_y = INT_TO_FIXED( FT_NEXT_SHORT( p ) );
+ }
+ else
+ {
+ apaint->u.skew.center_x = 0;
+ apaint->u.skew.center_y = 0;
+ }
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW ||
+ (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW_CENTER )
+ {
+ ENSURE_READ_BYTES( 4 );
+ var_index_base = FT_NEXT_ULONG( p );
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW )
+ {
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 2,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.skew.x_skew_angle += F2DOT14_TO_FIXED( item_deltas[0] );
+ apaint->u.skew.y_skew_angle += F2DOT14_TO_FIXED( item_deltas[1] );
+ }
+
+ if ( (FT_PaintFormat_Internal)apaint->format ==
+ FT_COLR_PAINTFORMAT_INTERNAL_VAR_SKEW_CENTER )
+ {
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 4,
+ item_deltas ) )
+ return 0;
+
+ apaint->u.skew.x_skew_angle += F2DOT14_TO_FIXED( item_deltas[0] );
+ apaint->u.skew.y_skew_angle += F2DOT14_TO_FIXED( item_deltas[1] );
+ apaint->u.skew.center_x += INT_TO_FIXED( item_deltas[2] );
+ apaint->u.skew.center_y += INT_TO_FIXED( item_deltas[3] );
+ }
+ }
+#endif
+
+ apaint->format = FT_COLR_PAINTFORMAT_SKEW;
+
+ return 1;
+ }
+
+ else if ( apaint->format == FT_COLR_PAINTFORMAT_COMPOSITE )
+ {
+ FT_UInt composite_mode;
+
+
+ apaint->u.composite.source_paint.p = child_table_p;
+ apaint->u.composite.source_paint.insert_root_transform = 0;
+
+ ENSURE_READ_BYTES( 1 );
+ composite_mode = FT_NEXT_BYTE( p );
+ if ( composite_mode >= FT_COLR_COMPOSITE_MAX )
+ return 0;
+
+ apaint->u.composite.composite_mode = (FT_Composite_Mode)composite_mode;
+
+ if ( !get_child_table_pointer( colr, paint_base, &p, &child_table_p ) )
+ return 0;
+
+ apaint->u.composite.backdrop_paint.p =
+ child_table_p;
+ apaint->u.composite.backdrop_paint.insert_root_transform =
+ 0;
+
+ return 1;
+ }
+
+ return 0;
+ }
+
+
+ static FT_Bool
+ find_base_glyph_v1_record( FT_Byte * base_glyph_begin,
+ FT_UInt num_base_glyph,
+ FT_UInt glyph_id,
+ BaseGlyphV1Record *record )
+ {
+ FT_UInt min = 0;
+ FT_UInt max = num_base_glyph;
+
+
+ while ( min < max )
+ {
+ FT_UInt mid = min + ( max - min ) / 2;
+
+ /*
+ * `base_glyph_begin` is the beginning of `BaseGlyphV1List`;
+ * skip `numBaseGlyphV1Records` by adding 4 to start binary search
+ * in the array of `BaseGlyphV1Record`.
+ */
+ FT_Byte *p = base_glyph_begin + 4 + mid * BASE_GLYPH_PAINT_RECORD_SIZE;
+
+ FT_UShort gid = FT_NEXT_USHORT( p );
+
+
+ if ( gid < glyph_id )
+ min = mid + 1;
+ else if (gid > glyph_id )
+ max = mid;
+ else
+ {
+ record->gid = gid;
+ record->paint_offset = FT_NEXT_ULONG ( p );
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ tt_face_get_colr_glyph_paint( TT_Face face,
+ FT_UInt base_glyph,
+ FT_Color_Root_Transform root_transform,
+ FT_OpaquePaint* opaque_paint )
+ {
+ Colr* colr = (Colr*)face->colr;
+ BaseGlyphV1Record base_glyph_v1_record;
+ FT_Byte* p;
+
+ if ( !colr || !colr->table )
+ return 0;
+
+ if ( colr->version < 1 || !colr->num_base_glyphs_v1 ||
+ !colr->base_glyphs_v1 )
+ return 0;
+
+ if ( opaque_paint->p )
+ return 0;
+
+ if ( !find_base_glyph_v1_record( colr->base_glyphs_v1,
+ colr->num_base_glyphs_v1,
+ base_glyph,
+ &base_glyph_v1_record ) )
+ return 0;
+
+ if ( !base_glyph_v1_record.paint_offset ||
+ base_glyph_v1_record.paint_offset > colr->table_size )
+ return 0;
+
+ p = (FT_Byte*)( colr->base_glyphs_v1 +
+ base_glyph_v1_record.paint_offset );
+ if ( p >= ( (FT_Byte*)colr->table + colr->table_size ) )
+ return 0;
+
+ opaque_paint->p = p;
+
+ if ( root_transform == FT_COLOR_INCLUDE_ROOT_TRANSFORM )
+ opaque_paint->insert_root_transform = 1;
+ else
+ opaque_paint->insert_root_transform = 0;
+
+ return 1;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ tt_face_get_color_glyph_clipbox( TT_Face face,
+ FT_UInt base_glyph,
+ FT_ClipBox* clip_box )
+ {
+ Colr* colr;
+
+ FT_Byte *p, *p1, *clip_base, *limit;
+
+ FT_Byte clip_list_format;
+ FT_ULong num_clip_boxes, i;
+ FT_UShort gid_start, gid_end;
+ FT_UInt32 clip_box_offset;
+ FT_Byte format;
+
+ const FT_Byte num_corners = 4;
+ FT_Vector corners[4];
+ FT_Byte j;
+ FT_BBox font_clip_box;
+
+
+ colr = (Colr*)face->colr;
+ if ( !colr )
+ return 0;
+
+ if ( !colr->clip_list )
+ return 0;
+
+ p = colr->clip_list;
+
+ /* Limit points to the first byte after the end of the color table. */
+ /* Thus, in subsequent limit checks below we need to check whether the */
+ /* read pointer is strictly greater than a position offset by certain */
+ /* field sizes to the left of that position. */
+ limit = (FT_Byte*)colr->table + colr->table_size;
+
+ /* Check whether we can extract one `uint8` and one `uint32`. */
+ if ( p > limit - ( 1 + 4 ) )
+ return 0;
+
+ clip_base = p;
+ clip_list_format = FT_NEXT_BYTE ( p );
+
+ /* Format byte used here to be able to upgrade ClipList for >16bit */
+ /* glyph ids; for now we can expect it to be 1. */
+ if ( !( clip_list_format == 1 ) )
+ return 0;
+
+ num_clip_boxes = FT_NEXT_ULONG( p );
+
+ /* Check whether we can extract two `uint16` and one `Offset24`, */
+ /* `num_clip_boxes` times. */
+ if ( colr->table_size / ( 2 + 2 + 3 ) < num_clip_boxes ||
+ p > limit - ( 2 + 2 + 3 ) * num_clip_boxes )
+ return 0;
+
+ for ( i = 0; i < num_clip_boxes; ++i )
+ {
+ gid_start = FT_NEXT_USHORT( p );
+ gid_end = FT_NEXT_USHORT( p );
+ clip_box_offset = FT_NEXT_UOFF3( p );
+
+ if ( base_glyph >= gid_start && base_glyph <= gid_end )
+ {
+ p1 = (FT_Byte*)( clip_base + clip_box_offset );
+
+ /* Check whether we can extract one `uint8`. */
+ if ( p1 > limit - 1 )
+ return 0;
+
+ format = FT_NEXT_BYTE( p1 );
+
+ if ( format > 2 )
+ return 0;
+
+ /* Check whether we can extract four `FWORD`. */
+ if ( p1 > limit - ( 2 + 2 + 2 + 2 ) )
+ return 0;
+
+ /* `face->root.size->metrics.x_scale` and `y_scale` are factors */
+ /* that scale a font unit value in integers to a 26.6 fixed value */
+ /* according to the requested size, see for example */
+ /* `ft_recompute_scaled_metrics`. */
+ font_clip_box.xMin = FT_MulFix( FT_NEXT_SHORT( p1 ),
+ face->root.size->metrics.x_scale );
+ font_clip_box.yMin = FT_MulFix( FT_NEXT_SHORT( p1 ),
+ face->root.size->metrics.y_scale );
+ font_clip_box.xMax = FT_MulFix( FT_NEXT_SHORT( p1 ),
+ face->root.size->metrics.x_scale );
+ font_clip_box.yMax = FT_MulFix( FT_NEXT_SHORT( p1 ),
+ face->root.size->metrics.y_scale );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( format == 2 )
+ {
+ FT_ULong var_index_base = 0;
+ /* varIndexBase offset for clipbox is 3 at most. */
+ FT_ItemVarDelta item_deltas[4] = { 0, 0, 0, 0 };
+
+
+ /* Check whether we can extract a 32-bit varIndexBase now. */
+ if ( p1 > limit - 4 )
+ return 0;
+
+ var_index_base = FT_NEXT_ULONG( p1 );
+
+ if ( !get_deltas_for_var_index_base( face, colr, var_index_base, 4,
+ item_deltas ) )
+ return 0;
+
+ font_clip_box.xMin +=
+ FT_MulFix( item_deltas[0], face->root.size->metrics.x_scale );
+ font_clip_box.yMin +=
+ FT_MulFix( item_deltas[1], face->root.size->metrics.y_scale );
+ font_clip_box.xMax +=
+ FT_MulFix( item_deltas[2], face->root.size->metrics.x_scale );
+ font_clip_box.yMax +=
+ FT_MulFix( item_deltas[3], face->root.size->metrics.y_scale );
+ }
+#endif
+
+ /* Make 4 corner points (xMin, yMin), (xMax, yMax) and transform */
+ /* them. If we we would only transform two corner points and */
+ /* span a rectangle based on those, the rectangle may become too */
+ /* small to cover the glyph. */
+ corners[0].x = font_clip_box.xMin;
+ corners[1].x = font_clip_box.xMin;
+ corners[2].x = font_clip_box.xMax;
+ corners[3].x = font_clip_box.xMax;
+
+ corners[0].y = font_clip_box.yMin;
+ corners[1].y = font_clip_box.yMax;
+ corners[2].y = font_clip_box.yMax;
+ corners[3].y = font_clip_box.yMin;
+
+ for ( j = 0; j < num_corners; ++j )
+ {
+ if ( face->root.internal->transform_flags & 1 )
+ FT_Vector_Transform( &corners[j],
+ &face->root.internal->transform_matrix );
+
+ if ( face->root.internal->transform_flags & 2 )
+ {
+ corners[j].x += face->root.internal->transform_delta.x;
+ corners[j].y += face->root.internal->transform_delta.y;
+ }
+ }
+
+ clip_box->bottom_left = corners[0];
+ clip_box->top_left = corners[1];
+ clip_box->top_right = corners[2];
+ clip_box->bottom_right = corners[3];
+
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ tt_face_get_paint_layers( TT_Face face,
+ FT_LayerIterator* iterator,
+ FT_OpaquePaint* opaque_paint )
+ {
+ FT_Byte* p = NULL;
+ FT_Byte* p_first_layer = NULL;
+ FT_Byte* p_paint = NULL;
+ FT_UInt32 paint_offset;
+
+ Colr* colr;
+
+
+ if ( iterator->layer == iterator->num_layers )
+ return 0;
+
+ colr = (Colr*)face->colr;
+ if ( !colr )
+ return 0;
+
+ /*
+ * We have an iterator pointing at a paint offset as part of the
+ * `paintOffset` array in `LayerV1List`.
+ */
+ p = iterator->p;
+
+ /*
+ * Do a cursor sanity check of the iterator. Counting backwards from
+ * where it stands, we need to end up at a position after the beginning
+ * of the `LayerV1List` table and not after the end of the
+ * `LayerV1List`.
+ */
+ p_first_layer = p -
+ iterator->layer * LAYER_V1_LIST_PAINT_OFFSET_SIZE -
+ LAYER_V1_LIST_NUM_LAYERS_SIZE;
+ if ( p_first_layer < (FT_Byte*)colr->layers_v1 )
+ return 0;
+ if ( p_first_layer >= (FT_Byte*)(
+ colr->layers_v1 + LAYER_V1_LIST_NUM_LAYERS_SIZE +
+ colr->num_layers_v1 * LAYER_V1_LIST_PAINT_OFFSET_SIZE ) )
+ return 0;
+
+ /*
+ * Before reading, ensure that `p` is within 'COLR' v1 and we can read a
+ * 4-byte ULONG.
+ */
+ if ( p < colr->layers_v1 ||
+ p > (FT_Byte*)colr->table + colr->table_size - 4 )
+ return 0;
+
+ paint_offset =
+ FT_NEXT_ULONG( p );
+ opaque_paint->insert_root_transform =
+ 0;
+
+ p_paint = (FT_Byte*)( colr->layers_v1 + paint_offset );
+
+ if ( p_paint < colr->paints_start_v1 ||
+ p_paint >= ( (FT_Byte*)colr->table + colr->table_size ) )
+ return 0;
+
+ opaque_paint->p = p_paint;
+
+ iterator->p = p;
+
+ iterator->layer++;
+
+ return 1;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ tt_face_get_colorline_stops( TT_Face face,
+ FT_ColorStop* color_stop,
+ FT_ColorStopIterator *iterator )
+ {
+ Colr* colr = (Colr*)face->colr;
+
+ FT_Byte* p;
+ FT_ULong var_index_base;
+ FT_Byte* last_entry_p = NULL;
+ FT_UInt entry_size = COLOR_STOP_SIZE;
+
+
+ if ( !colr || !colr->table || !iterator )
+ return 0;
+
+ if ( iterator->current_color_stop >= iterator->num_color_stops )
+ return 0;
+
+ if ( iterator->read_variable )
+ entry_size += VAR_IDX_BASE_SIZE;
+
+ /* Calculate the start pointer for the last to-be-read (Var)ColorStop */
+ /* and check whether we can read a full (Var)ColorStop at that */
+ /* position by comparing it to the position that is the size of one */
+ /* (Var)ColorStop before the end of the 'COLR' table. */
+ last_entry_p =
+ iterator->p + ( iterator->num_color_stops - 1 -
+ iterator->current_color_stop ) * entry_size;
+ if ( iterator->p < colr->paints_start_v1 ||
+ last_entry_p > (FT_Byte*)colr->table +
+ colr->table_size - entry_size )
+ return 0;
+
+ /* Iterator points at first `ColorStop` of `ColorLine`. */
+ p = iterator->p;
+
+ color_stop->stop_offset = F2DOT14_TO_FIXED( FT_NEXT_SHORT( p ) );
+
+ color_stop->color.palette_index = FT_NEXT_USHORT( p );
+
+ color_stop->color.alpha = FT_NEXT_SHORT( p );
+
+ if ( iterator->read_variable )
+ {
+ /* Pointer p needs to be advanced independently of whether we intend */
+ /* to take variable deltas into account or not. Otherwise iteration */
+ /* would fail due to wrong offsets. */
+ var_index_base = FT_NEXT_ULONG( p );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ {
+ FT_Int item_deltas[2];
+
+
+ if ( !get_deltas_for_var_index_base( face, colr,
+ var_index_base,
+ 2,
+ item_deltas ) )
+ return 0;
+
+ color_stop->stop_offset += F2DOT14_TO_FIXED( item_deltas[0] );
+ color_stop->color.alpha += item_deltas[1];
+ }
+#else
+ FT_UNUSED( var_index_base );
+#endif
+ }
+
+ iterator->p = p;
+ iterator->current_color_stop++;
+
+ return 1;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ tt_face_get_paint( TT_Face face,
+ FT_OpaquePaint opaque_paint,
+ FT_COLR_Paint* paint )
+ {
+ Colr* colr = (Colr*)face->colr;
+ FT_OpaquePaint next_paint;
+ FT_Matrix ft_root_scale;
+
+ if ( !colr || !colr->base_glyphs_v1 || !colr->table )
+ return 0;
+
+ if ( opaque_paint.insert_root_transform )
+ {
+ /* 'COLR' v1 glyph information is returned in unscaled coordinates,
+ * i.e., `FT_Size` is not applied or multiplied into the values. When
+ * client applications draw color glyphs, they can request to include
+ * a top-level transform, which includes the active `x_scale` and
+ * `y_scale` information for scaling the glyph, as well the additional
+ * transform and translate configured through `FT_Set_Transform`.
+ * This allows client applications to apply this top-level transform
+ * to the graphics context first and only once, then have gradient and
+ * contour scaling applied correctly when performing the additional
+ * drawing operations for subsequenct paints. Prepare this initial
+ * transform here.
+ */
+ paint->format = FT_COLR_PAINTFORMAT_TRANSFORM;
+
+ next_paint.p = opaque_paint.p;
+ next_paint.insert_root_transform = 0;
+ paint->u.transform.paint = next_paint;
+
+ /* `x_scale` and `y_scale` are in 26.6 format, representing the scale
+ * factor to get from font units to requested size. However, expected
+ * return values are in 16.16, so we shift accordingly with rounding.
+ */
+ ft_root_scale.xx = ( face->root.size->metrics.x_scale + 32 ) >> 6;
+ ft_root_scale.xy = 0;
+ ft_root_scale.yx = 0;
+ ft_root_scale.yy = ( face->root.size->metrics.y_scale + 32 ) >> 6;
+
+ if ( face->root.internal->transform_flags & 1 )
+ FT_Matrix_Multiply( &face->root.internal->transform_matrix,
+ &ft_root_scale );
+
+ paint->u.transform.affine.xx = ft_root_scale.xx;
+ paint->u.transform.affine.xy = ft_root_scale.xy;
+ paint->u.transform.affine.yx = ft_root_scale.yx;
+ paint->u.transform.affine.yy = ft_root_scale.yy;
+
+ /* The translation is specified in 26.6 format and, according to the
+ * documentation of `FT_Set_Translate`, is performed on the character
+ * size given in the last call to `FT_Set_Char_Size`. The
+ * 'PaintTransform' paint table's `FT_Affine23` format expects
+ * values in 16.16 format, thus we need to shift by 10 bits.
+ */
+ if ( face->root.internal->transform_flags & 2 )
+ {
+ paint->u.transform.affine.dx =
+ face->root.internal->transform_delta.x * ( 1 << 10 );
+ paint->u.transform.affine.dy =
+ face->root.internal->transform_delta.y * ( 1 << 10 );
+ }
+ else
+ {
+ paint->u.transform.affine.dx = 0;
+ paint->u.transform.affine.dy = 0;
+ }
+
+ return 1;
+ }
+
+ return read_paint( face, colr, opaque_paint.p, paint );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_colr_blend_layer( TT_Face face,
+ FT_UInt color_index,
+ FT_GlyphSlot dstSlot,
+ FT_GlyphSlot srcSlot )
+ {
+ FT_Error error;
+
+ FT_UInt x, y;
+ FT_Byte b, g, r, alpha;
+
+ FT_ULong size;
+ FT_Byte* src;
+ FT_Byte* dst;
+
+
+ if ( !dstSlot->bitmap.buffer )
+ {
+ /* Initialize destination of color bitmap */
+ /* with the size of first component. */
+ dstSlot->bitmap_left = srcSlot->bitmap_left;
+ dstSlot->bitmap_top = srcSlot->bitmap_top;
+
+ dstSlot->bitmap.width = srcSlot->bitmap.width;
+ dstSlot->bitmap.rows = srcSlot->bitmap.rows;
+ dstSlot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
+ dstSlot->bitmap.pitch = (int)dstSlot->bitmap.width * 4;
+ dstSlot->bitmap.num_grays = 256;
+
+ size = dstSlot->bitmap.rows * (unsigned int)dstSlot->bitmap.pitch;
+
+ error = ft_glyphslot_alloc_bitmap( dstSlot, size );
+ if ( error )
+ return error;
+
+ FT_MEM_ZERO( dstSlot->bitmap.buffer, size );
+ }
+ else
+ {
+ /* Resize destination if needed such that new component fits. */
+ FT_Int x_min, x_max, y_min, y_max;
+
+
+ x_min = FT_MIN( dstSlot->bitmap_left, srcSlot->bitmap_left );
+ x_max = FT_MAX( dstSlot->bitmap_left + (FT_Int)dstSlot->bitmap.width,
+ srcSlot->bitmap_left + (FT_Int)srcSlot->bitmap.width );
+
+ y_min = FT_MIN( dstSlot->bitmap_top - (FT_Int)dstSlot->bitmap.rows,
+ srcSlot->bitmap_top - (FT_Int)srcSlot->bitmap.rows );
+ y_max = FT_MAX( dstSlot->bitmap_top, srcSlot->bitmap_top );
+
+ if ( x_min != dstSlot->bitmap_left ||
+ x_max != dstSlot->bitmap_left + (FT_Int)dstSlot->bitmap.width ||
+ y_min != dstSlot->bitmap_top - (FT_Int)dstSlot->bitmap.rows ||
+ y_max != dstSlot->bitmap_top )
+ {
+ FT_Memory memory = face->root.memory;
+
+ FT_UInt width = (FT_UInt)( x_max - x_min );
+ FT_UInt rows = (FT_UInt)( y_max - y_min );
+ FT_UInt pitch = width * 4;
+
+ FT_Byte* buf = NULL;
+ FT_Byte* p;
+ FT_Byte* q;
+
+
+ size = rows * pitch;
+ if ( FT_ALLOC( buf, size ) )
+ return error;
+
+ p = dstSlot->bitmap.buffer;
+ q = buf +
+ (int)pitch * ( y_max - dstSlot->bitmap_top ) +
+ 4 * ( dstSlot->bitmap_left - x_min );
+
+ for ( y = 0; y < dstSlot->bitmap.rows; y++ )
+ {
+ FT_MEM_COPY( q, p, dstSlot->bitmap.width * 4 );
+
+ p += dstSlot->bitmap.pitch;
+ q += pitch;
+ }
+
+ ft_glyphslot_set_bitmap( dstSlot, buf );
+
+ dstSlot->bitmap_top = y_max;
+ dstSlot->bitmap_left = x_min;
+
+ dstSlot->bitmap.width = width;
+ dstSlot->bitmap.rows = rows;
+ dstSlot->bitmap.pitch = (int)pitch;
+
+ dstSlot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+ dstSlot->format = FT_GLYPH_FORMAT_BITMAP;
+ }
+ }
+
+ if ( color_index == 0xFFFF )
+ {
+ if ( face->have_foreground_color )
+ {
+ b = face->foreground_color.blue;
+ g = face->foreground_color.green;
+ r = face->foreground_color.red;
+ alpha = face->foreground_color.alpha;
+ }
+ else
+ {
+ if ( face->palette_data.palette_flags &&
+ ( face->palette_data.palette_flags[face->palette_index] &
+ FT_PALETTE_FOR_DARK_BACKGROUND ) )
+ {
+ /* white opaque */
+ b = 0xFF;
+ g = 0xFF;
+ r = 0xFF;
+ alpha = 0xFF;
+ }
+ else
+ {
+ /* black opaque */
+ b = 0x00;
+ g = 0x00;
+ r = 0x00;
+ alpha = 0xFF;
+ }
+ }
+ }
+ else
+ {
+ b = face->palette[color_index].blue;
+ g = face->palette[color_index].green;
+ r = face->palette[color_index].red;
+ alpha = face->palette[color_index].alpha;
+ }
+
+ /* XXX Convert if srcSlot.bitmap is not grey? */
+ src = srcSlot->bitmap.buffer;
+ dst = dstSlot->bitmap.buffer +
+ dstSlot->bitmap.pitch * ( dstSlot->bitmap_top - srcSlot->bitmap_top ) +
+ 4 * ( srcSlot->bitmap_left - dstSlot->bitmap_left );
+
+ for ( y = 0; y < srcSlot->bitmap.rows; y++ )
+ {
+ for ( x = 0; x < srcSlot->bitmap.width; x++ )
+ {
+ int aa = src[x];
+ int fa = alpha * aa / 255;
+
+ int fb = b * fa / 255;
+ int fg = g * fa / 255;
+ int fr = r * fa / 255;
+
+ int ba2 = 255 - fa;
+
+ int bb = dst[4 * x + 0];
+ int bg = dst[4 * x + 1];
+ int br = dst[4 * x + 2];
+ int ba = dst[4 * x + 3];
+
+
+ dst[4 * x + 0] = (FT_Byte)( bb * ba2 / 255 + fb );
+ dst[4 * x + 1] = (FT_Byte)( bg * ba2 / 255 + fg );
+ dst[4 * x + 2] = (FT_Byte)( br * ba2 / 255 + fr );
+ dst[4 * x + 3] = (FT_Byte)( ba * ba2 / 255 + fa );
+ }
+
+ src += srcSlot->bitmap.pitch;
+ dst += dstSlot->bitmap.pitch;
+ }
+
+ return FT_Err_Ok;
+ }
+
+#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _tt_colr_dummy;
+
+#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+/* EOF */
diff --git a/modules/freetype2/src/sfnt/ttcolr.h b/modules/freetype2/src/sfnt/ttcolr.h
new file mode 100644
index 0000000000..20c85f0359
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttcolr.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+ *
+ * ttcolr.h
+ *
+ * TrueType and OpenType colored glyph layer support (specification).
+ *
+ * Copyright (C) 2018-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * Originally written by Shao Yu Zhang <shaozhang@fb.com>.
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef __TTCOLR_H__
+#define __TTCOLR_H__
+
+
+#include "ttload.h"
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_colr( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_free_colr( TT_Face face );
+
+ FT_LOCAL( FT_Bool )
+ tt_face_get_colr_layer( TT_Face face,
+ FT_UInt base_glyph,
+ FT_UInt *aglyph_index,
+ FT_UInt *acolor_index,
+ FT_LayerIterator* iterator );
+
+ FT_LOCAL( FT_Bool )
+ tt_face_get_colr_glyph_paint( TT_Face face,
+ FT_UInt base_glyph,
+ FT_Color_Root_Transform root_transform,
+ FT_OpaquePaint* paint );
+
+ FT_LOCAL( FT_Bool )
+ tt_face_get_color_glyph_clipbox( TT_Face face,
+ FT_UInt base_glyph,
+ FT_ClipBox* clip_box );
+
+ FT_LOCAL( FT_Bool )
+ tt_face_get_paint_layers( TT_Face face,
+ FT_LayerIterator* iterator,
+ FT_OpaquePaint* paint );
+
+ FT_LOCAL( FT_Bool )
+ tt_face_get_colorline_stops( TT_Face face,
+ FT_ColorStop* color_stop,
+ FT_ColorStopIterator* iterator );
+
+ FT_LOCAL( FT_Bool )
+ tt_face_get_paint( TT_Face face,
+ FT_OpaquePaint opaque_paint,
+ FT_COLR_Paint* paint );
+
+ FT_LOCAL( FT_Error )
+ tt_face_colr_blend_layer( TT_Face face,
+ FT_UInt color_index,
+ FT_GlyphSlot dstSlot,
+ FT_GlyphSlot srcSlot );
+
+
+FT_END_HEADER
+
+
+#endif /* __TTCOLR_H__ */
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttcpal.c b/modules/freetype2/src/sfnt/ttcpal.c
new file mode 100644
index 0000000000..4279bc0bd1
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttcpal.c
@@ -0,0 +1,310 @@
+/****************************************************************************
+ *
+ * ttcpal.c
+ *
+ * TrueType and OpenType color palette support (body).
+ *
+ * Copyright (C) 2018-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * Originally written by Shao Yu Zhang <shaozhang@fb.com>.
+ *
+ * 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.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * `CPAL' table specification:
+ *
+ * https://www.microsoft.com/typography/otspec/cpal.htm
+ *
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+#include <freetype/ftcolor.h>
+
+
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+
+#include "ttcpal.h"
+
+
+ /* NOTE: These are the table sizes calculated through the specs. */
+#define CPAL_V0_HEADER_BASE_SIZE 12U
+#define COLOR_SIZE 4U
+
+
+ /* all data from `CPAL' not covered in FT_Palette_Data */
+ typedef struct Cpal_
+ {
+ FT_UShort version; /* Table version number (0 or 1 supported). */
+ FT_UShort num_colors; /* Total number of color records, */
+ /* combined for all palettes. */
+ FT_Byte* colors; /* RGBA array of colors */
+ FT_Byte* color_indices; /* Index of each palette's first color record */
+ /* in the combined color record array. */
+
+ /* The memory which backs up the `CPAL' table. */
+ void* table;
+ FT_ULong table_size;
+
+ } Cpal;
+
+
+ /**************************************************************************
+ *
+ * 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 ttcpal
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_cpal( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = face->root.memory;
+
+ FT_Byte* table = NULL;
+ FT_Byte* p = NULL;
+
+ Cpal* cpal = NULL;
+
+ FT_ULong colors_offset;
+ FT_ULong table_size;
+
+
+ error = face->goto_table( face, TTAG_CPAL, stream, &table_size );
+ if ( error )
+ goto NoCpal;
+
+ if ( table_size < CPAL_V0_HEADER_BASE_SIZE )
+ goto InvalidTable;
+
+ if ( FT_FRAME_EXTRACT( table_size, table ) )
+ goto NoCpal;
+
+ p = table;
+
+ if ( FT_NEW( cpal ) )
+ goto NoCpal;
+
+ cpal->version = FT_NEXT_USHORT( p );
+ if ( cpal->version > 1 )
+ goto InvalidTable;
+
+ face->palette_data.num_palette_entries = FT_NEXT_USHORT( p );
+ face->palette_data.num_palettes = FT_NEXT_USHORT( p );
+
+ cpal->num_colors = FT_NEXT_USHORT( p );
+ colors_offset = FT_NEXT_ULONG( p );
+
+ if ( CPAL_V0_HEADER_BASE_SIZE +
+ face->palette_data.num_palettes * 2U > table_size )
+ goto InvalidTable;
+
+ if ( colors_offset >= table_size )
+ goto InvalidTable;
+ if ( cpal->num_colors * COLOR_SIZE > table_size - colors_offset )
+ goto InvalidTable;
+
+ if ( face->palette_data.num_palette_entries > cpal->num_colors )
+ goto InvalidTable;
+
+ cpal->color_indices = p;
+ cpal->colors = (FT_Byte*)( table + colors_offset );
+
+ if ( cpal->version == 1 )
+ {
+ FT_ULong type_offset, label_offset, entry_label_offset;
+ FT_UShort* array = NULL;
+ FT_UShort* limit;
+ FT_UShort* q;
+
+
+ if ( CPAL_V0_HEADER_BASE_SIZE +
+ face->palette_data.num_palettes * 2U +
+ 3U * 4 > table_size )
+ goto InvalidTable;
+
+ p += face->palette_data.num_palettes * 2U;
+
+ type_offset = FT_NEXT_ULONG( p );
+ label_offset = FT_NEXT_ULONG( p );
+ entry_label_offset = FT_NEXT_ULONG( p );
+
+ if ( type_offset )
+ {
+ if ( type_offset >= table_size )
+ goto InvalidTable;
+ if ( face->palette_data.num_palettes * 2U >
+ table_size - type_offset )
+ goto InvalidTable;
+
+ if ( FT_QNEW_ARRAY( array, face->palette_data.num_palettes ) )
+ goto NoCpal;
+
+ p = table + type_offset;
+ q = array;
+ limit = q + face->palette_data.num_palettes;
+
+ while ( q < limit )
+ *q++ = FT_NEXT_USHORT( p );
+
+ face->palette_data.palette_flags = array;
+ }
+
+ if ( label_offset )
+ {
+ if ( label_offset >= table_size )
+ goto InvalidTable;
+ if ( face->palette_data.num_palettes * 2U >
+ table_size - label_offset )
+ goto InvalidTable;
+
+ if ( FT_QNEW_ARRAY( array, face->palette_data.num_palettes ) )
+ goto NoCpal;
+
+ p = table + label_offset;
+ q = array;
+ limit = q + face->palette_data.num_palettes;
+
+ while ( q < limit )
+ *q++ = FT_NEXT_USHORT( p );
+
+ face->palette_data.palette_name_ids = array;
+ }
+
+ if ( entry_label_offset )
+ {
+ if ( entry_label_offset >= table_size )
+ goto InvalidTable;
+ if ( face->palette_data.num_palette_entries * 2U >
+ table_size - entry_label_offset )
+ goto InvalidTable;
+
+ if ( FT_QNEW_ARRAY( array, face->palette_data.num_palette_entries ) )
+ goto NoCpal;
+
+ p = table + entry_label_offset;
+ q = array;
+ limit = q + face->palette_data.num_palette_entries;
+
+ while ( q < limit )
+ *q++ = FT_NEXT_USHORT( p );
+
+ face->palette_data.palette_entry_name_ids = array;
+ }
+ }
+
+ cpal->table = table;
+ cpal->table_size = table_size;
+
+ face->cpal = cpal;
+
+ /* set up default palette */
+ if ( FT_NEW_ARRAY( face->palette,
+ face->palette_data.num_palette_entries ) )
+ goto NoCpal;
+
+ if ( tt_face_palette_set( face, 0 ) )
+ goto InvalidTable;
+
+ return FT_Err_Ok;
+
+ InvalidTable:
+ error = FT_THROW( Invalid_Table );
+
+ NoCpal:
+ FT_FRAME_RELEASE( table );
+ FT_FREE( cpal );
+
+ face->cpal = NULL;
+
+ /* arrays in `face->palette_data' and `face->palette' */
+ /* are freed in `sfnt_done_face' */
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_cpal( TT_Face face )
+ {
+ FT_Stream stream = face->root.stream;
+ FT_Memory memory = face->root.memory;
+
+ Cpal* cpal = (Cpal*)face->cpal;
+
+
+ if ( cpal )
+ {
+ FT_FRAME_RELEASE( cpal->table );
+ FT_FREE( cpal );
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_palette_set( TT_Face face,
+ FT_UInt palette_index )
+ {
+ Cpal* cpal = (Cpal*)face->cpal;
+
+ FT_Byte* offset;
+ FT_Byte* p;
+
+ FT_Color* q;
+ FT_Color* limit;
+
+ FT_UShort color_index;
+
+
+ if ( !cpal || palette_index >= face->palette_data.num_palettes )
+ return FT_THROW( Invalid_Argument );
+
+ offset = cpal->color_indices + 2 * palette_index;
+ color_index = FT_PEEK_USHORT( offset );
+
+ if ( color_index + face->palette_data.num_palette_entries >
+ cpal->num_colors )
+ return FT_THROW( Invalid_Table );
+
+ p = cpal->colors + COLOR_SIZE * color_index;
+ q = face->palette;
+ limit = q + face->palette_data.num_palette_entries;
+
+ while ( q < limit )
+ {
+ q->blue = FT_NEXT_BYTE( p );
+ q->green = FT_NEXT_BYTE( p );
+ q->red = FT_NEXT_BYTE( p );
+ q->alpha = FT_NEXT_BYTE( p );
+
+ q++;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _tt_cpal_dummy;
+
+#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+/* EOF */
diff --git a/modules/freetype2/src/sfnt/ttcpal.h b/modules/freetype2/src/sfnt/ttcpal.h
new file mode 100644
index 0000000000..8e9913f0cc
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttcpal.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+ *
+ * ttcpal.h
+ *
+ * TrueType and OpenType color palette support (specification).
+ *
+ * Copyright (C) 2018-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * Originally written by Shao Yu Zhang <shaozhang@fb.com>.
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef __TTCPAL_H__
+#define __TTCPAL_H__
+
+
+#include "ttload.h"
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_cpal( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_free_cpal( TT_Face face );
+
+ FT_LOCAL( FT_Error )
+ tt_face_palette_set( TT_Face face,
+ FT_UInt palette_index );
+
+
+FT_END_HEADER
+
+
+#endif /* __TTCPAL_H__ */
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttkern.c b/modules/freetype2/src/sfnt/ttkern.c
new file mode 100644
index 0000000000..a47d08bd6d
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttkern.c
@@ -0,0 +1,317 @@
+/****************************************************************************
+ *
+ * ttkern.c
+ *
+ * Load the basic TrueType kerning table. This doesn't handle
+ * kerning data within the GPOS table at the moment.
+ *
+ * Copyright (C) 1996-2023 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 <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+#include "ttkern.h"
+
+#include "sferrors.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 ttkern
+
+
+#undef TT_KERN_INDEX
+#define TT_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) )
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_kern( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_ULong table_size;
+ FT_Byte* p;
+ FT_Byte* p_limit;
+ FT_UInt nn, num_tables;
+ FT_UInt32 avail = 0, ordered = 0;
+
+
+ /* the kern table is optional; exit silently if it is missing */
+ error = face->goto_table( face, TTAG_kern, stream, &table_size );
+ if ( error )
+ goto Exit;
+
+ if ( table_size < 4 ) /* the case of a malformed table */
+ {
+ FT_ERROR(( "tt_face_load_kern:"
+ " kerning table is too small - ignored\n" ));
+ error = FT_THROW( Table_Missing );
+ goto Exit;
+ }
+
+ if ( FT_FRAME_EXTRACT( table_size, face->kern_table ) )
+ {
+ FT_ERROR(( "tt_face_load_kern:"
+ " could not extract kerning table\n" ));
+ goto Exit;
+ }
+
+ face->kern_table_size = table_size;
+
+ p = face->kern_table;
+ p_limit = p + table_size;
+
+ p += 2; /* skip version */
+ num_tables = FT_NEXT_USHORT( p );
+
+ if ( num_tables > 32 ) /* we only support up to 32 sub-tables */
+ num_tables = 32;
+
+ for ( nn = 0; nn < num_tables; nn++ )
+ {
+ FT_UInt num_pairs, length, coverage, format;
+ FT_Byte* p_next;
+ FT_UInt32 mask = (FT_UInt32)1UL << nn;
+
+
+ if ( p + 6 > p_limit )
+ break;
+
+ p_next = p;
+
+ p += 2; /* skip version */
+ length = FT_NEXT_USHORT( p );
+ coverage = FT_NEXT_USHORT( p );
+
+ if ( length <= 6 + 8 )
+ break;
+
+ p_next += length;
+
+ if ( p_next > p_limit ) /* handle broken table */
+ p_next = p_limit;
+
+ format = coverage >> 8;
+
+ /* we currently only support format 0 kerning tables */
+ if ( format != 0 )
+ goto NextTable;
+
+ /* only use horizontal kerning tables */
+ if ( ( coverage & 3U ) != 0x0001 ||
+ p + 8 > p_next )
+ goto NextTable;
+
+ num_pairs = FT_NEXT_USHORT( p );
+ p += 6;
+
+ if ( ( p_next - p ) < 6 * (int)num_pairs ) /* handle broken count */
+ num_pairs = (FT_UInt)( ( p_next - p ) / 6 );
+
+ avail |= mask;
+
+ /*
+ * Now check whether the pairs in this table are ordered.
+ * We then can use binary search.
+ */
+ if ( num_pairs > 0 )
+ {
+ FT_ULong count;
+ FT_ULong old_pair;
+
+
+ old_pair = FT_NEXT_ULONG( p );
+ p += 2;
+
+ for ( count = num_pairs - 1; count > 0; count-- )
+ {
+ FT_UInt32 cur_pair;
+
+
+ cur_pair = FT_NEXT_ULONG( p );
+ if ( cur_pair < old_pair )
+ break;
+
+ p += 2;
+ old_pair = cur_pair;
+ }
+
+ if ( count == 0 )
+ ordered |= mask;
+ }
+
+ NextTable:
+ p = p_next;
+ }
+
+ face->num_kern_tables = nn;
+ face->kern_avail_bits = avail;
+ face->kern_order_bits = ordered;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_done_kern( TT_Face face )
+ {
+ FT_Stream stream = face->root.stream;
+
+
+ FT_FRAME_RELEASE( face->kern_table );
+ face->kern_table_size = 0;
+ face->num_kern_tables = 0;
+ face->kern_avail_bits = 0;
+ face->kern_order_bits = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ tt_face_get_kerning( TT_Face face,
+ FT_UInt left_glyph,
+ FT_UInt right_glyph )
+ {
+ FT_Int result = 0;
+ FT_UInt count, mask;
+
+ FT_Byte* p;
+ FT_Byte* p_limit;
+
+
+ if ( !face->kern_table )
+ return result;
+
+ p = face->kern_table;
+ p_limit = p + face->kern_table_size;
+
+ p += 4;
+ mask = 0x0001;
+
+ for ( count = face->num_kern_tables;
+ count > 0 && p + 6 <= p_limit;
+ count--, mask <<= 1 )
+ {
+ FT_Byte* base = p;
+ FT_Byte* next;
+ FT_UInt version = FT_NEXT_USHORT( p );
+ FT_UInt length = FT_NEXT_USHORT( p );
+ FT_UInt coverage = FT_NEXT_USHORT( p );
+ FT_UInt num_pairs;
+ FT_Int value = 0;
+
+ FT_UNUSED( version );
+
+
+ next = base + length;
+
+ if ( next > p_limit ) /* handle broken table */
+ next = p_limit;
+
+ if ( ( face->kern_avail_bits & mask ) == 0 )
+ goto NextTable;
+
+ FT_ASSERT( p + 8 <= next ); /* tested in tt_face_load_kern */
+
+ num_pairs = FT_NEXT_USHORT( p );
+ p += 6;
+
+ if ( ( next - p ) < 6 * (int)num_pairs ) /* handle broken count */
+ num_pairs = (FT_UInt)( ( next - p ) / 6 );
+
+ switch ( coverage >> 8 )
+ {
+ case 0:
+ {
+ FT_ULong key0 = TT_KERN_INDEX( left_glyph, right_glyph );
+
+
+ if ( face->kern_order_bits & mask ) /* binary search */
+ {
+ FT_UInt min = 0;
+ FT_UInt max = num_pairs;
+
+
+ while ( min < max )
+ {
+ FT_UInt mid = ( min + max ) >> 1;
+ FT_Byte* q = p + 6 * mid;
+ FT_ULong key;
+
+
+ key = FT_NEXT_ULONG( q );
+
+ if ( key == key0 )
+ {
+ value = FT_PEEK_SHORT( q );
+ goto Found;
+ }
+ if ( key < key0 )
+ min = mid + 1;
+ else
+ max = mid;
+ }
+ }
+ else /* linear search */
+ {
+ FT_UInt count2;
+
+
+ for ( count2 = num_pairs; count2 > 0; count2-- )
+ {
+ FT_ULong key = FT_NEXT_ULONG( p );
+
+
+ if ( key == key0 )
+ {
+ value = FT_PEEK_SHORT( p );
+ goto Found;
+ }
+ p += 2;
+ }
+ }
+ }
+ break;
+
+ /*
+ * We don't support format 2 because we haven't seen a single font
+ * using it in real life...
+ */
+
+ default:
+ ;
+ }
+
+ goto NextTable;
+
+ Found:
+ if ( coverage & 8 ) /* override or add */
+ result = value;
+ else
+ result += value;
+
+ NextTable:
+ p = next;
+ }
+
+ return result;
+ }
+
+#undef TT_KERN_INDEX
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttkern.h b/modules/freetype2/src/sfnt/ttkern.h
new file mode 100644
index 0000000000..960c7da494
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttkern.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+ *
+ * ttkern.h
+ *
+ * Load the basic TrueType kerning table. This doesn't handle
+ * kerning data within the GPOS table at the moment.
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#ifndef TTKERN_H_
+#define TTKERN_H_
+
+
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/tttypes.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_kern( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_done_kern( TT_Face face );
+
+ FT_LOCAL( FT_Int )
+ tt_face_get_kerning( TT_Face face,
+ FT_UInt left_glyph,
+ FT_UInt right_glyph );
+
+#define TT_FACE_HAS_KERNING( face ) ( (face)->kern_avail_bits != 0 )
+
+
+FT_END_HEADER
+
+#endif /* TTKERN_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttload.c b/modules/freetype2/src/sfnt/ttload.c
new file mode 100644
index 0000000000..14f625c824
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttload.c
@@ -0,0 +1,1489 @@
+/****************************************************************************
+ *
+ * ttload.c
+ *
+ * Load the basic TrueType tables, i.e., tables that can be either in
+ * TTF or OTF fonts (body).
+ *
+ * Copyright (C) 1996-2023 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 <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+#include "ttload.h"
+
+#include "sferrors.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 ttload
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_lookup_table
+ *
+ * @Description:
+ * Looks for a TrueType table by name.
+ *
+ * @Input:
+ * face ::
+ * A face object handle.
+ *
+ * tag ::
+ * The searched tag.
+ *
+ * @Return:
+ * A pointer to the table directory entry. 0 if not found.
+ */
+ FT_LOCAL_DEF( TT_Table )
+ tt_face_lookup_table( TT_Face face,
+ FT_ULong tag )
+ {
+ TT_Table entry;
+ TT_Table limit;
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_Bool zero_length = FALSE;
+#endif
+
+
+ FT_TRACE4(( "tt_face_lookup_table: %p, `%c%c%c%c' -- ",
+ (void *)face,
+ (FT_Char)( tag >> 24 ),
+ (FT_Char)( tag >> 16 ),
+ (FT_Char)( tag >> 8 ),
+ (FT_Char)( tag ) ));
+
+ entry = face->dir_tables;
+ limit = entry + face->num_tables;
+
+ for ( ; entry < limit; entry++ )
+ {
+ /* For compatibility with Windows, we consider */
+ /* zero-length tables the same as missing tables. */
+ if ( entry->Tag == tag )
+ {
+ if ( entry->Length != 0 )
+ {
+ FT_TRACE4(( "found table.\n" ));
+ return entry;
+ }
+#ifdef FT_DEBUG_LEVEL_TRACE
+ zero_length = TRUE;
+#endif
+ }
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( zero_length )
+ FT_TRACE4(( "ignoring empty table\n" ));
+ else
+ FT_TRACE4(( "could not find table\n" ));
+#endif
+
+ return NULL;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_goto_table
+ *
+ * @Description:
+ * Looks for a TrueType table by name, then seek a stream to it.
+ *
+ * @Input:
+ * face ::
+ * A face object handle.
+ *
+ * tag ::
+ * The searched tag.
+ *
+ * stream ::
+ * The stream to seek when the table is found.
+ *
+ * @Output:
+ * length ::
+ * The length of the table if found, undefined otherwise.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_goto_table( TT_Face face,
+ FT_ULong tag,
+ FT_Stream stream,
+ FT_ULong* length )
+ {
+ TT_Table table;
+ FT_Error error;
+
+
+ table = tt_face_lookup_table( face, tag );
+ if ( table )
+ {
+ if ( length )
+ *length = table->Length;
+
+ if ( FT_STREAM_SEEK( table->Offset ) )
+ goto Exit;
+ }
+ else
+ error = FT_THROW( Table_Missing );
+
+ Exit:
+ return error;
+ }
+
+
+ /* Here, we */
+ /* */
+ /* - check that `num_tables' is valid (and adjust it if necessary); */
+ /* also return the number of valid table entries */
+ /* */
+ /* - look for a `head' table, check its size, and parse it to check */
+ /* whether its `magic' field is correctly set */
+ /* */
+ /* - errors (except errors returned by stream handling) */
+ /* */
+ /* SFNT_Err_Unknown_File_Format: */
+ /* no table is defined in directory, it is not sfnt-wrapped */
+ /* data */
+ /* SFNT_Err_Table_Missing: */
+ /* table directory is valid, but essential tables */
+ /* (head/bhed/SING) are missing */
+ /* */
+ static FT_Error
+ check_table_dir( SFNT_Header sfnt,
+ FT_Stream stream,
+ FT_UShort* valid )
+ {
+ FT_Error error;
+ FT_UShort nn, valid_entries = 0;
+ FT_UInt has_head = 0, has_sing = 0, has_meta = 0;
+ FT_ULong offset = sfnt->offset + 12;
+
+ static const FT_Frame_Field table_dir_entry_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_TableRec
+
+ FT_FRAME_START( 16 ),
+ FT_FRAME_ULONG( Tag ),
+ FT_FRAME_ULONG( CheckSum ),
+ FT_FRAME_ULONG( Offset ),
+ FT_FRAME_ULONG( Length ),
+ FT_FRAME_END
+ };
+
+
+ if ( FT_STREAM_SEEK( offset ) )
+ goto Exit;
+
+ for ( nn = 0; nn < sfnt->num_tables; nn++ )
+ {
+ TT_TableRec table;
+
+
+ if ( FT_STREAM_READ_FIELDS( table_dir_entry_fields, &table ) )
+ {
+ FT_TRACE2(( "check_table_dir:"
+ " can read only %hu table%s in font (instead of %hu)\n",
+ nn, nn == 1 ? "" : "s", sfnt->num_tables ));
+ sfnt->num_tables = nn;
+ break;
+ }
+
+ /* we ignore invalid tables */
+
+ if ( table.Offset > stream->size )
+ {
+ FT_TRACE2(( "check_table_dir: table entry %hu invalid\n", nn ));
+ continue;
+ }
+ else if ( table.Length > stream->size - table.Offset )
+ {
+ /* Some tables have such a simple structure that clipping its */
+ /* contents is harmless. This also makes FreeType less sensitive */
+ /* to invalid table lengths (which programs like Acroread seem to */
+ /* ignore in general). */
+
+ if ( table.Tag == TTAG_hmtx ||
+ table.Tag == TTAG_vmtx )
+ valid_entries++;
+ else
+ {
+ FT_TRACE2(( "check_table_dir: table entry %hu invalid\n", nn ));
+ continue;
+ }
+ }
+ else
+ valid_entries++;
+
+ if ( table.Tag == TTAG_head || table.Tag == TTAG_bhed )
+ {
+ FT_UInt32 magic;
+
+
+#ifndef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+ if ( table.Tag == TTAG_head )
+#endif
+ has_head = 1;
+
+ /*
+ * The table length should be 0x36, but certain font tools make it
+ * 0x38, so we will just check that it is greater.
+ *
+ * Note that according to the specification, the table must be
+ * padded to 32-bit lengths, but this doesn't apply to the value of
+ * its `Length' field!
+ *
+ */
+ if ( table.Length < 0x36 )
+ {
+ FT_TRACE2(( "check_table_dir:"
+ " `head' or `bhed' table too small\n" ));
+ error = FT_THROW( Table_Missing );
+ goto Exit;
+ }
+
+ if ( FT_STREAM_SEEK( table.Offset + 12 ) ||
+ FT_READ_ULONG( magic ) )
+ goto Exit;
+
+ if ( magic != 0x5F0F3CF5UL )
+ FT_TRACE2(( "check_table_dir:"
+ " invalid magic number in `head' or `bhed' table\n"));
+
+ if ( FT_STREAM_SEEK( offset + ( nn + 1 ) * 16 ) )
+ goto Exit;
+ }
+ else if ( table.Tag == TTAG_SING )
+ has_sing = 1;
+ else if ( table.Tag == TTAG_META )
+ has_meta = 1;
+ }
+
+ *valid = valid_entries;
+
+ if ( !valid_entries )
+ {
+ FT_TRACE2(( "check_table_dir: no valid tables found\n" ));
+ error = FT_THROW( Unknown_File_Format );
+ goto Exit;
+ }
+
+ /* if `sing' and `meta' tables are present, there is no `head' table */
+ if ( has_head || ( has_sing && has_meta ) )
+ {
+ error = FT_Err_Ok;
+ goto Exit;
+ }
+ else
+ {
+ FT_TRACE2(( "check_table_dir:" ));
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+ FT_TRACE2(( " neither `head', `bhed', nor `sing' table found\n" ));
+#else
+ FT_TRACE2(( " neither `head' nor `sing' table found\n" ));
+#endif
+ error = FT_THROW( Table_Missing );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_font_dir
+ *
+ * @Description:
+ * Loads the header of a SFNT font file.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * The input stream.
+ *
+ * @Output:
+ * sfnt ::
+ * The SFNT header.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ *
+ * @Note:
+ * The stream cursor must be at the beginning of the font directory.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_font_dir( TT_Face face,
+ FT_Stream stream )
+ {
+ SFNT_HeaderRec sfnt;
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_UShort nn, valid_entries = 0;
+
+ static const FT_Frame_Field offset_table_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE SFNT_HeaderRec
+
+ FT_FRAME_START( 8 ),
+ FT_FRAME_USHORT( num_tables ),
+ FT_FRAME_USHORT( search_range ),
+ FT_FRAME_USHORT( entry_selector ),
+ FT_FRAME_USHORT( range_shift ),
+ FT_FRAME_END
+ };
+
+
+ FT_TRACE2(( "tt_face_load_font_dir: %p\n", (void *)face ));
+
+ /* read the offset table */
+
+ sfnt.offset = FT_STREAM_POS();
+
+ if ( FT_READ_ULONG( sfnt.format_tag ) ||
+ FT_STREAM_READ_FIELDS( offset_table_fields, &sfnt ) )
+ goto Exit;
+
+ /* many fonts don't have these fields set correctly */
+#if 0
+ if ( sfnt.search_range != 1 << ( sfnt.entry_selector + 4 ) ||
+ sfnt.search_range + sfnt.range_shift != sfnt.num_tables << 4 )
+ return FT_THROW( Unknown_File_Format );
+#endif
+
+ /* load the table directory */
+
+ FT_TRACE2(( "-- Number of tables: %10hu\n", sfnt.num_tables ));
+ FT_TRACE2(( "-- Format version: 0x%08lx\n", sfnt.format_tag ));
+
+ if ( sfnt.format_tag != TTAG_OTTO )
+ {
+ /* check first */
+ error = check_table_dir( &sfnt, stream, &valid_entries );
+ if ( error )
+ {
+ FT_TRACE2(( "tt_face_load_font_dir:"
+ " invalid table directory for TrueType\n" ));
+ goto Exit;
+ }
+ }
+ else
+ {
+ valid_entries = sfnt.num_tables;
+ if ( !valid_entries )
+ {
+ FT_TRACE2(( "tt_face_load_font_dir: no valid tables found\n" ));
+ error = FT_THROW( Unknown_File_Format );
+ goto Exit;
+ }
+ }
+
+ face->num_tables = valid_entries;
+ face->format_tag = sfnt.format_tag;
+
+ if ( FT_QNEW_ARRAY( face->dir_tables, face->num_tables ) )
+ goto Exit;
+
+ if ( FT_STREAM_SEEK( sfnt.offset + 12 ) ||
+ FT_FRAME_ENTER( sfnt.num_tables * 16L ) )
+ goto Exit;
+
+ FT_TRACE2(( "\n" ));
+ FT_TRACE2(( " tag offset length checksum\n" ));
+ FT_TRACE2(( " ----------------------------------\n" ));
+
+ valid_entries = 0;
+ for ( nn = 0; nn < sfnt.num_tables; nn++ )
+ {
+ TT_TableRec entry;
+ FT_UShort i;
+ FT_Bool duplicate;
+
+
+ entry.Tag = FT_GET_TAG4();
+ entry.CheckSum = FT_GET_ULONG();
+ entry.Offset = FT_GET_ULONG();
+ entry.Length = FT_GET_ULONG();
+
+ /* ignore invalid tables that can't be sanitized */
+
+ if ( entry.Offset > stream->size )
+ continue;
+ else if ( entry.Length > stream->size - entry.Offset )
+ {
+ if ( entry.Tag == TTAG_hmtx ||
+ entry.Tag == TTAG_vmtx )
+ {
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_ULong old_length = entry.Length;
+#endif
+
+
+ /* make metrics table length a multiple of 4 */
+ entry.Length = ( stream->size - entry.Offset ) & ~3U;
+
+ FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx"
+ " (sanitized; original length %08lx)",
+ (FT_Char)( entry.Tag >> 24 ),
+ (FT_Char)( entry.Tag >> 16 ),
+ (FT_Char)( entry.Tag >> 8 ),
+ (FT_Char)( entry.Tag ),
+ entry.Offset,
+ entry.Length,
+ entry.CheckSum,
+ old_length ));
+ }
+ else
+ continue;
+ }
+#ifdef FT_DEBUG_LEVEL_TRACE
+ else
+ FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx",
+ (FT_Char)( entry.Tag >> 24 ),
+ (FT_Char)( entry.Tag >> 16 ),
+ (FT_Char)( entry.Tag >> 8 ),
+ (FT_Char)( entry.Tag ),
+ entry.Offset,
+ entry.Length,
+ entry.CheckSum ));
+#endif
+
+ /* ignore duplicate tables – the first one wins */
+ duplicate = 0;
+ for ( i = 0; i < valid_entries; i++ )
+ {
+ if ( face->dir_tables[i].Tag == entry.Tag )
+ {
+ duplicate = 1;
+ break;
+ }
+ }
+ if ( duplicate )
+ {
+ FT_TRACE2(( " (duplicate, ignored)\n" ));
+ continue;
+ }
+ else
+ {
+ FT_TRACE2(( "\n" ));
+
+ /* we finally have a valid entry */
+ face->dir_tables[valid_entries++] = entry;
+ }
+ }
+
+ /* final adjustment to number of tables */
+ face->num_tables = valid_entries;
+
+ FT_FRAME_EXIT();
+
+ FT_TRACE2(( "table directory loaded\n" ));
+ FT_TRACE2(( "\n" ));
+
+ Exit:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_any
+ *
+ * @Description:
+ * Loads any font table into client memory.
+ *
+ * @Input:
+ * face ::
+ * The face object to look for.
+ *
+ * tag ::
+ * The tag of table to load. Use the value 0 if you want
+ * to access the whole font file, else set this parameter
+ * to a valid TrueType table tag that you can forge with
+ * the MAKE_TT_TAG macro.
+ *
+ * offset ::
+ * The starting offset in the table (or the file if
+ * tag == 0).
+ *
+ * length ::
+ * The address of the decision variable:
+ *
+ * If length == NULL:
+ * Loads the whole table. Returns an error if
+ * `offset' == 0!
+ *
+ * If *length == 0:
+ * Exits immediately; returning the length of the given
+ * table or of the font file, depending on the value of
+ * `tag'.
+ *
+ * If *length != 0:
+ * Loads the next `length' bytes of table or font,
+ * starting at offset `offset' (in table or font too).
+ *
+ * @Output:
+ * buffer ::
+ * The address of target buffer.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_any( TT_Face face,
+ FT_ULong tag,
+ FT_Long offset,
+ FT_Byte* buffer,
+ FT_ULong* length )
+ {
+ FT_Error error;
+ FT_Stream stream;
+ TT_Table table;
+ FT_ULong size;
+
+
+ if ( tag != 0 )
+ {
+ /* look for tag in font directory */
+ table = tt_face_lookup_table( face, tag );
+ if ( !table )
+ {
+ error = FT_THROW( Table_Missing );
+ goto Exit;
+ }
+
+ offset += table->Offset;
+ size = table->Length;
+ }
+ else
+ /* tag == 0 -- the user wants to access the font file directly */
+ size = face->root.stream->size;
+
+ if ( length && *length == 0 )
+ {
+ *length = size;
+
+ return FT_Err_Ok;
+ }
+
+ if ( length )
+ size = *length;
+
+ stream = face->root.stream;
+ /* the `if' is syntactic sugar for picky compilers */
+ if ( FT_STREAM_READ_AT( offset, buffer, size ) )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_generic_header
+ *
+ * @Description:
+ * Loads the TrueType table `head' or `bhed'.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * The input stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ static FT_Error
+ tt_face_load_generic_header( TT_Face face,
+ FT_Stream stream,
+ FT_ULong tag )
+ {
+ FT_Error error;
+ TT_Header* header;
+
+ static const FT_Frame_Field header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_Header
+
+ FT_FRAME_START( 54 ),
+ FT_FRAME_ULONG ( Table_Version ),
+ FT_FRAME_ULONG ( Font_Revision ),
+ FT_FRAME_LONG ( CheckSum_Adjust ),
+ FT_FRAME_LONG ( Magic_Number ),
+ FT_FRAME_USHORT( Flags ),
+ FT_FRAME_USHORT( Units_Per_EM ),
+ FT_FRAME_ULONG ( Created[0] ),
+ FT_FRAME_ULONG ( Created[1] ),
+ FT_FRAME_ULONG ( Modified[0] ),
+ FT_FRAME_ULONG ( Modified[1] ),
+ FT_FRAME_SHORT ( xMin ),
+ FT_FRAME_SHORT ( yMin ),
+ FT_FRAME_SHORT ( xMax ),
+ FT_FRAME_SHORT ( yMax ),
+ FT_FRAME_USHORT( Mac_Style ),
+ FT_FRAME_USHORT( Lowest_Rec_PPEM ),
+ FT_FRAME_SHORT ( Font_Direction ),
+ FT_FRAME_SHORT ( Index_To_Loc_Format ),
+ FT_FRAME_SHORT ( Glyph_Data_Format ),
+ FT_FRAME_END
+ };
+
+
+ error = face->goto_table( face, tag, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ header = &face->header;
+
+ if ( FT_STREAM_READ_FIELDS( header_fields, header ) )
+ goto Exit;
+
+ FT_TRACE3(( "Units per EM: %4hu\n", header->Units_Per_EM ));
+ FT_TRACE3(( "IndexToLoc: %4hd\n", header->Index_To_Loc_Format ));
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_head( TT_Face face,
+ FT_Stream stream )
+ {
+ return tt_face_load_generic_header( face, stream, TTAG_head );
+ }
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_bhed( TT_Face face,
+ FT_Stream stream )
+ {
+ return tt_face_load_generic_header( face, stream, TTAG_bhed );
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_maxp
+ *
+ * @Description:
+ * Loads the maximum profile into a face object.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * The input stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_maxp( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ TT_MaxProfile* maxProfile = &face->max_profile;
+
+ static const FT_Frame_Field maxp_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_MaxProfile
+
+ FT_FRAME_START( 6 ),
+ FT_FRAME_LONG ( version ),
+ FT_FRAME_USHORT( numGlyphs ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field maxp_fields_extra[] =
+ {
+ FT_FRAME_START( 26 ),
+ FT_FRAME_USHORT( maxPoints ),
+ FT_FRAME_USHORT( maxContours ),
+ FT_FRAME_USHORT( maxCompositePoints ),
+ FT_FRAME_USHORT( maxCompositeContours ),
+ FT_FRAME_USHORT( maxZones ),
+ FT_FRAME_USHORT( maxTwilightPoints ),
+ FT_FRAME_USHORT( maxStorage ),
+ FT_FRAME_USHORT( maxFunctionDefs ),
+ FT_FRAME_USHORT( maxInstructionDefs ),
+ FT_FRAME_USHORT( maxStackElements ),
+ FT_FRAME_USHORT( maxSizeOfInstructions ),
+ FT_FRAME_USHORT( maxComponentElements ),
+ FT_FRAME_USHORT( maxComponentDepth ),
+ FT_FRAME_END
+ };
+
+
+ error = face->goto_table( face, TTAG_maxp, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) )
+ goto Exit;
+
+ maxProfile->maxPoints = 0;
+ maxProfile->maxContours = 0;
+ maxProfile->maxCompositePoints = 0;
+ maxProfile->maxCompositeContours = 0;
+ maxProfile->maxZones = 0;
+ maxProfile->maxTwilightPoints = 0;
+ maxProfile->maxStorage = 0;
+ maxProfile->maxFunctionDefs = 0;
+ maxProfile->maxInstructionDefs = 0;
+ maxProfile->maxStackElements = 0;
+ maxProfile->maxSizeOfInstructions = 0;
+ maxProfile->maxComponentElements = 0;
+ maxProfile->maxComponentDepth = 0;
+
+ if ( maxProfile->version >= 0x10000L )
+ {
+ if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) )
+ goto Exit;
+
+ /* XXX: an adjustment that is necessary to load certain */
+ /* broken fonts like `Keystrokes MT' :-( */
+ /* */
+ /* We allocate 64 function entries by default when */
+ /* the maxFunctionDefs value is smaller. */
+
+ if ( maxProfile->maxFunctionDefs < 64 )
+ maxProfile->maxFunctionDefs = 64;
+
+ /* we add 4 phantom points later */
+ if ( maxProfile->maxTwilightPoints > ( 0xFFFFU - 4 ) )
+ {
+ FT_TRACE0(( "tt_face_load_maxp:"
+ " too much twilight points in `maxp' table;\n" ));
+ FT_TRACE0(( " "
+ " some glyphs might be rendered incorrectly\n" ));
+
+ maxProfile->maxTwilightPoints = 0xFFFFU - 4;
+ }
+ }
+
+ FT_TRACE3(( "numGlyphs: %hu\n", maxProfile->numGlyphs ));
+
+ Exit:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_name
+ *
+ * @Description:
+ * Loads the name records.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * The input stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_name( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_ULong table_pos, table_len;
+ FT_ULong storage_start, storage_limit;
+ TT_NameTable table;
+ TT_Name names = NULL;
+ TT_LangTag langTags = NULL;
+
+ static const FT_Frame_Field name_table_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_NameTableRec
+
+ FT_FRAME_START( 6 ),
+ FT_FRAME_USHORT( format ),
+ FT_FRAME_USHORT( numNameRecords ),
+ FT_FRAME_USHORT( storageOffset ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field name_record_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_NameRec
+
+ /* no FT_FRAME_START */
+ FT_FRAME_USHORT( platformID ),
+ FT_FRAME_USHORT( encodingID ),
+ FT_FRAME_USHORT( languageID ),
+ FT_FRAME_USHORT( nameID ),
+ FT_FRAME_USHORT( stringLength ),
+ FT_FRAME_USHORT( stringOffset ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field langTag_record_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_LangTagRec
+
+ /* no FT_FRAME_START */
+ FT_FRAME_USHORT( stringLength ),
+ FT_FRAME_USHORT( stringOffset ),
+ FT_FRAME_END
+ };
+
+
+ table = &face->name_table;
+ table->stream = stream;
+
+ error = face->goto_table( face, TTAG_name, stream, &table_len );
+ if ( error )
+ goto Exit;
+
+ table_pos = FT_STREAM_POS();
+
+ if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
+ goto Exit;
+
+ /* Some popular Asian fonts have an invalid `storageOffset' value (it */
+ /* should be at least `6 + 12*numNameRecords'). However, the string */
+ /* offsets, computed as `storageOffset + entry->stringOffset', are */
+ /* valid pointers within the name table... */
+ /* */
+ /* We thus can't check `storageOffset' right now. */
+ /* */
+ storage_start = table_pos + 6 + 12 * table->numNameRecords;
+ storage_limit = table_pos + table_len;
+
+ if ( storage_start > storage_limit )
+ {
+ FT_ERROR(( "tt_face_load_name: invalid `name' table\n" ));
+ error = FT_THROW( Name_Table_Missing );
+ goto Exit;
+ }
+
+ /* `name' format 1 contains additional language tag records, */
+ /* which we load first */
+ if ( table->format == 1 )
+ {
+ if ( FT_STREAM_SEEK( storage_start ) ||
+ FT_READ_USHORT( table->numLangTagRecords ) )
+ goto Exit;
+
+ storage_start += 2 + 4 * table->numLangTagRecords;
+
+ /* allocate language tag records array */
+ if ( FT_QNEW_ARRAY( langTags, table->numLangTagRecords ) ||
+ FT_FRAME_ENTER( table->numLangTagRecords * 4 ) )
+ goto Exit;
+
+ /* load language tags */
+ {
+ TT_LangTag entry = langTags;
+ TT_LangTag limit = FT_OFFSET( entry, table->numLangTagRecords );
+
+
+ for ( ; entry < limit; entry++ )
+ {
+ (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry );
+
+ /* check that the langTag string is within the table */
+ entry->stringOffset += table_pos + table->storageOffset;
+ if ( entry->stringOffset < storage_start ||
+ entry->stringOffset + entry->stringLength > storage_limit )
+ {
+ /* invalid entry; ignore it */
+ entry->stringLength = 0;
+ }
+
+ /* mark the string as not yet loaded */
+ entry->string = NULL;
+ }
+
+ table->langTags = langTags;
+ langTags = NULL;
+ }
+
+ FT_FRAME_EXIT();
+
+ (void)FT_STREAM_SEEK( table_pos + 6 );
+ }
+
+ /* allocate name records array */
+ if ( FT_QNEW_ARRAY( names, table->numNameRecords ) ||
+ FT_FRAME_ENTER( table->numNameRecords * 12 ) )
+ goto Exit;
+
+ /* load name records */
+ {
+ TT_Name entry = names;
+ FT_UInt count = table->numNameRecords;
+ FT_UInt valid = 0;
+
+
+ for ( ; count > 0; count-- )
+ {
+ if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) )
+ continue;
+
+ /* check that the name is not empty */
+ if ( entry->stringLength == 0 )
+ continue;
+
+ /* check that the name string is within the table */
+ entry->stringOffset += table_pos + table->storageOffset;
+ if ( entry->stringOffset < storage_start ||
+ entry->stringOffset + entry->stringLength > storage_limit )
+ {
+ /* invalid entry; ignore it */
+ continue;
+ }
+
+ /* assure that we have a valid language tag ID, and */
+ /* that the corresponding langTag entry is valid, too */
+ if ( table->format == 1 && entry->languageID >= 0x8000U )
+ {
+ if ( entry->languageID - 0x8000U >= table->numLangTagRecords ||
+ !table->langTags[entry->languageID - 0x8000U].stringLength )
+ {
+ /* invalid entry; ignore it */
+ continue;
+ }
+ }
+
+ /* mark the string as not yet converted */
+ entry->string = NULL;
+
+ valid++;
+ entry++;
+ }
+
+ /* reduce array size to the actually used elements */
+ FT_MEM_QRENEW_ARRAY( names,
+ table->numNameRecords,
+ valid );
+ table->names = names;
+ names = NULL;
+ table->numNameRecords = valid;
+ }
+
+ FT_FRAME_EXIT();
+
+ /* everything went well, update face->num_names */
+ face->num_names = (FT_UShort)table->numNameRecords;
+
+ Exit:
+ FT_FREE( names );
+ FT_FREE( langTags );
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_free_name
+ *
+ * @Description:
+ * Frees the name records.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ */
+ FT_LOCAL_DEF( void )
+ tt_face_free_name( TT_Face face )
+ {
+ FT_Memory memory = face->root.driver->root.memory;
+ TT_NameTable table = &face->name_table;
+
+
+ if ( table->names )
+ {
+ TT_Name entry = table->names;
+ TT_Name limit = entry + table->numNameRecords;
+
+
+ for ( ; entry < limit; entry++ )
+ FT_FREE( entry->string );
+
+ FT_FREE( table->names );
+ }
+
+ if ( table->langTags )
+ {
+ TT_LangTag entry = table->langTags;
+ TT_LangTag limit = entry + table->numLangTagRecords;
+
+
+ for ( ; entry < limit; entry++ )
+ FT_FREE( entry->string );
+
+ FT_FREE( table->langTags );
+ }
+
+ table->numNameRecords = 0;
+ table->numLangTagRecords = 0;
+ table->format = 0;
+ table->storageOffset = 0;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_cmap
+ *
+ * @Description:
+ * Loads the cmap directory in a face object. The cmaps themselves
+ * are loaded on demand in the `ttcmap.c' module.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * A handle to the input stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_cmap( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size );
+ if ( error )
+ goto Exit;
+
+ if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) )
+ face->cmap_size = 0;
+
+ Exit:
+ return error;
+ }
+
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_os2
+ *
+ * @Description:
+ * Loads the OS2 table.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * A handle to the input stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_os2( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ TT_OS2* os2;
+
+ static const FT_Frame_Field os2_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_OS2
+
+ FT_FRAME_START( 78 ),
+ FT_FRAME_USHORT( version ),
+ FT_FRAME_SHORT ( xAvgCharWidth ),
+ FT_FRAME_USHORT( usWeightClass ),
+ FT_FRAME_USHORT( usWidthClass ),
+ FT_FRAME_SHORT ( fsType ),
+ FT_FRAME_SHORT ( ySubscriptXSize ),
+ FT_FRAME_SHORT ( ySubscriptYSize ),
+ FT_FRAME_SHORT ( ySubscriptXOffset ),
+ FT_FRAME_SHORT ( ySubscriptYOffset ),
+ FT_FRAME_SHORT ( ySuperscriptXSize ),
+ FT_FRAME_SHORT ( ySuperscriptYSize ),
+ FT_FRAME_SHORT ( ySuperscriptXOffset ),
+ FT_FRAME_SHORT ( ySuperscriptYOffset ),
+ FT_FRAME_SHORT ( yStrikeoutSize ),
+ FT_FRAME_SHORT ( yStrikeoutPosition ),
+ FT_FRAME_SHORT ( sFamilyClass ),
+ FT_FRAME_BYTE ( panose[0] ),
+ FT_FRAME_BYTE ( panose[1] ),
+ FT_FRAME_BYTE ( panose[2] ),
+ FT_FRAME_BYTE ( panose[3] ),
+ FT_FRAME_BYTE ( panose[4] ),
+ FT_FRAME_BYTE ( panose[5] ),
+ FT_FRAME_BYTE ( panose[6] ),
+ FT_FRAME_BYTE ( panose[7] ),
+ FT_FRAME_BYTE ( panose[8] ),
+ FT_FRAME_BYTE ( panose[9] ),
+ FT_FRAME_ULONG ( ulUnicodeRange1 ),
+ FT_FRAME_ULONG ( ulUnicodeRange2 ),
+ FT_FRAME_ULONG ( ulUnicodeRange3 ),
+ FT_FRAME_ULONG ( ulUnicodeRange4 ),
+ FT_FRAME_BYTE ( achVendID[0] ),
+ FT_FRAME_BYTE ( achVendID[1] ),
+ FT_FRAME_BYTE ( achVendID[2] ),
+ FT_FRAME_BYTE ( achVendID[3] ),
+
+ FT_FRAME_USHORT( fsSelection ),
+ FT_FRAME_USHORT( usFirstCharIndex ),
+ FT_FRAME_USHORT( usLastCharIndex ),
+ FT_FRAME_SHORT ( sTypoAscender ),
+ FT_FRAME_SHORT ( sTypoDescender ),
+ FT_FRAME_SHORT ( sTypoLineGap ),
+ FT_FRAME_USHORT( usWinAscent ),
+ FT_FRAME_USHORT( usWinDescent ),
+ FT_FRAME_END
+ };
+
+ /* `OS/2' version 1 and newer */
+ static const FT_Frame_Field os2_fields_extra1[] =
+ {
+ FT_FRAME_START( 8 ),
+ FT_FRAME_ULONG( ulCodePageRange1 ),
+ FT_FRAME_ULONG( ulCodePageRange2 ),
+ FT_FRAME_END
+ };
+
+ /* `OS/2' version 2 and newer */
+ static const FT_Frame_Field os2_fields_extra2[] =
+ {
+ FT_FRAME_START( 10 ),
+ FT_FRAME_SHORT ( sxHeight ),
+ FT_FRAME_SHORT ( sCapHeight ),
+ FT_FRAME_USHORT( usDefaultChar ),
+ FT_FRAME_USHORT( usBreakChar ),
+ FT_FRAME_USHORT( usMaxContext ),
+ FT_FRAME_END
+ };
+
+ /* `OS/2' version 5 and newer */
+ static const FT_Frame_Field os2_fields_extra5[] =
+ {
+ FT_FRAME_START( 4 ),
+ FT_FRAME_USHORT( usLowerOpticalPointSize ),
+ FT_FRAME_USHORT( usUpperOpticalPointSize ),
+ FT_FRAME_END
+ };
+
+
+ /* We now support old Mac fonts where the OS/2 table doesn't */
+ /* exist. Simply put, we set the `version' field to 0xFFFF */
+ /* and test this value each time we need to access the table. */
+ error = face->goto_table( face, TTAG_OS2, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ os2 = &face->os2;
+
+ if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) )
+ goto Exit;
+
+ os2->ulCodePageRange1 = 0;
+ os2->ulCodePageRange2 = 0;
+ os2->sxHeight = 0;
+ os2->sCapHeight = 0;
+ os2->usDefaultChar = 0;
+ os2->usBreakChar = 0;
+ os2->usMaxContext = 0;
+ os2->usLowerOpticalPointSize = 0;
+ os2->usUpperOpticalPointSize = 0xFFFF;
+
+ if ( os2->version >= 0x0001 )
+ {
+ /* only version 1 tables */
+ if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) )
+ goto Exit;
+
+ if ( os2->version >= 0x0002 )
+ {
+ /* only version 2 tables */
+ if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) )
+ goto Exit;
+
+ if ( os2->version >= 0x0005 )
+ {
+ /* only version 5 tables */
+ if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) )
+ goto Exit;
+ }
+ }
+ }
+
+ FT_TRACE3(( "sTypoAscender: %4hd\n", os2->sTypoAscender ));
+ FT_TRACE3(( "sTypoDescender: %4hd\n", os2->sTypoDescender ));
+ FT_TRACE3(( "usWinAscent: %4hu\n", os2->usWinAscent ));
+ FT_TRACE3(( "usWinDescent: %4hu\n", os2->usWinDescent ));
+ FT_TRACE3(( "fsSelection: 0x%2hx\n", os2->fsSelection ));
+
+ Exit:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_postscript
+ *
+ * @Description:
+ * Loads the Postscript table.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * A handle to the input stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_post( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ TT_Postscript* post = &face->postscript;
+
+ static const FT_Frame_Field post_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_Postscript
+
+ FT_FRAME_START( 32 ),
+ FT_FRAME_LONG ( FormatType ),
+ FT_FRAME_LONG ( italicAngle ),
+ FT_FRAME_SHORT( underlinePosition ),
+ FT_FRAME_SHORT( underlineThickness ),
+ FT_FRAME_ULONG( isFixedPitch ),
+ FT_FRAME_ULONG( minMemType42 ),
+ FT_FRAME_ULONG( maxMemType42 ),
+ FT_FRAME_ULONG( minMemType1 ),
+ FT_FRAME_ULONG( maxMemType1 ),
+ FT_FRAME_END
+ };
+
+
+ error = face->goto_table( face, TTAG_post, stream, 0 );
+ if ( error )
+ return error;
+
+ if ( FT_STREAM_READ_FIELDS( post_fields, post ) )
+ return error;
+
+ if ( post->FormatType != 0x00030000L &&
+ post->FormatType != 0x00025000L &&
+ post->FormatType != 0x00020000L &&
+ post->FormatType != 0x00010000L )
+ return FT_THROW( Invalid_Post_Table_Format );
+
+ /* we don't load the glyph names, we do that in another */
+ /* module (ttpost). */
+
+ FT_TRACE3(( "FormatType: 0x%lx\n", post->FormatType ));
+ FT_TRACE3(( "isFixedPitch: %s\n", post->isFixedPitch
+ ? " yes" : " no" ));
+
+ return FT_Err_Ok;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_pclt
+ *
+ * @Description:
+ * Loads the PCL 5 Table.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * A handle to the input stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_pclt( TT_Face face,
+ FT_Stream stream )
+ {
+ static const FT_Frame_Field pclt_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_PCLT
+
+ FT_FRAME_START( 54 ),
+ FT_FRAME_ULONG ( Version ),
+ FT_FRAME_ULONG ( FontNumber ),
+ FT_FRAME_USHORT( Pitch ),
+ FT_FRAME_USHORT( xHeight ),
+ FT_FRAME_USHORT( Style ),
+ FT_FRAME_USHORT( TypeFamily ),
+ FT_FRAME_USHORT( CapHeight ),
+ FT_FRAME_USHORT( SymbolSet ),
+ FT_FRAME_BYTES ( TypeFace, 16 ),
+ FT_FRAME_BYTES ( CharacterComplement, 8 ),
+ FT_FRAME_BYTES ( FileName, 6 ),
+ FT_FRAME_CHAR ( StrokeWeight ),
+ FT_FRAME_CHAR ( WidthType ),
+ FT_FRAME_BYTE ( SerifStyle ),
+ FT_FRAME_BYTE ( Reserved ),
+ FT_FRAME_END
+ };
+
+ FT_Error error;
+ TT_PCLT* pclt = &face->pclt;
+
+
+ /* optional table */
+ error = face->goto_table( face, TTAG_PCLT, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_gasp
+ *
+ * @Description:
+ * Loads the `gasp' table into a face object.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * The input stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_gasp( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort j, num_ranges;
+ TT_GaspRange gasp_ranges = NULL;
+
+
+ /* the gasp table is optional */
+ error = face->goto_table( face, TTAG_gasp, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ if ( FT_FRAME_ENTER( 4L ) )
+ goto Exit;
+
+ face->gasp.version = FT_GET_USHORT();
+ num_ranges = FT_GET_USHORT();
+
+ FT_FRAME_EXIT();
+
+ /* only support versions 0 and 1 of the table */
+ if ( face->gasp.version >= 2 )
+ {
+ face->gasp.numRanges = 0;
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ FT_TRACE3(( "numRanges: %hu\n", num_ranges ));
+
+ if ( FT_QNEW_ARRAY( gasp_ranges, num_ranges ) ||
+ FT_FRAME_ENTER( num_ranges * 4L ) )
+ goto Exit;
+
+ for ( j = 0; j < num_ranges; j++ )
+ {
+ gasp_ranges[j].maxPPEM = FT_GET_USHORT();
+ gasp_ranges[j].gaspFlag = FT_GET_USHORT();
+
+ FT_TRACE3(( "gaspRange %hu: rangeMaxPPEM %5hu, rangeGaspBehavior 0x%hx\n",
+ j,
+ gasp_ranges[j].maxPPEM,
+ gasp_ranges[j].gaspFlag ));
+ }
+
+ face->gasp.gaspRanges = gasp_ranges;
+ gasp_ranges = NULL;
+ face->gasp.numRanges = num_ranges;
+
+ FT_FRAME_EXIT();
+
+ Exit:
+ FT_FREE( gasp_ranges );
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttload.h b/modules/freetype2/src/sfnt/ttload.h
new file mode 100644
index 0000000000..1499dd5735
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttload.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+ *
+ * ttload.h
+ *
+ * Load the basic TrueType tables, i.e., tables that can be either in
+ * TTF or OTF fonts (specification).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#ifndef TTLOAD_H_
+#define TTLOAD_H_
+
+
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/tttypes.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( TT_Table )
+ tt_face_lookup_table( TT_Face face,
+ FT_ULong tag );
+
+ FT_LOCAL( FT_Error )
+ tt_face_goto_table( TT_Face face,
+ FT_ULong tag,
+ FT_Stream stream,
+ FT_ULong* length );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_font_dir( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_any( TT_Face face,
+ FT_ULong tag,
+ FT_Long offset,
+ FT_Byte* buffer,
+ FT_ULong* length );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_head( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_cmap( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_maxp( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_name( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_os2( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_post( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_pclt( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_free_name( TT_Face face );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_gasp( TT_Face face,
+ FT_Stream stream );
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_bhed( TT_Face face,
+ FT_Stream stream );
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+FT_END_HEADER
+
+#endif /* TTLOAD_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttmtx.c b/modules/freetype2/src/sfnt/ttmtx.c
new file mode 100644
index 0000000000..5e53e6dd4a
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttmtx.c
@@ -0,0 +1,338 @@
+/****************************************************************************
+ *
+ * ttmtx.c
+ *
+ * Load the metrics tables common to TTF and OTF fonts (body).
+ *
+ * Copyright (C) 2006-2023 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 <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include <freetype/internal/services/svmetric.h>
+#endif
+
+#include "ttmtx.h"
+
+#include "sferrors.h"
+
+
+ /* IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should */
+ /* be identical except for the names of their fields, */
+ /* which are different. */
+ /* */
+ /* This ensures that `tt_face_load_hmtx' is able to read */
+ /* both the horizontal and vertical headers. */
+
+
+ /**************************************************************************
+ *
+ * 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 ttmtx
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_hmtx
+ *
+ * @Description:
+ * Load the `hmtx' or `vmtx' table into a face object.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * The input stream.
+ *
+ * vertical ::
+ * A boolean flag. If set, load `vmtx'.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_hmtx( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical )
+ {
+ FT_Error error;
+ FT_ULong tag, table_size;
+ FT_ULong* ptable_offset;
+ FT_ULong* ptable_size;
+
+
+ if ( vertical )
+ {
+ tag = TTAG_vmtx;
+ ptable_offset = &face->vert_metrics_offset;
+ ptable_size = &face->vert_metrics_size;
+ }
+ else
+ {
+ tag = TTAG_hmtx;
+ ptable_offset = &face->horz_metrics_offset;
+ ptable_size = &face->horz_metrics_size;
+ }
+
+ error = face->goto_table( face, tag, stream, &table_size );
+ if ( error )
+ goto Fail;
+
+ *ptable_size = table_size;
+ *ptable_offset = FT_STREAM_POS();
+
+ Fail:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_load_hhea
+ *
+ * @Description:
+ * Load the `hhea' or 'vhea' table into a face object.
+ *
+ * @Input:
+ * face ::
+ * A handle to the target face object.
+ *
+ * stream ::
+ * The input stream.
+ *
+ * vertical ::
+ * A boolean flag. If set, load `vhea'.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_hhea( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical )
+ {
+ FT_Error error;
+ TT_HoriHeader* header;
+
+ static const FT_Frame_Field metrics_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_HoriHeader
+
+ FT_FRAME_START( 36 ),
+ FT_FRAME_ULONG ( Version ),
+ FT_FRAME_SHORT ( Ascender ),
+ FT_FRAME_SHORT ( Descender ),
+ FT_FRAME_SHORT ( Line_Gap ),
+ FT_FRAME_USHORT( advance_Width_Max ),
+ FT_FRAME_SHORT ( min_Left_Side_Bearing ),
+ FT_FRAME_SHORT ( min_Right_Side_Bearing ),
+ FT_FRAME_SHORT ( xMax_Extent ),
+ FT_FRAME_SHORT ( caret_Slope_Rise ),
+ FT_FRAME_SHORT ( caret_Slope_Run ),
+ FT_FRAME_SHORT ( caret_Offset ),
+ FT_FRAME_SHORT ( Reserved[0] ),
+ FT_FRAME_SHORT ( Reserved[1] ),
+ FT_FRAME_SHORT ( Reserved[2] ),
+ FT_FRAME_SHORT ( Reserved[3] ),
+ FT_FRAME_SHORT ( metric_Data_Format ),
+ FT_FRAME_USHORT( number_Of_HMetrics ),
+ FT_FRAME_END
+ };
+
+
+ if ( vertical )
+ {
+ void *v = &face->vertical;
+
+
+ error = face->goto_table( face, TTAG_vhea, stream, 0 );
+ if ( error )
+ goto Fail;
+
+ header = (TT_HoriHeader*)v;
+ }
+ else
+ {
+ error = face->goto_table( face, TTAG_hhea, stream, 0 );
+ if ( error )
+ goto Fail;
+
+ header = &face->horizontal;
+ }
+
+ if ( FT_STREAM_READ_FIELDS( metrics_header_fields, header ) )
+ goto Fail;
+
+ FT_TRACE3(( "Ascender: %5d\n", header->Ascender ));
+ FT_TRACE3(( "Descender: %5d\n", header->Descender ));
+ FT_TRACE3(( "number_Of_Metrics: %5u\n", header->number_Of_HMetrics ));
+
+ header->long_metrics = NULL;
+ header->short_metrics = NULL;
+
+ Fail:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_get_metrics
+ *
+ * @Description:
+ * Return the horizontal or vertical metrics in font units for a
+ * given glyph. The values are the left side bearing (top side
+ * bearing for vertical metrics) and advance width (advance height
+ * for vertical metrics).
+ *
+ * @Input:
+ * face ::
+ * A pointer to the TrueType face structure.
+ *
+ * vertical ::
+ * If set to TRUE, get vertical metrics.
+ *
+ * gindex ::
+ * The glyph index.
+ *
+ * @Output:
+ * abearing ::
+ * The bearing, either left side or top side.
+ *
+ * aadvance ::
+ * The advance width or advance height, depending on
+ * the `vertical' flag.
+ */
+ FT_LOCAL_DEF( void )
+ tt_face_get_metrics( TT_Face face,
+ FT_Bool vertical,
+ FT_UInt gindex,
+ FT_Short *abearing,
+ FT_UShort *aadvance )
+ {
+ FT_Error error;
+ FT_Stream stream = face->root.stream;
+ TT_HoriHeader* header;
+ FT_ULong table_pos, table_size, table_end;
+ FT_UShort k;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ FT_Service_MetricsVariations var =
+ (FT_Service_MetricsVariations)face->var;
+#endif
+
+
+ if ( vertical )
+ {
+ void* v = &face->vertical;
+
+
+ header = (TT_HoriHeader*)v;
+ table_pos = face->vert_metrics_offset;
+ table_size = face->vert_metrics_size;
+ }
+ else
+ {
+ header = &face->horizontal;
+ table_pos = face->horz_metrics_offset;
+ table_size = face->horz_metrics_size;
+ }
+
+ table_end = table_pos + table_size;
+
+ k = header->number_Of_HMetrics;
+
+ if ( k > 0 )
+ {
+ if ( gindex < (FT_UInt)k )
+ {
+ table_pos += 4 * gindex;
+ if ( table_pos + 4 > table_end )
+ goto NoData;
+
+ if ( FT_STREAM_SEEK( table_pos ) ||
+ FT_READ_USHORT( *aadvance ) ||
+ FT_READ_SHORT( *abearing ) )
+ goto NoData;
+ }
+ else
+ {
+ table_pos += 4 * ( k - 1 );
+ if ( table_pos + 2 > table_end )
+ goto NoData;
+
+ if ( FT_STREAM_SEEK( table_pos ) ||
+ FT_READ_USHORT( *aadvance ) )
+ goto NoData;
+
+ table_pos += 4 + 2 * ( gindex - k );
+ if ( table_pos + 2 > table_end )
+ *abearing = 0;
+ else
+ {
+ if ( FT_STREAM_SEEK( table_pos ) )
+ *abearing = 0;
+ else
+ (void)FT_READ_SHORT( *abearing );
+ }
+ }
+ }
+ else
+ {
+ NoData:
+ *abearing = 0;
+ *aadvance = 0;
+ }
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( var && face->blend )
+ {
+ FT_Face f = FT_FACE( face );
+ FT_Int a = (FT_Int)*aadvance;
+ FT_Int b = (FT_Int)*abearing;
+
+
+ if ( vertical )
+ {
+ if ( var->vadvance_adjust )
+ var->vadvance_adjust( f, gindex, &a );
+ if ( var->tsb_adjust )
+ var->tsb_adjust( f, gindex, &b );
+ }
+ else
+ {
+ if ( var->hadvance_adjust )
+ var->hadvance_adjust( f, gindex, &a );
+ if ( var->lsb_adjust )
+ var->lsb_adjust( f, gindex, &b );
+ }
+
+ *aadvance = (FT_UShort)a;
+ *abearing = (FT_Short)b;
+ }
+#endif
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttmtx.h b/modules/freetype2/src/sfnt/ttmtx.h
new file mode 100644
index 0000000000..56d2b62766
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttmtx.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+ *
+ * ttmtx.h
+ *
+ * Load the metrics tables common to TTF and OTF fonts (specification).
+ *
+ * Copyright (C) 2006-2023 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.
+ *
+ */
+
+
+#ifndef TTMTX_H_
+#define TTMTX_H_
+
+
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/tttypes.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_hhea( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_hmtx( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical );
+
+
+ FT_LOCAL( void )
+ tt_face_get_metrics( TT_Face face,
+ FT_Bool vertical,
+ FT_UInt gindex,
+ FT_Short* abearing,
+ FT_UShort* aadvance );
+
+FT_END_HEADER
+
+#endif /* TTMTX_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttpost.c b/modules/freetype2/src/sfnt/ttpost.c
new file mode 100644
index 0000000000..0e17c6f34a
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttpost.c
@@ -0,0 +1,566 @@
+/****************************************************************************
+ *
+ * ttpost.c
+ *
+ * PostScript name table processing for TrueType and OpenType fonts
+ * (body).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+ /**************************************************************************
+ *
+ * The post table is not completely loaded by the core engine. This
+ * file loads the missing PS glyph names and implements an API to access
+ * them.
+ *
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+#include "ttpost.h"
+
+#include "sferrors.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 ttpost
+
+
+ /* If this configuration macro is defined, we rely on the `psnames' */
+ /* module to grab the glyph names. */
+
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+
+#include <freetype/internal/services/svpscmap.h>
+
+#define MAC_NAME( x ) (FT_String*)psnames->macintosh_name( (FT_UInt)(x) )
+
+
+#else /* !FT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+
+ /* Otherwise, we ignore the `psnames' module, and provide our own */
+ /* table of Mac names. Thus, it is possible to build a version of */
+ /* FreeType without the Type 1 driver & psnames module. */
+
+#define MAC_NAME( x ) (FT_String*)tt_post_default_names[x]
+
+ /* the 258 default Mac PS glyph names; see file `tools/glnames.py' */
+
+ static const FT_String* const tt_post_default_names[258] =
+ {
+ /* 0 */
+ ".notdef", ".null", "nonmarkingreturn", "space", "exclam",
+ "quotedbl", "numbersign", "dollar", "percent", "ampersand",
+ /* 10 */
+ "quotesingle", "parenleft", "parenright", "asterisk", "plus",
+ "comma", "hyphen", "period", "slash", "zero",
+ /* 20 */
+ "one", "two", "three", "four", "five",
+ "six", "seven", "eight", "nine", "colon",
+ /* 30 */
+ "semicolon", "less", "equal", "greater", "question",
+ "at", "A", "B", "C", "D",
+ /* 40 */
+ "E", "F", "G", "H", "I",
+ "J", "K", "L", "M", "N",
+ /* 50 */
+ "O", "P", "Q", "R", "S",
+ "T", "U", "V", "W", "X",
+ /* 60 */
+ "Y", "Z", "bracketleft", "backslash", "bracketright",
+ "asciicircum", "underscore", "grave", "a", "b",
+ /* 70 */
+ "c", "d", "e", "f", "g",
+ "h", "i", "j", "k", "l",
+ /* 80 */
+ "m", "n", "o", "p", "q",
+ "r", "s", "t", "u", "v",
+ /* 90 */
+ "w", "x", "y", "z", "braceleft",
+ "bar", "braceright", "asciitilde", "Adieresis", "Aring",
+ /* 100 */
+ "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis",
+ "aacute", "agrave", "acircumflex", "adieresis", "atilde",
+ /* 110 */
+ "aring", "ccedilla", "eacute", "egrave", "ecircumflex",
+ "edieresis", "iacute", "igrave", "icircumflex", "idieresis",
+ /* 120 */
+ "ntilde", "oacute", "ograve", "ocircumflex", "odieresis",
+ "otilde", "uacute", "ugrave", "ucircumflex", "udieresis",
+ /* 130 */
+ "dagger", "degree", "cent", "sterling", "section",
+ "bullet", "paragraph", "germandbls", "registered", "copyright",
+ /* 140 */
+ "trademark", "acute", "dieresis", "notequal", "AE",
+ "Oslash", "infinity", "plusminus", "lessequal", "greaterequal",
+ /* 150 */
+ "yen", "mu", "partialdiff", "summation", "product",
+ "pi", "integral", "ordfeminine", "ordmasculine", "Omega",
+ /* 160 */
+ "ae", "oslash", "questiondown", "exclamdown", "logicalnot",
+ "radical", "florin", "approxequal", "Delta", "guillemotleft",
+ /* 170 */
+ "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde",
+ "Otilde", "OE", "oe", "endash", "emdash",
+ /* 180 */
+ "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide",
+ "lozenge", "ydieresis", "Ydieresis", "fraction", "currency",
+ /* 190 */
+ "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl",
+ "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",
+ /* 200 */
+ "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute",
+ "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex",
+ /* 210 */
+ "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave",
+ "dotlessi", "circumflex", "tilde", "macron", "breve",
+ /* 220 */
+ "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek",
+ "caron", "Lslash", "lslash", "Scaron", "scaron",
+ /* 230 */
+ "Zcaron", "zcaron", "brokenbar", "Eth", "eth",
+ "Yacute", "yacute", "Thorn", "thorn", "minus",
+ /* 240 */
+ "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf",
+ "onequarter", "threequarters", "franc", "Gbreve", "gbreve",
+ /* 250 */
+ "Idotaccent", "Scedilla", "scedilla", "Cacute", "cacute",
+ "Ccaron", "ccaron", "dcroat",
+ };
+
+
+#endif /* !FT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+
+ static FT_Error
+ load_format_20( TT_Face face,
+ FT_Stream stream,
+ FT_ULong post_len )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+
+ FT_Int num_glyphs;
+ FT_UShort num_names;
+
+ FT_UShort* glyph_indices = NULL;
+ FT_Char** name_strings = NULL;
+ FT_Byte* strings = NULL;
+
+
+ if ( FT_READ_USHORT( num_glyphs ) )
+ goto Exit;
+
+ /* UNDOCUMENTED! The number of glyphs in this table can be smaller */
+ /* than the value in the maxp table (cf. cyberbit.ttf). */
+
+ /* There already exist fonts which have more than 32768 glyph names */
+ /* in this table, so the test for this threshold has been dropped. */
+
+ if ( num_glyphs > face->max_profile.numGlyphs ||
+ (FT_ULong)num_glyphs * 2UL > post_len - 2 )
+ {
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+ /* load the indices */
+ {
+ FT_Int n;
+
+
+ if ( FT_QNEW_ARRAY( glyph_indices, num_glyphs ) ||
+ FT_FRAME_ENTER( num_glyphs * 2L ) )
+ goto Fail;
+
+ for ( n = 0; n < num_glyphs; n++ )
+ glyph_indices[n] = FT_GET_USHORT();
+
+ FT_FRAME_EXIT();
+ }
+
+ /* compute number of names stored in table */
+ {
+ FT_Int n;
+
+
+ num_names = 0;
+
+ for ( n = 0; n < num_glyphs; n++ )
+ {
+ FT_Int idx;
+
+
+ idx = glyph_indices[n];
+ if ( idx >= 258 )
+ {
+ idx -= 257;
+ if ( idx > num_names )
+ num_names = (FT_UShort)idx;
+ }
+ }
+ }
+
+ /* now load the name strings */
+ if ( num_names )
+ {
+ FT_UShort n;
+ FT_ULong p;
+
+
+ post_len -= (FT_ULong)num_glyphs * 2UL + 2;
+
+ if ( FT_QALLOC( strings, post_len + 1 ) ||
+ FT_STREAM_READ( strings, post_len ) ||
+ FT_QNEW_ARRAY( name_strings, num_names ) )
+ goto Fail;
+
+ /* convert from Pascal- to C-strings and set pointers */
+ for ( p = 0, n = 0; p < post_len && n < num_names; n++ )
+ {
+ FT_UInt len = strings[p];
+
+
+ if ( len > 63U )
+ {
+ error = FT_THROW( Invalid_File_Format );
+ goto Fail;
+ }
+
+ strings[p] = 0;
+ name_strings[n] = (FT_Char*)strings + p + 1;
+ p += len + 1;
+ }
+ strings[post_len] = 0;
+
+ /* deal with missing or insufficient string data */
+ if ( n < num_names )
+ {
+ if ( post_len == 0 )
+ {
+ /* fake empty string */
+ if ( FT_QREALLOC( strings, 1, 2 ) )
+ goto Fail;
+
+ post_len = 1;
+ strings[post_len] = 0;
+ }
+
+ FT_ERROR(( "load_format_20:"
+ " all entries in post table are already parsed,"
+ " using NULL names for gid %d - %d\n",
+ n, num_names - 1 ));
+ for ( ; n < num_names; n++ )
+ name_strings[n] = (FT_Char*)strings + post_len;
+ }
+ }
+
+ /* all right, set table fields and exit successfully */
+ {
+ TT_Post_20 table = &face->postscript_names.names.format_20;
+
+
+ table->num_glyphs = (FT_UShort)num_glyphs;
+ table->num_names = (FT_UShort)num_names;
+ table->glyph_indices = glyph_indices;
+ table->glyph_names = name_strings;
+ }
+ return FT_Err_Ok;
+
+ Fail:
+ FT_FREE( name_strings );
+ FT_FREE( strings );
+ FT_FREE( glyph_indices );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ load_format_25( TT_Face face,
+ FT_Stream stream,
+ FT_ULong post_len )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+
+ FT_Int num_glyphs;
+ FT_Char* offset_table = NULL;
+
+ FT_UNUSED( post_len );
+
+
+ if ( FT_READ_USHORT( num_glyphs ) )
+ goto Exit;
+
+ /* check the number of glyphs */
+ if ( num_glyphs > face->max_profile.numGlyphs ||
+ num_glyphs > 258 ||
+ num_glyphs < 1 )
+ {
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+ if ( FT_QNEW_ARRAY( offset_table, num_glyphs ) ||
+ FT_STREAM_READ( offset_table, num_glyphs ) )
+ goto Fail;
+
+ /* now check the offset table */
+ {
+ FT_Int n;
+
+
+ for ( n = 0; n < num_glyphs; n++ )
+ {
+ FT_Long idx = (FT_Long)n + offset_table[n];
+
+
+ if ( idx < 0 || idx > num_glyphs )
+ {
+ error = FT_THROW( Invalid_File_Format );
+ goto Fail;
+ }
+ }
+ }
+
+ /* OK, set table fields and exit successfully */
+ {
+ TT_Post_25 table = &face->postscript_names.names.format_25;
+
+
+ table->num_glyphs = (FT_UShort)num_glyphs;
+ table->offsets = offset_table;
+ }
+
+ return FT_Err_Ok;
+
+ Fail:
+ FT_FREE( offset_table );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ load_post_names( TT_Face face )
+ {
+ FT_Stream stream;
+ FT_Error error;
+ FT_Fixed format;
+ FT_ULong post_len;
+
+
+ /* get a stream for the face's resource */
+ stream = face->root.stream;
+
+ /* seek to the beginning of the PS names table */
+ error = face->goto_table( face, TTAG_post, stream, &post_len );
+ if ( error )
+ goto Exit;
+
+ format = face->postscript.FormatType;
+
+ /* go to beginning of subtable */
+ if ( FT_STREAM_SKIP( 32 ) )
+ goto Exit;
+
+ /* now read postscript table */
+ if ( format == 0x00020000L && post_len >= 34 )
+ error = load_format_20( face, stream, post_len - 32 );
+ else if ( format == 0x00025000L && post_len >= 34 )
+ error = load_format_25( face, stream, post_len - 32 );
+ else
+ error = FT_THROW( Invalid_File_Format );
+
+ face->postscript_names.loaded = 1;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_ps_names( TT_Face face )
+ {
+ FT_Memory memory = face->root.memory;
+ TT_Post_Names names = &face->postscript_names;
+ FT_Fixed format;
+
+
+ if ( names->loaded )
+ {
+ format = face->postscript.FormatType;
+
+ if ( format == 0x00020000L )
+ {
+ TT_Post_20 table = &names->names.format_20;
+
+
+ FT_FREE( table->glyph_indices );
+ table->num_glyphs = 0;
+
+ if ( table->num_names )
+ {
+ table->glyph_names[0]--;
+ FT_FREE( table->glyph_names[0] );
+
+ FT_FREE( table->glyph_names );
+ table->num_names = 0;
+ }
+ }
+ else if ( format == 0x00025000L )
+ {
+ TT_Post_25 table = &names->names.format_25;
+
+
+ FT_FREE( table->offsets );
+ table->num_glyphs = 0;
+ }
+ }
+ names->loaded = 0;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_face_get_ps_name
+ *
+ * @Description:
+ * Get the PostScript glyph name of a glyph.
+ *
+ * @Input:
+ * face ::
+ * A handle to the parent face.
+ *
+ * idx ::
+ * The glyph index.
+ *
+ * @InOut:
+ * PSname ::
+ * The address of a string pointer. Undefined in case of
+ * error, otherwise it is a pointer to the glyph name.
+ *
+ * You must not modify the returned string!
+ *
+ * @Output:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_get_ps_name( TT_Face face,
+ FT_UInt idx,
+ FT_String** PSname )
+ {
+ FT_Error error;
+ TT_Post_Names names;
+ FT_Fixed format;
+
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ FT_Service_PsCMaps psnames;
+#endif
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( idx >= (FT_UInt)face->max_profile.numGlyphs )
+ return FT_THROW( Invalid_Glyph_Index );
+
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ psnames = (FT_Service_PsCMaps)face->psnames;
+ if ( !psnames )
+ return FT_THROW( Unimplemented_Feature );
+#endif
+
+ names = &face->postscript_names;
+
+ /* `.notdef' by default */
+ *PSname = MAC_NAME( 0 );
+
+ format = face->postscript.FormatType;
+
+ if ( format == 0x00010000L )
+ {
+ if ( idx < 258 ) /* paranoid checking */
+ *PSname = MAC_NAME( idx );
+ }
+ else if ( format == 0x00020000L )
+ {
+ TT_Post_20 table = &names->names.format_20;
+
+
+ if ( !names->loaded )
+ {
+ error = load_post_names( face );
+ if ( error )
+ goto End;
+ }
+
+ if ( idx < (FT_UInt)table->num_glyphs )
+ {
+ FT_UShort name_index = table->glyph_indices[idx];
+
+
+ if ( name_index < 258 )
+ *PSname = MAC_NAME( name_index );
+ else
+ *PSname = (FT_String*)table->glyph_names[name_index - 258];
+ }
+ }
+ else if ( format == 0x00025000L )
+ {
+ TT_Post_25 table = &names->names.format_25;
+
+
+ if ( !names->loaded )
+ {
+ error = load_post_names( face );
+ if ( error )
+ goto End;
+ }
+
+ if ( idx < (FT_UInt)table->num_glyphs ) /* paranoid checking */
+ *PSname = MAC_NAME( (FT_Int)idx + table->offsets[idx] );
+ }
+
+ /* nothing to do for format == 0x00030000L */
+
+ End:
+ return FT_Err_Ok;
+ }
+
+#else /* !TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _tt_post_dummy;
+
+#endif /* !TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttpost.h b/modules/freetype2/src/sfnt/ttpost.h
new file mode 100644
index 0000000000..528f1c5f2f
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttpost.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+ *
+ * ttpost.h
+ *
+ * PostScript name table processing for TrueType and OpenType fonts
+ * (specification).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#ifndef TTPOST_H_
+#define TTPOST_H_
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include <freetype/internal/tttypes.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_get_ps_name( TT_Face face,
+ FT_UInt idx,
+ FT_String** PSname );
+
+ FT_LOCAL( void )
+ tt_face_free_ps_names( TT_Face face );
+
+
+FT_END_HEADER
+
+#endif /* TTPOST_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttsbit.c b/modules/freetype2/src/sfnt/ttsbit.c
new file mode 100644
index 0000000000..3c06955131
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttsbit.c
@@ -0,0 +1,1685 @@
+/****************************************************************************
+ *
+ * ttsbit.c
+ *
+ * TrueType and OpenType embedded bitmap support (body).
+ *
+ * Copyright (C) 2005-2023 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 */
diff --git a/modules/freetype2/src/sfnt/ttsbit.h b/modules/freetype2/src/sfnt/ttsbit.h
new file mode 100644
index 0000000000..07e2db461a
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttsbit.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+ *
+ * ttsbit.h
+ *
+ * TrueType and OpenType embedded bitmap support (specification).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#ifndef TTSBIT_H_
+#define TTSBIT_H_
+
+
+#include "ttload.h"
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_sbit( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_free_sbit( TT_Face face );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_set_sbit_strike( TT_Face face,
+ FT_Size_Request req,
+ FT_ULong* astrike_index );
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_strike_metrics( TT_Face face,
+ FT_ULong strike_index,
+ FT_Size_Metrics* metrics );
+
+ FT_LOCAL( 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_END_HEADER
+
+#endif /* TTSBIT_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttsvg.c b/modules/freetype2/src/sfnt/ttsvg.c
new file mode 100644
index 0000000000..c1bbb66b81
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttsvg.c
@@ -0,0 +1,413 @@
+/****************************************************************************
+ *
+ * ttsvg.c
+ *
+ * OpenType SVG Color (specification).
+ *
+ * Copyright (C) 2022-2023 by
+ * David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti.
+ *
+ * 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.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * 'SVG' table specification:
+ *
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/svg
+ *
+ */
+
+#include <ft2build.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/tttags.h>
+#include <freetype/ftgzip.h>
+#include <freetype/otsvg.h>
+
+
+#ifdef FT_CONFIG_OPTION_SVG
+
+#include "ttsvg.h"
+
+
+ /* NOTE: These table sizes are given by the specification. */
+#define SVG_TABLE_HEADER_SIZE (10U)
+#define SVG_DOCUMENT_RECORD_SIZE (12U)
+#define SVG_DOCUMENT_LIST_MINIMUM_SIZE (2U + SVG_DOCUMENT_RECORD_SIZE)
+#define SVG_MINIMUM_SIZE (SVG_TABLE_HEADER_SIZE + \
+ SVG_DOCUMENT_LIST_MINIMUM_SIZE)
+
+
+ typedef struct Svg_
+ {
+ FT_UShort version; /* table version (starting at 0) */
+ FT_UShort num_entries; /* number of SVG document records */
+
+ FT_Byte* svg_doc_list; /* pointer to the start of SVG Document List */
+
+ void* table; /* memory that backs up SVG */
+ FT_ULong table_size;
+
+ } Svg;
+
+
+ /**************************************************************************
+ *
+ * The macro FT_COMPONENT is used in trace mode. It is an implicit
+ * parameter of the FT_TRACE() and FT_ERROR() macros, usued to print/log
+ * messages during execution.
+ */
+#undef FT_COMPONENT
+#define FT_COMPONENT ttsvg
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_svg( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = face->root.memory;
+
+ FT_ULong table_size;
+ FT_Byte* table = NULL;
+ FT_Byte* p = NULL;
+ Svg* svg = NULL;
+ FT_ULong offsetToSVGDocumentList;
+
+
+ error = face->goto_table( face, TTAG_SVG, stream, &table_size );
+ if ( error )
+ goto NoSVG;
+
+ if ( table_size < SVG_MINIMUM_SIZE )
+ goto InvalidTable;
+
+ if ( FT_FRAME_EXTRACT( table_size, table ) )
+ goto NoSVG;
+
+ /* Allocate memory for the SVG object */
+ if ( FT_NEW( svg ) )
+ goto NoSVG;
+
+ p = table;
+ svg->version = FT_NEXT_USHORT( p );
+ offsetToSVGDocumentList = FT_NEXT_ULONG( p );
+
+ if ( offsetToSVGDocumentList < SVG_TABLE_HEADER_SIZE ||
+ offsetToSVGDocumentList > table_size -
+ SVG_DOCUMENT_LIST_MINIMUM_SIZE )
+ goto InvalidTable;
+
+ svg->svg_doc_list = (FT_Byte*)( table + offsetToSVGDocumentList );
+
+ p = svg->svg_doc_list;
+ svg->num_entries = FT_NEXT_USHORT( p );
+
+ FT_TRACE3(( "version: %d\n", svg->version ));
+ FT_TRACE3(( "number of entries: %d\n", svg->num_entries ));
+
+ if ( offsetToSVGDocumentList + 2U +
+ svg->num_entries * SVG_DOCUMENT_RECORD_SIZE > table_size )
+ goto InvalidTable;
+
+ svg->table = table;
+ svg->table_size = table_size;
+
+ face->svg = svg;
+ face->root.face_flags |= FT_FACE_FLAG_SVG;
+
+ return FT_Err_Ok;
+
+ InvalidTable:
+ error = FT_THROW( Invalid_Table );
+
+ NoSVG:
+ FT_FRAME_RELEASE( table );
+ FT_FREE( svg );
+ face->svg = NULL;
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_svg( TT_Face face )
+ {
+ FT_Memory memory = face->root.memory;
+ FT_Stream stream = face->root.stream;
+
+ Svg* svg = (Svg*)face->svg;
+
+
+ if ( svg )
+ {
+ FT_FRAME_RELEASE( svg->table );
+ FT_FREE( svg );
+ }
+ }
+
+
+ typedef struct Svg_doc_
+ {
+ FT_UShort start_glyph_id;
+ FT_UShort end_glyph_id;
+
+ FT_ULong offset;
+ FT_ULong length;
+
+ } Svg_doc;
+
+
+ static Svg_doc
+ extract_svg_doc( FT_Byte* stream )
+ {
+ Svg_doc doc;
+
+
+ doc.start_glyph_id = FT_NEXT_USHORT( stream );
+ doc.end_glyph_id = FT_NEXT_USHORT( stream );
+
+ doc.offset = FT_NEXT_ULONG( stream );
+ doc.length = FT_NEXT_ULONG( stream );
+
+ return doc;
+ }
+
+
+ static FT_Int
+ compare_svg_doc( Svg_doc doc,
+ FT_UInt glyph_index )
+ {
+ if ( glyph_index < doc.start_glyph_id )
+ return -1;
+ else if ( glyph_index > doc.end_glyph_id )
+ return 1;
+ else
+ return 0;
+ }
+
+
+ static FT_Error
+ find_doc( FT_Byte* document_records,
+ FT_UShort num_entries,
+ FT_UInt glyph_index,
+ FT_ULong *doc_offset,
+ FT_ULong *doc_length,
+ FT_UShort *start_glyph,
+ FT_UShort *end_glyph )
+ {
+ FT_Error error;
+
+ Svg_doc start_doc;
+ Svg_doc mid_doc = { 0, 0, 0, 0 }; /* pacify compiler */
+ Svg_doc end_doc;
+
+ FT_Bool found = FALSE;
+ FT_UInt i = 0;
+
+ FT_UInt start_index = 0;
+ FT_UInt end_index = num_entries - 1;
+ FT_Int comp_res;
+
+
+ /* search algorithm */
+ if ( num_entries == 0 )
+ {
+ error = FT_THROW( Invalid_Table );
+ return error;
+ }
+
+ start_doc = extract_svg_doc( document_records + start_index * 12 );
+ end_doc = extract_svg_doc( document_records + end_index * 12 );
+
+ if ( ( compare_svg_doc( start_doc, glyph_index ) == -1 ) ||
+ ( compare_svg_doc( end_doc, glyph_index ) == 1 ) )
+ {
+ error = FT_THROW( Invalid_Glyph_Index );
+ return error;
+ }
+
+ while ( start_index <= end_index )
+ {
+ i = ( start_index + end_index ) / 2;
+ mid_doc = extract_svg_doc( document_records + i * 12 );
+ comp_res = compare_svg_doc( mid_doc, glyph_index );
+
+ if ( comp_res == 1 )
+ {
+ start_index = i + 1;
+ start_doc = extract_svg_doc( document_records + start_index * 4 );
+ }
+ else if ( comp_res == -1 )
+ {
+ end_index = i - 1;
+ end_doc = extract_svg_doc( document_records + end_index * 4 );
+ }
+ else
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ /* search algorithm end */
+
+ if ( found != TRUE )
+ {
+ FT_TRACE5(( "SVG glyph not found\n" ));
+ error = FT_THROW( Invalid_Glyph_Index );
+ }
+ else
+ {
+ *doc_offset = mid_doc.offset;
+ *doc_length = mid_doc.length;
+
+ *start_glyph = mid_doc.start_glyph_id;
+ *end_glyph = mid_doc.end_glyph_id;
+
+ error = FT_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_svg_doc( FT_GlyphSlot glyph,
+ FT_UInt glyph_index )
+ {
+ FT_Error error = FT_Err_Ok;
+ TT_Face face = (TT_Face)glyph->face;
+ FT_Memory memory = face->root.memory;
+ Svg* svg = (Svg*)face->svg;
+
+ FT_Byte* doc_list;
+ FT_ULong doc_limit;
+
+ FT_Byte* doc;
+ FT_ULong doc_offset;
+ FT_ULong doc_length;
+ FT_UShort doc_start_glyph_id;
+ FT_UShort doc_end_glyph_id;
+
+ FT_SVG_Document svg_document = (FT_SVG_Document)glyph->other;
+
+
+ FT_ASSERT( !( svg == NULL ) );
+
+ doc_list = svg->svg_doc_list;
+
+ error = find_doc( doc_list + 2, svg->num_entries, glyph_index,
+ &doc_offset, &doc_length,
+ &doc_start_glyph_id, &doc_end_glyph_id );
+ if ( error != FT_Err_Ok )
+ goto Exit;
+
+ doc_limit = svg->table_size -
+ (FT_ULong)( doc_list - (FT_Byte*)svg->table );
+ if ( doc_offset > doc_limit ||
+ doc_length > doc_limit - doc_offset )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ doc = doc_list + doc_offset;
+
+ if ( doc_length > 6 &&
+ doc[0] == 0x1F &&
+ doc[1] == 0x8B &&
+ doc[2] == 0x08 )
+ {
+#ifdef FT_CONFIG_OPTION_USE_ZLIB
+
+ FT_ULong uncomp_size;
+ FT_Byte* uncomp_buffer = NULL;
+
+
+ /*
+ * Get the size of the original document. This helps in allotting the
+ * buffer to accommodate the uncompressed version. The last 4 bytes
+ * of the compressed document are equal to the original size modulo
+ * 2^32. Since the size of SVG documents is less than 2^32 bytes we
+ * can use this accurately. The four bytes are stored in
+ * little-endian format.
+ */
+ FT_TRACE4(( "SVG document is GZIP compressed\n" ));
+ uncomp_size = (FT_ULong)doc[doc_length - 1] << 24 |
+ (FT_ULong)doc[doc_length - 2] << 16 |
+ (FT_ULong)doc[doc_length - 3] << 8 |
+ (FT_ULong)doc[doc_length - 4];
+
+ if ( FT_QALLOC( uncomp_buffer, uncomp_size ) )
+ goto Exit;
+
+ error = FT_Gzip_Uncompress( memory,
+ uncomp_buffer,
+ &uncomp_size,
+ doc,
+ doc_length );
+ if ( error )
+ {
+ FT_FREE( uncomp_buffer );
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ glyph->internal->flags |= FT_GLYPH_OWN_GZIP_SVG;
+
+ doc = uncomp_buffer;
+ doc_length = uncomp_size;
+
+#else /* !FT_CONFIG_OPTION_USE_ZLIB */
+
+ error = FT_THROW( Unimplemented_Feature );
+ goto Exit;
+
+#endif /* !FT_CONFIG_OPTION_USE_ZLIB */
+ }
+
+ svg_document->svg_document = doc;
+ svg_document->svg_document_length = doc_length;
+
+ svg_document->metrics = glyph->face->size->metrics;
+ svg_document->units_per_EM = glyph->face->units_per_EM;
+
+ svg_document->start_glyph_id = doc_start_glyph_id;
+ svg_document->end_glyph_id = doc_end_glyph_id;
+
+ svg_document->transform.xx = 0x10000;
+ svg_document->transform.xy = 0;
+ svg_document->transform.yx = 0;
+ svg_document->transform.yy = 0x10000;
+
+ svg_document->delta.x = 0;
+ svg_document->delta.y = 0;
+
+ FT_TRACE5(( "start_glyph_id: %d\n", doc_start_glyph_id ));
+ FT_TRACE5(( "end_glyph_id: %d\n", doc_end_glyph_id ));
+ FT_TRACE5(( "svg_document:\n" ));
+ FT_TRACE5(( " %.*s\n", (FT_UInt)doc_length, doc ));
+
+ glyph->other = svg_document;
+
+ Exit:
+ return error;
+ }
+
+#else /* !FT_CONFIG_OPTION_SVG */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _tt_svg_dummy;
+
+#endif /* !FT_CONFIG_OPTION_SVG */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/ttsvg.h b/modules/freetype2/src/sfnt/ttsvg.h
new file mode 100644
index 0000000000..3f32321ded
--- /dev/null
+++ b/modules/freetype2/src/sfnt/ttsvg.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+ *
+ * ttsvg.h
+ *
+ * OpenType SVG Color (specification).
+ *
+ * Copyright (C) 2022-2023 by
+ * David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti.
+ *
+ * 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.
+ *
+ */
+
+#ifndef TTSVG_H_
+#define TTSVG_H_
+
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/tttypes.h>
+
+
+FT_BEGIN_HEADER
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_svg( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_free_svg( TT_Face face );
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_svg_doc( FT_GlyphSlot glyph,
+ FT_UInt glyph_index );
+
+FT_END_HEADER
+
+#endif /* TTSVG_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/woff2tags.c b/modules/freetype2/src/sfnt/woff2tags.c
new file mode 100644
index 0000000000..7a0a351f06
--- /dev/null
+++ b/modules/freetype2/src/sfnt/woff2tags.c
@@ -0,0 +1,119 @@
+/****************************************************************************
+ *
+ * woff2tags.c
+ *
+ * WOFF2 Font table tags (base).
+ *
+ * Copyright (C) 2019-2023 by
+ * Nikhil Ramakrishnan, 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 <freetype/tttags.h>
+
+#ifdef FT_CONFIG_OPTION_USE_BROTLI
+
+#include "woff2tags.h"
+
+ /*
+ * Return tag from index in the order given in WOFF2 specification.
+ *
+ * See
+ *
+ * https://www.w3.org/TR/WOFF2/#table_dir_format
+ *
+ * for details.
+ */
+ FT_LOCAL_DEF( FT_Tag )
+ woff2_known_tags( FT_Byte index )
+ {
+ static const FT_Tag known_tags[63] =
+ {
+ FT_MAKE_TAG('c', 'm', 'a', 'p'), /* 0 */
+ FT_MAKE_TAG('h', 'e', 'a', 'd'), /* 1 */
+ FT_MAKE_TAG('h', 'h', 'e', 'a'), /* 2 */
+ FT_MAKE_TAG('h', 'm', 't', 'x'), /* 3 */
+ FT_MAKE_TAG('m', 'a', 'x', 'p'), /* 4 */
+ FT_MAKE_TAG('n', 'a', 'm', 'e'), /* 5 */
+ FT_MAKE_TAG('O', 'S', '/', '2'), /* 6 */
+ FT_MAKE_TAG('p', 'o', 's', 't'), /* 7 */
+ FT_MAKE_TAG('c', 'v', 't', ' '), /* 8 */
+ FT_MAKE_TAG('f', 'p', 'g', 'm'), /* 9 */
+ FT_MAKE_TAG('g', 'l', 'y', 'f'), /* 10 */
+ FT_MAKE_TAG('l', 'o', 'c', 'a'), /* 11 */
+ FT_MAKE_TAG('p', 'r', 'e', 'p'), /* 12 */
+ FT_MAKE_TAG('C', 'F', 'F', ' '), /* 13 */
+ FT_MAKE_TAG('V', 'O', 'R', 'G'), /* 14 */
+ FT_MAKE_TAG('E', 'B', 'D', 'T'), /* 15 */
+ FT_MAKE_TAG('E', 'B', 'L', 'C'), /* 16 */
+ FT_MAKE_TAG('g', 'a', 's', 'p'), /* 17 */
+ FT_MAKE_TAG('h', 'd', 'm', 'x'), /* 18 */
+ FT_MAKE_TAG('k', 'e', 'r', 'n'), /* 19 */
+ FT_MAKE_TAG('L', 'T', 'S', 'H'), /* 20 */
+ FT_MAKE_TAG('P', 'C', 'L', 'T'), /* 21 */
+ FT_MAKE_TAG('V', 'D', 'M', 'X'), /* 22 */
+ FT_MAKE_TAG('v', 'h', 'e', 'a'), /* 23 */
+ FT_MAKE_TAG('v', 'm', 't', 'x'), /* 24 */
+ FT_MAKE_TAG('B', 'A', 'S', 'E'), /* 25 */
+ FT_MAKE_TAG('G', 'D', 'E', 'F'), /* 26 */
+ FT_MAKE_TAG('G', 'P', 'O', 'S'), /* 27 */
+ FT_MAKE_TAG('G', 'S', 'U', 'B'), /* 28 */
+ FT_MAKE_TAG('E', 'B', 'S', 'C'), /* 29 */
+ FT_MAKE_TAG('J', 'S', 'T', 'F'), /* 30 */
+ FT_MAKE_TAG('M', 'A', 'T', 'H'), /* 31 */
+ FT_MAKE_TAG('C', 'B', 'D', 'T'), /* 32 */
+ FT_MAKE_TAG('C', 'B', 'L', 'C'), /* 33 */
+ FT_MAKE_TAG('C', 'O', 'L', 'R'), /* 34 */
+ FT_MAKE_TAG('C', 'P', 'A', 'L'), /* 35 */
+ FT_MAKE_TAG('S', 'V', 'G', ' '), /* 36 */
+ FT_MAKE_TAG('s', 'b', 'i', 'x'), /* 37 */
+ FT_MAKE_TAG('a', 'c', 'n', 't'), /* 38 */
+ FT_MAKE_TAG('a', 'v', 'a', 'r'), /* 39 */
+ FT_MAKE_TAG('b', 'd', 'a', 't'), /* 40 */
+ FT_MAKE_TAG('b', 'l', 'o', 'c'), /* 41 */
+ FT_MAKE_TAG('b', 's', 'l', 'n'), /* 42 */
+ FT_MAKE_TAG('c', 'v', 'a', 'r'), /* 43 */
+ FT_MAKE_TAG('f', 'd', 's', 'c'), /* 44 */
+ FT_MAKE_TAG('f', 'e', 'a', 't'), /* 45 */
+ FT_MAKE_TAG('f', 'm', 't', 'x'), /* 46 */
+ FT_MAKE_TAG('f', 'v', 'a', 'r'), /* 47 */
+ FT_MAKE_TAG('g', 'v', 'a', 'r'), /* 48 */
+ FT_MAKE_TAG('h', 's', 't', 'y'), /* 49 */
+ FT_MAKE_TAG('j', 'u', 's', 't'), /* 50 */
+ FT_MAKE_TAG('l', 'c', 'a', 'r'), /* 51 */
+ FT_MAKE_TAG('m', 'o', 'r', 't'), /* 52 */
+ FT_MAKE_TAG('m', 'o', 'r', 'x'), /* 53 */
+ FT_MAKE_TAG('o', 'p', 'b', 'd'), /* 54 */
+ FT_MAKE_TAG('p', 'r', 'o', 'p'), /* 55 */
+ FT_MAKE_TAG('t', 'r', 'a', 'k'), /* 56 */
+ FT_MAKE_TAG('Z', 'a', 'p', 'f'), /* 57 */
+ FT_MAKE_TAG('S', 'i', 'l', 'f'), /* 58 */
+ FT_MAKE_TAG('G', 'l', 'a', 't'), /* 59 */
+ FT_MAKE_TAG('G', 'l', 'o', 'c'), /* 60 */
+ FT_MAKE_TAG('F', 'e', 'a', 't'), /* 61 */
+ FT_MAKE_TAG('S', 'i', 'l', 'l'), /* 62 */
+ };
+
+
+ if ( index > 62 )
+ return 0;
+
+ return known_tags[index];
+ }
+
+#else /* !FT_CONFIG_OPTION_USE_BROTLI */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _woff2tags_dummy;
+
+#endif /* !FT_CONFIG_OPTION_USE_BROTLI */
+
+
+/* END */
diff --git a/modules/freetype2/src/sfnt/woff2tags.h b/modules/freetype2/src/sfnt/woff2tags.h
new file mode 100644
index 0000000000..1201848e5e
--- /dev/null
+++ b/modules/freetype2/src/sfnt/woff2tags.h
@@ -0,0 +1,41 @@
+/****************************************************************************
+ *
+ * woff2tags.h
+ *
+ * WOFF2 Font table tags (specification).
+ *
+ * Copyright (C) 2019-2023 by
+ * Nikhil Ramakrishnan, 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.
+ *
+ */
+
+
+#ifndef WOFF2TAGS_H
+#define WOFF2TAGS_H
+
+
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/compiler-macros.h>
+
+
+FT_BEGIN_HEADER
+
+#ifdef FT_CONFIG_OPTION_USE_BROTLI
+
+ FT_LOCAL( FT_Tag )
+ woff2_known_tags( FT_Byte index );
+
+#endif
+
+FT_END_HEADER
+
+#endif /* WOFF2TAGS_H */
+
+
+/* END */