summaryrefslogtreecommitdiffstats
path: root/modules/freetype2/src/base
diff options
context:
space:
mode:
Diffstat (limited to 'modules/freetype2/src/base')
-rw-r--r--modules/freetype2/src/base/ftadvanc.c174
-rw-r--r--modules/freetype2/src/base/ftbase.c41
-rw-r--r--modules/freetype2/src/base/ftbase.h82
-rw-r--r--modules/freetype2/src/base/ftbbox.c530
-rw-r--r--modules/freetype2/src/base/ftbdf.c90
-rw-r--r--modules/freetype2/src/base/ftbitmap.c1144
-rw-r--r--modules/freetype2/src/base/ftcalc.c1155
-rw-r--r--modules/freetype2/src/base/ftcid.c117
-rw-r--r--modules/freetype2/src/base/ftcolor.c156
-rw-r--r--modules/freetype2/src/base/ftdbgmem.c971
-rw-r--r--modules/freetype2/src/base/ftdebug.c644
-rw-r--r--modules/freetype2/src/base/fterrors.c45
-rw-r--r--modules/freetype2/src/base/ftfntfmt.c54
-rw-r--r--modules/freetype2/src/base/ftfstype.c61
-rw-r--r--modules/freetype2/src/base/ftgasp.c60
-rw-r--r--modules/freetype2/src/base/ftgloadr.c392
-rw-r--r--modules/freetype2/src/base/ftglyph.c911
-rw-r--r--modules/freetype2/src/base/ftgxval.c141
-rw-r--r--modules/freetype2/src/base/fthash.c338
-rw-r--r--modules/freetype2/src/base/ftinit.c263
-rw-r--r--modules/freetype2/src/base/ftlcdfil.c437
-rw-r--r--modules/freetype2/src/base/ftmac.c1090
-rw-r--r--modules/freetype2/src/base/ftmm.c568
-rw-r--r--modules/freetype2/src/base/ftobjs.c5906
-rw-r--r--modules/freetype2/src/base/ftotval.c90
-rw-r--r--modules/freetype2/src/base/ftoutln.c1122
-rw-r--r--modules/freetype2/src/base/ftpatent.c50
-rw-r--r--modules/freetype2/src/base/ftpfr.c152
-rw-r--r--modules/freetype2/src/base/ftpsprop.c284
-rw-r--r--modules/freetype2/src/base/ftrfork.c934
-rw-r--r--modules/freetype2/src/base/ftsnames.c185
-rw-r--r--modules/freetype2/src/base/ftstream.c868
-rw-r--r--modules/freetype2/src/base/ftstroke.c2408
-rw-r--r--modules/freetype2/src/base/ftsynth.c172
-rw-r--r--modules/freetype2/src/base/ftsystem.c333
-rw-r--r--modules/freetype2/src/base/fttrigon.c517
-rw-r--r--modules/freetype2/src/base/fttype1.c126
-rw-r--r--modules/freetype2/src/base/ftutil.c442
-rw-r--r--modules/freetype2/src/base/ftver.rc61
-rw-r--r--modules/freetype2/src/base/ftwinfnt.c52
-rw-r--r--modules/freetype2/src/base/md5.c291
-rw-r--r--modules/freetype2/src/base/md5.h45
-rw-r--r--modules/freetype2/src/base/rules.mk108
43 files changed, 23610 insertions, 0 deletions
diff --git a/modules/freetype2/src/base/ftadvanc.c b/modules/freetype2/src/base/ftadvanc.c
new file mode 100644
index 0000000000..de25476fe9
--- /dev/null
+++ b/modules/freetype2/src/base/ftadvanc.c
@@ -0,0 +1,174 @@
+/****************************************************************************
+ *
+ * ftadvanc.c
+ *
+ * Quick computation of advance widths (body).
+ *
+ * Copyright (C) 2008-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/ftadvanc.h>
+#include <freetype/internal/ftobjs.h>
+
+
+ static FT_Error
+ ft_face_scale_advances_( FT_Face face,
+ FT_Fixed* advances,
+ FT_UInt count,
+ FT_Int32 flags )
+ {
+ FT_Fixed scale;
+ FT_UInt nn;
+
+
+ if ( flags & FT_LOAD_NO_SCALE )
+ return FT_Err_Ok;
+
+ if ( !face->size )
+ return FT_THROW( Invalid_Size_Handle );
+
+ if ( flags & FT_LOAD_VERTICAL_LAYOUT )
+ scale = face->size->metrics.y_scale;
+ else
+ scale = face->size->metrics.x_scale;
+
+ /* this must be the same scaling as to get linear{Hori,Vert}Advance */
+ /* (see `FT_Load_Glyph' implementation in src/base/ftobjs.c) */
+
+ for ( nn = 0; nn < count; nn++ )
+ advances[nn] = FT_MulDiv( advances[nn], scale, 64 );
+
+ return FT_Err_Ok;
+ }
+
+
+ /* at the moment, we can perform fast advance retrieval only in */
+ /* the following cases: */
+ /* */
+ /* - unscaled load */
+ /* - unhinted load */
+ /* - light-hinted load */
+ /* - if a variations font, it must have an `HVAR' or `VVAR' */
+ /* table (thus the old MM or GX fonts don't qualify; this */
+ /* gets checked by the driver-specific functions) */
+
+#define LOAD_ADVANCE_FAST_CHECK( face, flags ) \
+ ( flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) || \
+ FT_LOAD_TARGET_MODE( flags ) == FT_RENDER_MODE_LIGHT )
+
+
+ /* documentation is in ftadvanc.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Advance( FT_Face face,
+ FT_UInt gindex,
+ FT_Int32 flags,
+ FT_Fixed *padvance )
+ {
+ FT_Face_GetAdvancesFunc func;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !padvance )
+ return FT_THROW( Invalid_Argument );
+
+ if ( gindex >= (FT_UInt)face->num_glyphs )
+ return FT_THROW( Invalid_Glyph_Index );
+
+ func = face->driver->clazz->get_advances;
+ if ( func && LOAD_ADVANCE_FAST_CHECK( face, flags ) )
+ {
+ FT_Error error;
+
+
+ error = func( face, gindex, 1, flags, padvance );
+ if ( !error )
+ return ft_face_scale_advances_( face, padvance, 1, flags );
+
+ if ( FT_ERR_NEQ( error, Unimplemented_Feature ) )
+ return error;
+ }
+
+ return FT_Get_Advances( face, gindex, 1, flags, padvance );
+ }
+
+
+ /* documentation is in ftadvanc.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Advances( FT_Face face,
+ FT_UInt start,
+ FT_UInt count,
+ FT_Int32 flags,
+ FT_Fixed *padvances )
+ {
+ FT_Error error = FT_Err_Ok;
+
+ FT_Face_GetAdvancesFunc func;
+
+ FT_UInt num, end, nn;
+ FT_Int factor;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !padvances )
+ return FT_THROW( Invalid_Argument );
+
+ num = (FT_UInt)face->num_glyphs;
+ end = start + count;
+ if ( start >= num || end < start || end > num )
+ return FT_THROW( Invalid_Glyph_Index );
+
+ if ( count == 0 )
+ return FT_Err_Ok;
+
+ func = face->driver->clazz->get_advances;
+ if ( func && LOAD_ADVANCE_FAST_CHECK( face, flags ) )
+ {
+ error = func( face, start, count, flags, padvances );
+ if ( !error )
+ return ft_face_scale_advances_( face, padvances, count, flags );
+
+ if ( FT_ERR_NEQ( error, Unimplemented_Feature ) )
+ return error;
+ }
+
+ error = FT_Err_Ok;
+
+ if ( flags & FT_ADVANCE_FLAG_FAST_ONLY )
+ return FT_THROW( Unimplemented_Feature );
+
+ flags |= (FT_UInt32)FT_LOAD_ADVANCE_ONLY;
+ factor = ( flags & FT_LOAD_NO_SCALE ) ? 1 : 1024;
+ for ( nn = 0; nn < count; nn++ )
+ {
+ error = FT_Load_Glyph( face, start + nn, flags );
+ if ( error )
+ break;
+
+ /* scale from 26.6 to 16.16, unless NO_SCALE was requested */
+ padvances[nn] = ( flags & FT_LOAD_VERTICAL_LAYOUT )
+ ? face->glyph->advance.y * factor
+ : face->glyph->advance.x * factor;
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftbase.c b/modules/freetype2/src/base/ftbase.c
new file mode 100644
index 0000000000..156510f007
--- /dev/null
+++ b/modules/freetype2/src/base/ftbase.c
@@ -0,0 +1,41 @@
+/****************************************************************************
+ *
+ * ftbase.c
+ *
+ * Single object library component (body only).
+ *
+ * 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 "ftadvanc.c"
+#include "ftcalc.c"
+#include "ftcolor.c"
+#include "ftdbgmem.c"
+#include "fterrors.c"
+#include "ftfntfmt.c"
+#include "ftgloadr.c"
+#include "fthash.c"
+#include "ftlcdfil.c"
+#include "ftmac.c"
+#include "ftobjs.c"
+#include "ftoutln.c"
+#include "ftpsprop.c"
+#include "ftrfork.c"
+#include "ftsnames.c"
+#include "ftstream.c"
+#include "fttrigon.c"
+#include "ftutil.c"
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftbase.h b/modules/freetype2/src/base/ftbase.h
new file mode 100644
index 0000000000..00790d3b22
--- /dev/null
+++ b/modules/freetype2/src/base/ftbase.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+ *
+ * ftbase.h
+ *
+ * Private functions used in the `base' module (specification).
+ *
+ * Copyright (C) 2008-2023 by
+ * David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya.
+ *
+ * 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 FTBASE_H_
+#define FTBASE_H_
+
+
+#include <freetype/internal/ftobjs.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_DECLARE_GLYPH( ft_bitmap_glyph_class )
+ FT_DECLARE_GLYPH( ft_outline_glyph_class )
+ FT_DECLARE_GLYPH( ft_svg_glyph_class )
+
+
+#ifdef FT_CONFIG_OPTION_MAC_FONTS
+
+ /* MacOS resource fork cannot exceed 16MB at least for Carbon code; */
+ /* see https://support.microsoft.com/en-us/kb/130437 */
+#define FT_MAC_RFORK_MAX_LEN 0x00FFFFFFUL
+
+
+ /* Assume the stream is sfnt-wrapped PS Type1 or sfnt-wrapped CID-keyed */
+ /* font, and try to load a face specified by the face_index. */
+ FT_LOCAL( FT_Error )
+ open_face_PS_from_sfnt_stream( FT_Library library,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Int num_params,
+ FT_Parameter *params,
+ FT_Face *aface );
+
+
+ /* Create a new FT_Face given a buffer and a driver name. */
+ /* From ftmac.c. */
+ FT_LOCAL( FT_Error )
+ open_face_from_buffer( FT_Library library,
+ FT_Byte* base,
+ FT_ULong size,
+ FT_Long face_index,
+ const char* driver_name,
+ FT_Face *aface );
+
+
+#if defined( FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK ) && \
+ !defined( FT_MACINTOSH )
+ /* Mac OS X/Darwin kernel often changes recommended method to access */
+ /* the resource fork and older methods makes the kernel issue the */
+ /* warning of deprecated method. To calm it down, the methods based */
+ /* on Darwin VFS should be grouped and skip the rest methods after */
+ /* the case the resource is opened but found to lack a font in it. */
+ FT_LOCAL( FT_Bool )
+ ft_raccess_rule_by_darwin_vfs( FT_Library library, FT_UInt rule_index );
+#endif
+
+#endif /* FT_CONFIG_OPTION_MAC_FONTS */
+
+
+FT_END_HEADER
+
+#endif /* FTBASE_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftbbox.c b/modules/freetype2/src/base/ftbbox.c
new file mode 100644
index 0000000000..7dd71882ea
--- /dev/null
+++ b/modules/freetype2/src/base/ftbbox.c
@@ -0,0 +1,530 @@
+/****************************************************************************
+ *
+ * ftbbox.c
+ *
+ * FreeType bbox computation (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.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * This component has a _single_ role: to compute exact outline bounding
+ * boxes.
+ *
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+
+#include <freetype/ftbbox.h>
+#include <freetype/ftimage.h>
+#include <freetype/ftoutln.h>
+#include <freetype/internal/ftcalc.h>
+#include <freetype/internal/ftobjs.h>
+
+
+ typedef struct TBBox_Rec_
+ {
+ FT_Vector last;
+ FT_BBox bbox;
+
+ } TBBox_Rec;
+
+
+#define FT_UPDATE_BBOX( p, bbox ) \
+ FT_BEGIN_STMNT \
+ if ( p->x < bbox.xMin ) \
+ bbox.xMin = p->x; \
+ if ( p->x > bbox.xMax ) \
+ bbox.xMax = p->x; \
+ if ( p->y < bbox.yMin ) \
+ bbox.yMin = p->y; \
+ if ( p->y > bbox.yMax ) \
+ bbox.yMax = p->y; \
+ FT_END_STMNT
+
+#define CHECK_X( p, bbox ) \
+ ( p->x < bbox.xMin || p->x > bbox.xMax )
+
+#define CHECK_Y( p, bbox ) \
+ ( p->y < bbox.yMin || p->y > bbox.yMax )
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * BBox_Move_To
+ *
+ * @Description:
+ * This function is used as a `move_to' emitter during
+ * FT_Outline_Decompose(). It simply records the destination point
+ * in `user->last'. We also update bbox in case contour starts with
+ * an implicit `on' point.
+ *
+ * @Input:
+ * to ::
+ * A pointer to the destination vector.
+ *
+ * @InOut:
+ * user ::
+ * A pointer to the current walk context.
+ *
+ * @Return:
+ * Always 0. Needed for the interface only.
+ */
+ static int
+ BBox_Move_To( FT_Vector* to,
+ TBBox_Rec* user )
+ {
+ FT_UPDATE_BBOX( to, user->bbox );
+
+ user->last = *to;
+
+ return 0;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * BBox_Line_To
+ *
+ * @Description:
+ * This function is used as a `line_to' emitter during
+ * FT_Outline_Decompose(). It simply records the destination point
+ * in `user->last'; no further computations are necessary because
+ * bbox already contains both explicit ends of the line segment.
+ *
+ * @Input:
+ * to ::
+ * A pointer to the destination vector.
+ *
+ * @InOut:
+ * user ::
+ * A pointer to the current walk context.
+ *
+ * @Return:
+ * Always 0. Needed for the interface only.
+ */
+ static int
+ BBox_Line_To( FT_Vector* to,
+ TBBox_Rec* user )
+ {
+ user->last = *to;
+
+ return 0;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * BBox_Conic_Check
+ *
+ * @Description:
+ * Find the extrema of a 1-dimensional conic Bezier curve and update
+ * a bounding range. This version uses direct computation, as it
+ * doesn't need square roots.
+ *
+ * @Input:
+ * y1 ::
+ * The start coordinate.
+ *
+ * y2 ::
+ * The coordinate of the control point.
+ *
+ * y3 ::
+ * The end coordinate.
+ *
+ * @InOut:
+ * min ::
+ * The address of the current minimum.
+ *
+ * max ::
+ * The address of the current maximum.
+ */
+ static void
+ BBox_Conic_Check( FT_Pos y1,
+ FT_Pos y2,
+ FT_Pos y3,
+ FT_Pos* min,
+ FT_Pos* max )
+ {
+ /* This function is only called when a control off-point is outside */
+ /* the bbox that contains all on-points. It finds a local extremum */
+ /* within the segment, equal to (y1*y3 - y2*y2)/(y1 - 2*y2 + y3). */
+ /* Or, offsetting from y2, we get */
+
+ y1 -= y2;
+ y3 -= y2;
+ y2 += FT_MulDiv( y1, y3, y1 + y3 );
+
+ if ( y2 < *min )
+ *min = y2;
+ if ( y2 > *max )
+ *max = y2;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * BBox_Conic_To
+ *
+ * @Description:
+ * This function is used as a `conic_to' emitter during
+ * FT_Outline_Decompose(). It checks a conic Bezier curve with the
+ * current bounding box, and computes its extrema if necessary to
+ * update it.
+ *
+ * @Input:
+ * control ::
+ * A pointer to a control point.
+ *
+ * to ::
+ * A pointer to the destination vector.
+ *
+ * @InOut:
+ * user ::
+ * The address of the current walk context.
+ *
+ * @Return:
+ * Always 0. Needed for the interface only.
+ *
+ * @Note:
+ * In the case of a non-monotonous arc, we compute directly the
+ * extremum coordinates, as it is sufficiently fast.
+ */
+ static int
+ BBox_Conic_To( FT_Vector* control,
+ FT_Vector* to,
+ TBBox_Rec* user )
+ {
+ /* in case `to' is implicit and not included in bbox yet */
+ FT_UPDATE_BBOX( to, user->bbox );
+
+ if ( CHECK_X( control, user->bbox ) )
+ BBox_Conic_Check( user->last.x,
+ control->x,
+ to->x,
+ &user->bbox.xMin,
+ &user->bbox.xMax );
+
+ if ( CHECK_Y( control, user->bbox ) )
+ BBox_Conic_Check( user->last.y,
+ control->y,
+ to->y,
+ &user->bbox.yMin,
+ &user->bbox.yMax );
+
+ user->last = *to;
+
+ return 0;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * BBox_Cubic_Check
+ *
+ * @Description:
+ * Find the extrema of a 1-dimensional cubic Bezier curve and
+ * update a bounding range. This version uses iterative splitting
+ * because it is faster than the exact solution with square roots.
+ *
+ * @Input:
+ * p1 ::
+ * The start coordinate.
+ *
+ * p2 ::
+ * The coordinate of the first control point.
+ *
+ * p3 ::
+ * The coordinate of the second control point.
+ *
+ * p4 ::
+ * The end coordinate.
+ *
+ * @InOut:
+ * min ::
+ * The address of the current minimum.
+ *
+ * max ::
+ * The address of the current maximum.
+ */
+ static FT_Pos
+ cubic_peak( FT_Pos q1,
+ FT_Pos q2,
+ FT_Pos q3,
+ FT_Pos q4 )
+ {
+ FT_Pos peak = 0;
+ FT_Int shift;
+
+
+ /* This function finds a peak of a cubic segment if it is above 0 */
+ /* using iterative bisection of the segment, or returns 0. */
+ /* The fixed-point arithmetic of bisection is inherently stable */
+ /* but may loose accuracy in the two lowest bits. To compensate, */
+ /* we upscale the segment if there is room. Large values may need */
+ /* to be downscaled to avoid overflows during bisection. */
+ /* It is called with either q2 or q3 positive, which is necessary */
+ /* for the peak to exist and avoids undefined FT_MSB. */
+
+ shift = 27 - FT_MSB( (FT_UInt32)( FT_ABS( q1 ) |
+ FT_ABS( q2 ) |
+ FT_ABS( q3 ) |
+ FT_ABS( q4 ) ) );
+
+ if ( shift > 0 )
+ {
+ /* upscaling too much just wastes time */
+ if ( shift > 2 )
+ shift = 2;
+
+ q1 *= 1 << shift;
+ q2 *= 1 << shift;
+ q3 *= 1 << shift;
+ q4 *= 1 << shift;
+ }
+ else
+ {
+ q1 >>= -shift;
+ q2 >>= -shift;
+ q3 >>= -shift;
+ q4 >>= -shift;
+ }
+
+ /* for a peak to exist above 0, the cubic segment must have */
+ /* at least one of its control off-points above 0. */
+ while ( q2 > 0 || q3 > 0 )
+ {
+ /* determine which half contains the maximum and split */
+ if ( q1 + q2 > q3 + q4 ) /* first half */
+ {
+ q4 = q4 + q3;
+ q3 = q3 + q2;
+ q2 = q2 + q1;
+ q4 = q4 + q3;
+ q3 = q3 + q2;
+ q4 = ( q4 + q3 ) >> 3;
+ q3 = q3 >> 2;
+ q2 = q2 >> 1;
+ }
+ else /* second half */
+ {
+ q1 = q1 + q2;
+ q2 = q2 + q3;
+ q3 = q3 + q4;
+ q1 = q1 + q2;
+ q2 = q2 + q3;
+ q1 = ( q1 + q2 ) >> 3;
+ q2 = q2 >> 2;
+ q3 = q3 >> 1;
+ }
+
+ /* check whether either end reached the maximum */
+ if ( q1 == q2 && q1 >= q3 )
+ {
+ peak = q1;
+ break;
+ }
+ if ( q3 == q4 && q2 <= q4 )
+ {
+ peak = q4;
+ break;
+ }
+ }
+
+ if ( shift > 0 )
+ peak >>= shift;
+ else
+ peak <<= -shift;
+
+ return peak;
+ }
+
+
+ static void
+ BBox_Cubic_Check( FT_Pos p1,
+ FT_Pos p2,
+ FT_Pos p3,
+ FT_Pos p4,
+ FT_Pos* min,
+ FT_Pos* max )
+ {
+ /* This function is only called when a control off-point is outside */
+ /* the bbox that contains all on-points. So at least one of the */
+ /* conditions below holds and cubic_peak is called with at least one */
+ /* non-zero argument. */
+
+ if ( p2 > *max || p3 > *max )
+ *max += cubic_peak( p1 - *max, p2 - *max, p3 - *max, p4 - *max );
+
+ /* now flip the signs to update the minimum */
+ if ( p2 < *min || p3 < *min )
+ *min -= cubic_peak( *min - p1, *min - p2, *min - p3, *min - p4 );
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * BBox_Cubic_To
+ *
+ * @Description:
+ * This function is used as a `cubic_to' emitter during
+ * FT_Outline_Decompose(). It checks a cubic Bezier curve with the
+ * current bounding box, and computes its extrema if necessary to
+ * update it.
+ *
+ * @Input:
+ * control1 ::
+ * A pointer to the first control point.
+ *
+ * control2 ::
+ * A pointer to the second control point.
+ *
+ * to ::
+ * A pointer to the destination vector.
+ *
+ * @InOut:
+ * user ::
+ * The address of the current walk context.
+ *
+ * @Return:
+ * Always 0. Needed for the interface only.
+ *
+ * @Note:
+ * In the case of a non-monotonous arc, we don't compute directly
+ * extremum coordinates, we subdivide instead.
+ */
+ static int
+ BBox_Cubic_To( FT_Vector* control1,
+ FT_Vector* control2,
+ FT_Vector* to,
+ TBBox_Rec* user )
+ {
+ /* We don't need to check `to' since it is always an on-point, */
+ /* thus within the bbox. Only segments with an off-point outside */
+ /* the bbox can possibly reach new extreme values. */
+
+ if ( CHECK_X( control1, user->bbox ) ||
+ CHECK_X( control2, user->bbox ) )
+ BBox_Cubic_Check( user->last.x,
+ control1->x,
+ control2->x,
+ to->x,
+ &user->bbox.xMin,
+ &user->bbox.xMax );
+
+ if ( CHECK_Y( control1, user->bbox ) ||
+ CHECK_Y( control2, user->bbox ) )
+ BBox_Cubic_Check( user->last.y,
+ control1->y,
+ control2->y,
+ to->y,
+ &user->bbox.yMin,
+ &user->bbox.yMax );
+
+ user->last = *to;
+
+ return 0;
+ }
+
+
+ FT_DEFINE_OUTLINE_FUNCS(
+ bbox_interface,
+
+ (FT_Outline_MoveTo_Func) BBox_Move_To, /* move_to */
+ (FT_Outline_LineTo_Func) BBox_Line_To, /* line_to */
+ (FT_Outline_ConicTo_Func)BBox_Conic_To, /* conic_to */
+ (FT_Outline_CubicTo_Func)BBox_Cubic_To, /* cubic_to */
+ 0, /* shift */
+ 0 /* delta */
+ )
+
+
+ /* documentation is in ftbbox.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Get_BBox( FT_Outline* outline,
+ FT_BBox *abbox )
+ {
+ FT_BBox cbox = { 0x7FFFFFFFL, 0x7FFFFFFFL,
+ -0x7FFFFFFFL, -0x7FFFFFFFL };
+ FT_BBox bbox = { 0x7FFFFFFFL, 0x7FFFFFFFL,
+ -0x7FFFFFFFL, -0x7FFFFFFFL };
+ FT_Vector* vec;
+ FT_UShort n;
+
+
+ if ( !abbox )
+ return FT_THROW( Invalid_Argument );
+
+ if ( !outline )
+ return FT_THROW( Invalid_Outline );
+
+ /* if outline is empty, return (0,0,0,0) */
+ if ( outline->n_points == 0 || outline->n_contours <= 0 )
+ {
+ abbox->xMin = abbox->xMax = 0;
+ abbox->yMin = abbox->yMax = 0;
+
+ return 0;
+ }
+
+ /* We compute the control box as well as the bounding box of */
+ /* all `on' points in the outline. Then, if the two boxes */
+ /* coincide, we exit immediately. */
+
+ vec = outline->points;
+
+ for ( n = 0; n < outline->n_points; n++ )
+ {
+ FT_UPDATE_BBOX( vec, cbox );
+
+ if ( FT_CURVE_TAG( outline->tags[n] ) == FT_CURVE_TAG_ON )
+ FT_UPDATE_BBOX( vec, bbox );
+
+ vec++;
+ }
+
+ /* test two boxes for equality */
+ if ( cbox.xMin < bbox.xMin || cbox.xMax > bbox.xMax ||
+ cbox.yMin < bbox.yMin || cbox.yMax > bbox.yMax )
+ {
+ /* the two boxes are different, now walk over the outline to */
+ /* get the Bezier arc extrema. */
+
+ FT_Error error;
+ TBBox_Rec user;
+
+
+ user.bbox = bbox;
+
+ error = FT_Outline_Decompose( outline, &bbox_interface, &user );
+ if ( error )
+ return error;
+
+ *abbox = user.bbox;
+ }
+ else
+ *abbox = bbox;
+
+ return FT_Err_Ok;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftbdf.c b/modules/freetype2/src/base/ftbdf.c
new file mode 100644
index 0000000000..f697c00fec
--- /dev/null
+++ b/modules/freetype2/src/base/ftbdf.c
@@ -0,0 +1,90 @@
+/****************************************************************************
+ *
+ * ftbdf.c
+ *
+ * FreeType API for accessing BDF-specific strings (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 <freetype/internal/ftobjs.h>
+#include <freetype/internal/services/svbdf.h>
+
+
+ /* documentation is in ftbdf.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_BDF_Charset_ID( FT_Face face,
+ const char* *acharset_encoding,
+ const char* *acharset_registry )
+ {
+ FT_Error error;
+ const char* encoding = NULL;
+ const char* registry = NULL;
+
+ FT_Service_BDF service;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ FT_FACE_FIND_SERVICE( face, service, BDF );
+
+ if ( service && service->get_charset_id )
+ error = service->get_charset_id( face, &encoding, &registry );
+ else
+ error = FT_THROW( Invalid_Argument );
+
+ if ( acharset_encoding )
+ *acharset_encoding = encoding;
+
+ if ( acharset_registry )
+ *acharset_registry = registry;
+
+ return error;
+ }
+
+
+ /* documentation is in ftbdf.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_BDF_Property( FT_Face face,
+ const char* prop_name,
+ BDF_PropertyRec *aproperty )
+ {
+ FT_Error error;
+
+ FT_Service_BDF service;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !aproperty )
+ return FT_THROW( Invalid_Argument );
+
+ aproperty->type = BDF_PROPERTY_TYPE_NONE;
+
+ FT_FACE_FIND_SERVICE( face, service, BDF );
+
+ if ( service && service->get_property )
+ error = service->get_property( face, prop_name, aproperty );
+ else
+ error = FT_THROW( Invalid_Argument );
+
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftbitmap.c b/modules/freetype2/src/base/ftbitmap.c
new file mode 100644
index 0000000000..1c93648dcb
--- /dev/null
+++ b/modules/freetype2/src/base/ftbitmap.c
@@ -0,0 +1,1144 @@
+/****************************************************************************
+ *
+ * ftbitmap.c
+ *
+ * FreeType utility functions for bitmaps (body).
+ *
+ * Copyright (C) 2004-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/ftbitmap.h>
+#include <freetype/ftimage.h>
+#include <freetype/internal/ftobjs.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 bitmap
+
+
+ static
+ const FT_Bitmap null_bitmap = { 0, 0, 0, NULL, 0, 0, 0, NULL };
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Bitmap_Init( FT_Bitmap *abitmap )
+ {
+ if ( abitmap )
+ *abitmap = null_bitmap;
+ }
+
+
+ /* deprecated function name; retained for ABI compatibility */
+
+ FT_EXPORT_DEF( void )
+ FT_Bitmap_New( FT_Bitmap *abitmap )
+ {
+ if ( abitmap )
+ *abitmap = null_bitmap;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Copy( FT_Library library,
+ const FT_Bitmap *source,
+ FT_Bitmap *target)
+ {
+ FT_Memory memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Int pitch;
+ FT_Int flip;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !source || !target )
+ return FT_THROW( Invalid_Argument );
+
+ if ( source == target )
+ return FT_Err_Ok;
+
+ flip = ( source->pitch < 0 && target->pitch > 0 ) ||
+ ( source->pitch > 0 && target->pitch < 0 );
+
+ memory = library->memory;
+ FT_FREE( target->buffer );
+
+ *target = *source;
+
+ if ( flip )
+ target->pitch = -target->pitch;
+
+ if ( !source->buffer )
+ return FT_Err_Ok;
+
+ pitch = source->pitch;
+ if ( pitch < 0 )
+ pitch = -pitch;
+
+ FT_MEM_QALLOC_MULT( target->buffer, target->rows, pitch );
+
+ if ( !error )
+ {
+ if ( flip )
+ {
+ /* take care of bitmap flow */
+ FT_UInt i;
+ FT_Byte* s = source->buffer;
+ FT_Byte* t = target->buffer;
+
+
+ t += (FT_ULong)pitch * ( target->rows - 1 );
+
+ for ( i = target->rows; i > 0; i-- )
+ {
+ FT_ARRAY_COPY( t, s, pitch );
+
+ s += pitch;
+ t -= pitch;
+ }
+ }
+ else
+ FT_MEM_COPY( target->buffer, source->buffer,
+ (FT_Long)source->rows * pitch );
+ }
+
+ return error;
+ }
+
+
+ /* Enlarge `bitmap' horizontally and vertically by `xpixels' */
+ /* and `ypixels', respectively. */
+
+ static FT_Error
+ ft_bitmap_assure_buffer( FT_Memory memory,
+ FT_Bitmap* bitmap,
+ FT_UInt xpixels,
+ FT_UInt ypixels )
+ {
+ FT_Error error;
+ unsigned int pitch;
+ unsigned int new_pitch;
+ FT_UInt bpp;
+ FT_UInt width, height;
+ unsigned char* buffer = NULL;
+
+
+ width = bitmap->width;
+ height = bitmap->rows;
+ pitch = (unsigned int)FT_ABS( bitmap->pitch );
+
+ switch ( bitmap->pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ bpp = 1;
+ new_pitch = ( width + xpixels + 7 ) >> 3;
+ break;
+ case FT_PIXEL_MODE_GRAY2:
+ bpp = 2;
+ new_pitch = ( width + xpixels + 3 ) >> 2;
+ break;
+ case FT_PIXEL_MODE_GRAY4:
+ bpp = 4;
+ new_pitch = ( width + xpixels + 1 ) >> 1;
+ break;
+ case FT_PIXEL_MODE_GRAY:
+ case FT_PIXEL_MODE_LCD:
+ case FT_PIXEL_MODE_LCD_V:
+ bpp = 8;
+ new_pitch = width + xpixels;
+ break;
+ default:
+ return FT_THROW( Invalid_Glyph_Format );
+ }
+
+ /* if no need to allocate memory */
+ if ( ypixels == 0 && new_pitch <= pitch )
+ {
+ /* zero the padding */
+ FT_UInt bit_width = pitch * 8;
+ FT_UInt bit_last = ( width + xpixels ) * bpp;
+
+
+ if ( bit_last < bit_width )
+ {
+ FT_Byte* line = bitmap->buffer + ( bit_last >> 3 );
+ FT_Byte* end = bitmap->buffer + pitch;
+ FT_UInt shift = bit_last & 7;
+ FT_UInt mask = 0xFF00U >> shift;
+ FT_UInt count = height;
+
+
+ for ( ; count > 0; count--, line += pitch, end += pitch )
+ {
+ FT_Byte* write = line;
+
+
+ if ( shift > 0 )
+ {
+ write[0] = (FT_Byte)( write[0] & mask );
+ write++;
+ }
+ if ( write < end )
+ FT_MEM_ZERO( write, end - write );
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+ /* otherwise allocate new buffer */
+ if ( FT_QALLOC_MULT( buffer, bitmap->rows + ypixels, new_pitch ) )
+ return error;
+
+ /* new rows get added at the top of the bitmap, */
+ /* thus take care of the flow direction */
+ if ( bitmap->pitch > 0 )
+ {
+ FT_UInt len = ( width * bpp + 7 ) >> 3;
+
+ unsigned char* in = bitmap->buffer;
+ unsigned char* out = buffer;
+
+ unsigned char* limit = bitmap->buffer + pitch * bitmap->rows;
+ unsigned int delta = new_pitch - len;
+
+
+ FT_MEM_ZERO( out, new_pitch * ypixels );
+ out += new_pitch * ypixels;
+
+ while ( in < limit )
+ {
+ FT_MEM_COPY( out, in, len );
+ in += pitch;
+ out += len;
+
+ /* we use FT_QALLOC_MULT, which doesn't zero out the buffer; */
+ /* consequently, we have to manually zero out the remaining bytes */
+ FT_MEM_ZERO( out, delta );
+ out += delta;
+ }
+ }
+ else
+ {
+ FT_UInt len = ( width * bpp + 7 ) >> 3;
+
+ unsigned char* in = bitmap->buffer;
+ unsigned char* out = buffer;
+
+ unsigned char* limit = bitmap->buffer + pitch * bitmap->rows;
+ unsigned int delta = new_pitch - len;
+
+
+ while ( in < limit )
+ {
+ FT_MEM_COPY( out, in, len );
+ in += pitch;
+ out += len;
+
+ FT_MEM_ZERO( out, delta );
+ out += delta;
+ }
+
+ FT_MEM_ZERO( out, new_pitch * ypixels );
+ }
+
+ FT_FREE( bitmap->buffer );
+ bitmap->buffer = buffer;
+
+ /* set pitch only, width and height are left untouched */
+ if ( bitmap->pitch < 0 )
+ bitmap->pitch = -(int)new_pitch;
+ else
+ bitmap->pitch = (int)new_pitch;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Embolden( FT_Library library,
+ FT_Bitmap* bitmap,
+ FT_Pos xStrength,
+ FT_Pos yStrength )
+ {
+ FT_Error error;
+ unsigned char* p;
+ FT_Int i, x, pitch;
+ FT_UInt y;
+ FT_Int xstr, ystr;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !bitmap || !bitmap->buffer )
+ return FT_THROW( Invalid_Argument );
+
+ if ( ( ( FT_PIX_ROUND( xStrength ) >> 6 ) > FT_INT_MAX ) ||
+ ( ( FT_PIX_ROUND( yStrength ) >> 6 ) > FT_INT_MAX ) )
+ return FT_THROW( Invalid_Argument );
+
+ xstr = (FT_Int)FT_PIX_ROUND( xStrength ) >> 6;
+ ystr = (FT_Int)FT_PIX_ROUND( yStrength ) >> 6;
+
+ if ( xstr == 0 && ystr == 0 )
+ return FT_Err_Ok;
+ else if ( xstr < 0 || ystr < 0 )
+ return FT_THROW( Invalid_Argument );
+
+ switch ( bitmap->pixel_mode )
+ {
+ case FT_PIXEL_MODE_GRAY2:
+ case FT_PIXEL_MODE_GRAY4:
+ {
+ FT_Bitmap tmp;
+
+
+ /* convert to 8bpp */
+ FT_Bitmap_Init( &tmp );
+ error = FT_Bitmap_Convert( library, bitmap, &tmp, 1 );
+ if ( error )
+ return error;
+
+ FT_Bitmap_Done( library, bitmap );
+ *bitmap = tmp;
+ }
+ break;
+
+ case FT_PIXEL_MODE_MONO:
+ if ( xstr > 8 )
+ xstr = 8;
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ xstr *= 3;
+ break;
+
+ case FT_PIXEL_MODE_LCD_V:
+ ystr *= 3;
+ break;
+
+ case FT_PIXEL_MODE_BGRA:
+ /* We don't embolden color glyphs. */
+ return FT_Err_Ok;
+ }
+
+ error = ft_bitmap_assure_buffer( library->memory, bitmap,
+ (FT_UInt)xstr, (FT_UInt)ystr );
+ if ( error )
+ return error;
+
+ /* take care of bitmap flow */
+ pitch = bitmap->pitch;
+ if ( pitch > 0 )
+ p = bitmap->buffer + pitch * ystr;
+ else
+ {
+ pitch = -pitch;
+ p = bitmap->buffer + (FT_UInt)pitch * ( bitmap->rows - 1 );
+ }
+
+ /* for each row */
+ for ( y = 0; y < bitmap->rows; y++ )
+ {
+ /*
+ * Horizontally:
+ *
+ * From the last pixel on, make each pixel or'ed with the
+ * `xstr' pixels before it.
+ */
+ for ( x = pitch - 1; x >= 0; x-- )
+ {
+ unsigned char tmp;
+
+
+ tmp = p[x];
+ for ( i = 1; i <= xstr; i++ )
+ {
+ if ( bitmap->pixel_mode == FT_PIXEL_MODE_MONO )
+ {
+ p[x] |= tmp >> i;
+
+ /* the maximum value of 8 for `xstr' comes from here */
+ if ( x > 0 )
+ p[x] |= p[x - 1] << ( 8 - i );
+
+#if 0
+ if ( p[x] == 0xFF )
+ break;
+#endif
+ }
+ else
+ {
+ if ( x - i >= 0 )
+ {
+ if ( p[x] + p[x - i] > bitmap->num_grays - 1 )
+ {
+ p[x] = (unsigned char)( bitmap->num_grays - 1 );
+ break;
+ }
+ else
+ {
+ p[x] = (unsigned char)( p[x] + p[x - i] );
+ if ( p[x] == bitmap->num_grays - 1 )
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ /*
+ * Vertically:
+ *
+ * Make the above `ystr' rows or'ed with it.
+ */
+ for ( x = 1; x <= ystr; x++ )
+ {
+ unsigned char* q;
+
+
+ q = p - bitmap->pitch * x;
+ for ( i = 0; i < pitch; i++ )
+ q[i] |= p[i];
+ }
+
+ p += bitmap->pitch;
+ }
+
+ bitmap->width += (FT_UInt)xstr;
+ bitmap->rows += (FT_UInt)ystr;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Byte
+ ft_gray_for_premultiplied_srgb_bgra( const FT_Byte* bgra )
+ {
+ FT_UInt a = bgra[3];
+ FT_UInt l;
+
+
+ /* Short-circuit transparent color to avoid division by zero. */
+ if ( !a )
+ return 0;
+
+ /*
+ * Luminosity for sRGB is defined using ~0.2126,0.7152,0.0722
+ * coefficients for RGB channels *on the linear colors*.
+ * A gamma of 2.2 is fair to assume. And then, we need to
+ * undo the premultiplication too.
+ *
+ * http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html#SideNotes
+ *
+ * We do the computation with integers only, applying a gamma of 2.0.
+ * We guarantee 32-bit arithmetic to avoid overflow but the resulting
+ * luminosity fits into 16 bits.
+ *
+ */
+
+ l = ( 4731UL /* 0.072186 * 65536 */ * bgra[0] * bgra[0] +
+ 46868UL /* 0.715158 * 65536 */ * bgra[1] * bgra[1] +
+ 13937UL /* 0.212656 * 65536 */ * bgra[2] * bgra[2] ) >> 16;
+
+ /*
+ * Final transparency can be determined as follows.
+ *
+ * - If alpha is zero, we want 0.
+ * - If alpha is zero and luminosity is zero, we want 255.
+ * - If alpha is zero and luminosity is one, we want 0.
+ *
+ * So the formula is a * (1 - l) = a - l * a.
+ *
+ * We still need to undo premultiplication by dividing l by a*a.
+ *
+ */
+
+ return (FT_Byte)( a - l / a );
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Convert( FT_Library library,
+ const FT_Bitmap *source,
+ FT_Bitmap *target,
+ FT_Int alignment )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory;
+
+ FT_Byte* s;
+ FT_Byte* t;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !source || !target )
+ return FT_THROW( Invalid_Argument );
+
+ memory = library->memory;
+
+ switch ( source->pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ case FT_PIXEL_MODE_GRAY:
+ case FT_PIXEL_MODE_GRAY2:
+ case FT_PIXEL_MODE_GRAY4:
+ case FT_PIXEL_MODE_LCD:
+ case FT_PIXEL_MODE_LCD_V:
+ case FT_PIXEL_MODE_BGRA:
+ {
+ FT_Int width = (FT_Int)source->width;
+ FT_Int neg = ( target->pitch == 0 && source->pitch < 0 ) ||
+ target->pitch < 0;
+
+
+ FT_Bitmap_Done( library, target );
+
+ target->pixel_mode = FT_PIXEL_MODE_GRAY;
+ target->rows = source->rows;
+ target->width = source->width;
+
+ if ( alignment )
+ {
+ FT_Int rem = width % alignment;
+
+
+ if ( rem )
+ width = alignment > 0 ? width - rem + alignment
+ : width - rem - alignment;
+ }
+
+ if ( FT_QALLOC_MULT( target->buffer, target->rows, width ) )
+ return error;
+
+ target->pitch = neg ? -width : width;
+ }
+ break;
+
+ default:
+ error = FT_THROW( Invalid_Argument );
+ }
+
+ s = source->buffer;
+ t = target->buffer;
+
+ /* take care of bitmap flow */
+ if ( source->pitch < 0 )
+ s -= source->pitch * (FT_Int)( source->rows - 1 );
+ if ( target->pitch < 0 )
+ t -= target->pitch * (FT_Int)( target->rows - 1 );
+
+ switch ( source->pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ {
+ FT_UInt i;
+
+
+ target->num_grays = 2;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_Byte* ss = s;
+ FT_Byte* tt = t;
+ FT_UInt j;
+
+
+ /* get the full bytes */
+ for ( j = source->width >> 3; j > 0; j-- )
+ {
+ FT_Int val = ss[0]; /* avoid a byte->int cast on each line */
+
+
+ tt[0] = (FT_Byte)( ( val & 0x80 ) >> 7 );
+ tt[1] = (FT_Byte)( ( val & 0x40 ) >> 6 );
+ tt[2] = (FT_Byte)( ( val & 0x20 ) >> 5 );
+ tt[3] = (FT_Byte)( ( val & 0x10 ) >> 4 );
+ tt[4] = (FT_Byte)( ( val & 0x08 ) >> 3 );
+ tt[5] = (FT_Byte)( ( val & 0x04 ) >> 2 );
+ tt[6] = (FT_Byte)( ( val & 0x02 ) >> 1 );
+ tt[7] = (FT_Byte)( val & 0x01 );
+
+ tt += 8;
+ ss += 1;
+ }
+
+ /* get remaining pixels (if any) */
+ j = source->width & 7;
+ if ( j > 0 )
+ {
+ FT_Int val = *ss;
+
+
+ for ( ; j > 0; j-- )
+ {
+ tt[0] = (FT_Byte)( ( val & 0x80 ) >> 7);
+ val <<= 1;
+ tt += 1;
+ }
+ }
+
+ s += source->pitch;
+ t += target->pitch;
+ }
+ }
+ break;
+
+
+ case FT_PIXEL_MODE_GRAY:
+ case FT_PIXEL_MODE_LCD:
+ case FT_PIXEL_MODE_LCD_V:
+ {
+ FT_UInt width = source->width;
+ FT_UInt i;
+
+
+ target->num_grays = 256;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_ARRAY_COPY( t, s, width );
+
+ s += source->pitch;
+ t += target->pitch;
+ }
+ }
+ break;
+
+
+ case FT_PIXEL_MODE_GRAY2:
+ {
+ FT_UInt i;
+
+
+ target->num_grays = 4;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_Byte* ss = s;
+ FT_Byte* tt = t;
+ FT_UInt j;
+
+
+ /* get the full bytes */
+ for ( j = source->width >> 2; j > 0; j-- )
+ {
+ FT_Int val = ss[0];
+
+
+ tt[0] = (FT_Byte)( ( val & 0xC0 ) >> 6 );
+ tt[1] = (FT_Byte)( ( val & 0x30 ) >> 4 );
+ tt[2] = (FT_Byte)( ( val & 0x0C ) >> 2 );
+ tt[3] = (FT_Byte)( ( val & 0x03 ) );
+
+ ss += 1;
+ tt += 4;
+ }
+
+ j = source->width & 3;
+ if ( j > 0 )
+ {
+ FT_Int val = ss[0];
+
+
+ for ( ; j > 0; j-- )
+ {
+ tt[0] = (FT_Byte)( ( val & 0xC0 ) >> 6 );
+ val <<= 2;
+ tt += 1;
+ }
+ }
+
+ s += source->pitch;
+ t += target->pitch;
+ }
+ }
+ break;
+
+
+ case FT_PIXEL_MODE_GRAY4:
+ {
+ FT_UInt i;
+
+
+ target->num_grays = 16;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_Byte* ss = s;
+ FT_Byte* tt = t;
+ FT_UInt j;
+
+
+ /* get the full bytes */
+ for ( j = source->width >> 1; j > 0; j-- )
+ {
+ FT_Int val = ss[0];
+
+
+ tt[0] = (FT_Byte)( ( val & 0xF0 ) >> 4 );
+ tt[1] = (FT_Byte)( ( val & 0x0F ) );
+
+ ss += 1;
+ tt += 2;
+ }
+
+ if ( source->width & 1 )
+ tt[0] = (FT_Byte)( ( ss[0] & 0xF0 ) >> 4 );
+
+ s += source->pitch;
+ t += target->pitch;
+ }
+ }
+ break;
+
+
+ case FT_PIXEL_MODE_BGRA:
+ {
+ FT_UInt i;
+
+
+ target->num_grays = 256;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_Byte* ss = s;
+ FT_Byte* tt = t;
+ FT_UInt j;
+
+
+ for ( j = source->width; j > 0; j-- )
+ {
+ tt[0] = ft_gray_for_premultiplied_srgb_bgra( ss );
+
+ ss += 4;
+ tt += 1;
+ }
+
+ s += source->pitch;
+ t += target->pitch;
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Blend( FT_Library library,
+ const FT_Bitmap* source_,
+ const FT_Vector source_offset_,
+ FT_Bitmap* target,
+ FT_Vector *atarget_offset,
+ FT_Color color )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory;
+
+ FT_Bitmap source_bitmap;
+ const FT_Bitmap* source;
+
+ FT_Vector source_offset;
+ FT_Vector target_offset;
+
+ FT_Bool free_source_bitmap = 0;
+ FT_Bool free_target_bitmap_on_error = 0;
+
+ FT_Pos source_llx, source_lly, source_urx, source_ury;
+ FT_Pos target_llx, target_lly, target_urx, target_ury;
+ FT_Pos final_llx, final_lly, final_urx, final_ury;
+
+ unsigned int final_rows, final_width;
+ long x, y;
+
+
+ if ( !library || !target || !source_ || !atarget_offset )
+ return FT_THROW( Invalid_Argument );
+
+ memory = library->memory;
+
+ if ( !( target->pixel_mode == FT_PIXEL_MODE_NONE ||
+ ( target->pixel_mode == FT_PIXEL_MODE_BGRA &&
+ target->buffer ) ) )
+ return FT_THROW( Invalid_Argument );
+
+ if ( source_->pixel_mode == FT_PIXEL_MODE_NONE )
+ return FT_Err_Ok; /* nothing to do */
+
+ /* pitches must have the same sign */
+ if ( target->pixel_mode == FT_PIXEL_MODE_BGRA &&
+ ( source_->pitch ^ target->pitch ) < 0 )
+ return FT_THROW( Invalid_Argument );
+
+ if ( !( source_->width && source_->rows ) )
+ return FT_Err_Ok; /* nothing to do */
+
+ /* assure integer pixel offsets */
+ source_offset.x = FT_PIX_FLOOR( source_offset_.x );
+ source_offset.y = FT_PIX_FLOOR( source_offset_.y );
+ target_offset.x = FT_PIX_FLOOR( atarget_offset->x );
+ target_offset.y = FT_PIX_FLOOR( atarget_offset->y );
+
+ /* get source bitmap dimensions */
+ source_llx = source_offset.x;
+ if ( FT_LONG_MIN + (FT_Pos)( source_->rows << 6 ) + 64 > source_offset.y )
+ {
+ FT_TRACE5((
+ "FT_Bitmap_Blend: y coordinate overflow in source bitmap\n" ));
+ return FT_THROW( Invalid_Argument );
+ }
+ source_lly = source_offset.y - ( source_->rows << 6 );
+
+ if ( FT_LONG_MAX - (FT_Pos)( source_->width << 6 ) - 64 < source_llx )
+ {
+ FT_TRACE5((
+ "FT_Bitmap_Blend: x coordinate overflow in source bitmap\n" ));
+ return FT_THROW( Invalid_Argument );
+ }
+ source_urx = source_llx + ( source_->width << 6 );
+ source_ury = source_offset.y;
+
+ /* get target bitmap dimensions */
+ if ( target->width && target->rows )
+ {
+ target_llx = target_offset.x;
+ if ( FT_LONG_MIN + (FT_Pos)( target->rows << 6 ) > target_offset.y )
+ {
+ FT_TRACE5((
+ "FT_Bitmap_Blend: y coordinate overflow in target bitmap\n" ));
+ return FT_THROW( Invalid_Argument );
+ }
+ target_lly = target_offset.y - ( target->rows << 6 );
+
+ if ( FT_LONG_MAX - (FT_Pos)( target->width << 6 ) < target_llx )
+ {
+ FT_TRACE5((
+ "FT_Bitmap_Blend: x coordinate overflow in target bitmap\n" ));
+ return FT_THROW( Invalid_Argument );
+ }
+ target_urx = target_llx + ( target->width << 6 );
+ target_ury = target_offset.y;
+ }
+ else
+ {
+ target_llx = FT_LONG_MAX;
+ target_lly = FT_LONG_MAX;
+ target_urx = FT_LONG_MIN;
+ target_ury = FT_LONG_MIN;
+ }
+
+ /* compute final bitmap dimensions */
+ final_llx = FT_MIN( source_llx, target_llx );
+ final_lly = FT_MIN( source_lly, target_lly );
+ final_urx = FT_MAX( source_urx, target_urx );
+ final_ury = FT_MAX( source_ury, target_ury );
+
+ final_width = ( final_urx - final_llx ) >> 6;
+ final_rows = ( final_ury - final_lly ) >> 6;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_TRACE5(( "FT_Bitmap_Blend:\n" ));
+ FT_TRACE5(( " source bitmap: (%ld, %ld) -- (%ld, %ld); %d x %d\n",
+ source_llx / 64, source_lly / 64,
+ source_urx / 64, source_ury / 64,
+ source_->width, source_->rows ));
+
+ if ( target->width && target->rows )
+ FT_TRACE5(( " target bitmap: (%ld, %ld) -- (%ld, %ld); %d x %d\n",
+ target_llx / 64, target_lly / 64,
+ target_urx / 64, target_ury / 64,
+ target->width, target->rows ));
+ else
+ FT_TRACE5(( " target bitmap: empty\n" ));
+
+ if ( final_width && final_rows )
+ FT_TRACE5(( " final bitmap: (%ld, %ld) -- (%ld, %ld); %d x %d\n",
+ final_llx / 64, final_lly / 64,
+ final_urx / 64, final_ury / 64,
+ final_width, final_rows ));
+ else
+ FT_TRACE5(( " final bitmap: empty\n" ));
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ if ( !( final_width && final_rows ) )
+ return FT_Err_Ok; /* nothing to do */
+
+ /* for blending, set offset vector of final bitmap */
+ /* temporarily to (0,0) */
+ source_llx -= final_llx;
+ source_lly -= final_lly;
+
+ if ( target->width && target->rows )
+ {
+ target_llx -= final_llx;
+ target_lly -= final_lly;
+ }
+
+ /* set up target bitmap */
+ if ( target->pixel_mode == FT_PIXEL_MODE_NONE )
+ {
+ /* create new empty bitmap */
+ target->width = final_width;
+ target->rows = final_rows;
+ target->pixel_mode = FT_PIXEL_MODE_BGRA;
+ target->pitch = (int)final_width * 4;
+ target->num_grays = 256;
+
+ if ( FT_LONG_MAX / target->pitch < (int)target->rows )
+ {
+ FT_TRACE5(( "FT_Blend_Bitmap: target bitmap too large (%d x %d)\n",
+ final_width, final_rows ));
+ return FT_THROW( Invalid_Argument );
+ }
+
+ if ( FT_ALLOC( target->buffer, target->pitch * (int)target->rows ) )
+ return error;
+
+ free_target_bitmap_on_error = 1;
+ }
+ else if ( target->width != final_width ||
+ target->rows != final_rows )
+ {
+ /* adjust old bitmap to enlarged size */
+ int pitch, new_pitch;
+
+ unsigned char* buffer = NULL;
+
+
+ pitch = target->pitch;
+
+ if ( pitch < 0 )
+ pitch = -pitch;
+
+ new_pitch = (int)final_width * 4;
+
+ if ( FT_LONG_MAX / new_pitch < (int)final_rows )
+ {
+ FT_TRACE5(( "FT_Blend_Bitmap: target bitmap too large (%d x %d)\n",
+ final_width, final_rows ));
+ return FT_THROW( Invalid_Argument );
+ }
+
+ /* TODO: provide an in-buffer solution for large bitmaps */
+ /* to avoid allocation of a new buffer */
+ if ( FT_ALLOC( buffer, new_pitch * (int)final_rows ) )
+ goto Error;
+
+ /* copy data to new buffer */
+ x = target_llx >> 6;
+ y = target_lly >> 6;
+
+ /* the bitmap flow is from top to bottom, */
+ /* but y is measured from bottom to top */
+ if ( target->pitch < 0 )
+ {
+ /* XXX */
+ }
+ else
+ {
+ unsigned char* p =
+ target->buffer;
+ unsigned char* q =
+ buffer +
+ ( final_rows - y - target->rows ) * new_pitch +
+ x * 4;
+ unsigned char* limit_p =
+ p + pitch * (int)target->rows;
+
+
+ while ( p < limit_p )
+ {
+ FT_MEM_COPY( q, p, pitch );
+
+ p += pitch;
+ q += new_pitch;
+ }
+ }
+
+ FT_FREE( target->buffer );
+
+ target->width = final_width;
+ target->rows = final_rows;
+
+ if ( target->pitch < 0 )
+ target->pitch = -new_pitch;
+ else
+ target->pitch = new_pitch;
+
+ target->buffer = buffer;
+ }
+
+ /* adjust source bitmap if necessary */
+ if ( source_->pixel_mode != FT_PIXEL_MODE_GRAY )
+ {
+ FT_Bitmap_Init( &source_bitmap );
+ error = FT_Bitmap_Convert( library, source_, &source_bitmap, 1 );
+ if ( error )
+ goto Error;
+
+ source = &source_bitmap;
+ free_source_bitmap = 1;
+ }
+ else
+ source = source_;
+
+ /* do blending; the code below returns pre-multiplied channels, */
+ /* similar to what FreeType gets from `CBDT' tables */
+ x = source_llx >> 6;
+ y = source_lly >> 6;
+
+ /* the bitmap flow is from top to bottom, */
+ /* but y is measured from bottom to top */
+ if ( target->pitch < 0 )
+ {
+ /* XXX */
+ }
+ else
+ {
+ unsigned char* p =
+ source->buffer;
+ unsigned char* q =
+ target->buffer +
+ ( target->rows - y - source->rows ) * target->pitch +
+ x * 4;
+ unsigned char* limit_p =
+ p + source->pitch * (int)source->rows;
+
+
+ while ( p < limit_p )
+ {
+ unsigned char* r = p;
+ unsigned char* s = q;
+ unsigned char* limit_r = r + source->width;
+
+
+ while ( r < limit_r )
+ {
+ int aa = *r++;
+ int fa = color.alpha * aa / 255;
+
+ int fb = color.blue * fa / 255;
+ int fg = color.green * fa / 255;
+ int fr = color.red * fa / 255;
+
+ int ba2 = 255 - fa;
+
+ int bb = s[0];
+ int bg = s[1];
+ int br = s[2];
+ int ba = s[3];
+
+
+ *s++ = (unsigned char)( bb * ba2 / 255 + fb );
+ *s++ = (unsigned char)( bg * ba2 / 255 + fg );
+ *s++ = (unsigned char)( br * ba2 / 255 + fr );
+ *s++ = (unsigned char)( ba * ba2 / 255 + fa );
+ }
+
+ p += source->pitch;
+ q += target->pitch;
+ }
+ }
+
+ atarget_offset->x = final_llx;
+ atarget_offset->y = final_lly + ( final_rows << 6 );
+
+ Error:
+ if ( error && free_target_bitmap_on_error )
+ FT_Bitmap_Done( library, target );
+
+ if ( free_source_bitmap )
+ FT_Bitmap_Done( library, &source_bitmap );
+
+ return error;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_GlyphSlot_Own_Bitmap( FT_GlyphSlot slot )
+ {
+ if ( slot && slot->format == FT_GLYPH_FORMAT_BITMAP &&
+ !( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) )
+ {
+ FT_Bitmap bitmap;
+ FT_Error error;
+
+
+ FT_Bitmap_Init( &bitmap );
+ error = FT_Bitmap_Copy( slot->library, &slot->bitmap, &bitmap );
+ if ( error )
+ return error;
+
+ slot->bitmap = bitmap;
+ slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Done( FT_Library library,
+ FT_Bitmap *bitmap )
+ {
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !bitmap )
+ return FT_THROW( Invalid_Argument );
+
+ memory = library->memory;
+
+ FT_FREE( bitmap->buffer );
+ *bitmap = null_bitmap;
+
+ return FT_Err_Ok;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftcalc.c b/modules/freetype2/src/base/ftcalc.c
new file mode 100644
index 0000000000..13e74f3353
--- /dev/null
+++ b/modules/freetype2/src/base/ftcalc.c
@@ -0,0 +1,1155 @@
+/****************************************************************************
+ *
+ * ftcalc.c
+ *
+ * Arithmetic computations (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.
+ *
+ */
+
+ /**************************************************************************
+ *
+ * Support for 1-complement arithmetic has been totally dropped in this
+ * release. You can still write your own code if you need it.
+ *
+ */
+
+ /**************************************************************************
+ *
+ * Implementing basic computation routines.
+ *
+ * FT_MulDiv(), FT_MulFix(), FT_DivFix(), FT_RoundFix(), FT_CeilFix(),
+ * and FT_FloorFix() are declared in freetype.h.
+ *
+ */
+
+
+#include <freetype/ftglyph.h>
+#include <freetype/fttrigon.h>
+#include <freetype/internal/ftcalc.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftobjs.h>
+
+
+#ifdef FT_MULFIX_ASSEMBLER
+#undef FT_MulFix
+#endif
+
+/* we need to emulate a 64-bit data type if a real one isn't available */
+
+#ifndef FT_INT64
+
+ typedef struct FT_Int64_
+ {
+ FT_UInt32 lo;
+ FT_UInt32 hi;
+
+ } FT_Int64;
+
+#endif /* !FT_INT64 */
+
+
+ /**************************************************************************
+ *
+ * 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 calc
+
+
+ /* transfer sign, leaving a positive number; */
+ /* we need an unsigned value to safely negate INT_MIN (or LONG_MIN) */
+#define FT_MOVE_SIGN( x, x_unsigned, s ) \
+ FT_BEGIN_STMNT \
+ if ( x < 0 ) \
+ { \
+ x_unsigned = 0U - (x_unsigned); \
+ s = -s; \
+ } \
+ FT_END_STMNT
+
+ /* The following three functions are available regardless of whether */
+ /* FT_INT64 is defined. */
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_RoundFix( FT_Fixed a )
+ {
+ return ( ADD_LONG( a, 0x8000L - ( a < 0 ) ) ) & ~0xFFFFL;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_CeilFix( FT_Fixed a )
+ {
+ return ( ADD_LONG( a, 0xFFFFL ) ) & ~0xFFFFL;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_FloorFix( FT_Fixed a )
+ {
+ return a & ~0xFFFFL;
+ }
+
+#ifndef FT_MSB
+
+ FT_BASE_DEF( FT_Int )
+ FT_MSB( FT_UInt32 z )
+ {
+ FT_Int shift = 0;
+
+
+ /* determine msb bit index in `shift' */
+ if ( z & 0xFFFF0000UL )
+ {
+ z >>= 16;
+ shift += 16;
+ }
+ if ( z & 0x0000FF00UL )
+ {
+ z >>= 8;
+ shift += 8;
+ }
+ if ( z & 0x000000F0UL )
+ {
+ z >>= 4;
+ shift += 4;
+ }
+ if ( z & 0x0000000CUL )
+ {
+ z >>= 2;
+ shift += 2;
+ }
+ if ( z & 0x00000002UL )
+ {
+ /* z >>= 1; */
+ shift += 1;
+ }
+
+ return shift;
+ }
+
+#endif /* !FT_MSB */
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Fixed )
+ FT_Hypot( FT_Fixed x,
+ FT_Fixed y )
+ {
+ FT_Vector v;
+
+
+ v.x = x;
+ v.y = y;
+
+ return FT_Vector_Length( &v );
+ }
+
+
+#ifdef FT_INT64
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_MulDiv( FT_Long a_,
+ FT_Long b_,
+ FT_Long c_ )
+ {
+ FT_Int s = 1;
+ FT_UInt64 a, b, c, d;
+ FT_Long d_;
+
+
+ a = (FT_UInt64)a_;
+ b = (FT_UInt64)b_;
+ c = (FT_UInt64)c_;
+
+ FT_MOVE_SIGN( a_, a, s );
+ FT_MOVE_SIGN( b_, b, s );
+ FT_MOVE_SIGN( c_, c, s );
+
+ d = c > 0 ? ( a * b + ( c >> 1 ) ) / c
+ : 0x7FFFFFFFUL;
+
+ d_ = (FT_Long)d;
+
+ return s < 0 ? NEG_LONG( d_ ) : d_;
+ }
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Long )
+ FT_MulDiv_No_Round( FT_Long a_,
+ FT_Long b_,
+ FT_Long c_ )
+ {
+ FT_Int s = 1;
+ FT_UInt64 a, b, c, d;
+ FT_Long d_;
+
+
+ a = (FT_UInt64)a_;
+ b = (FT_UInt64)b_;
+ c = (FT_UInt64)c_;
+
+ FT_MOVE_SIGN( a_, a, s );
+ FT_MOVE_SIGN( b_, b, s );
+ FT_MOVE_SIGN( c_, c, s );
+
+ d = c > 0 ? a * b / c
+ : 0x7FFFFFFFUL;
+
+ d_ = (FT_Long)d;
+
+ return s < 0 ? NEG_LONG( d_ ) : d_;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_MulFix( FT_Long a_,
+ FT_Long b_ )
+ {
+#ifdef FT_MULFIX_ASSEMBLER
+
+ return FT_MULFIX_ASSEMBLER( (FT_Int32)a_, (FT_Int32)b_ );
+
+#else
+
+ FT_Int64 ab = (FT_Int64)a_ * (FT_Int64)b_;
+
+ /* this requires arithmetic right shift of signed numbers */
+ return (FT_Long)( ( ab + 0x8000L - ( ab < 0 ) ) >> 16 );
+
+#endif /* FT_MULFIX_ASSEMBLER */
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_DivFix( FT_Long a_,
+ FT_Long b_ )
+ {
+ FT_Int s = 1;
+ FT_UInt64 a, b, q;
+ FT_Long q_;
+
+
+ a = (FT_UInt64)a_;
+ b = (FT_UInt64)b_;
+
+ FT_MOVE_SIGN( a_, a, s );
+ FT_MOVE_SIGN( b_, b, s );
+
+ q = b > 0 ? ( ( a << 16 ) + ( b >> 1 ) ) / b
+ : 0x7FFFFFFFUL;
+
+ q_ = (FT_Long)q;
+
+ return s < 0 ? NEG_LONG( q_ ) : q_;
+ }
+
+
+#else /* !FT_INT64 */
+
+
+ static void
+ ft_multo64( FT_UInt32 x,
+ FT_UInt32 y,
+ FT_Int64 *z )
+ {
+ FT_UInt32 lo1, hi1, lo2, hi2, lo, hi, i1, i2;
+
+
+ lo1 = x & 0x0000FFFFU; hi1 = x >> 16;
+ lo2 = y & 0x0000FFFFU; hi2 = y >> 16;
+
+ lo = lo1 * lo2;
+ i1 = lo1 * hi2;
+ i2 = lo2 * hi1;
+ hi = hi1 * hi2;
+
+ /* Check carry overflow of i1 + i2 */
+ i1 += i2;
+ hi += (FT_UInt32)( i1 < i2 ) << 16;
+
+ hi += i1 >> 16;
+ i1 = i1 << 16;
+
+ /* Check carry overflow of i1 + lo */
+ lo += i1;
+ hi += ( lo < i1 );
+
+ z->lo = lo;
+ z->hi = hi;
+ }
+
+
+ static FT_UInt32
+ ft_div64by32( FT_UInt32 hi,
+ FT_UInt32 lo,
+ FT_UInt32 y )
+ {
+ FT_UInt32 r, q;
+ FT_Int i;
+
+
+ if ( hi >= y )
+ return (FT_UInt32)0x7FFFFFFFL;
+
+ /* We shift as many bits as we can into the high register, perform */
+ /* 32-bit division with modulo there, then work through the remaining */
+ /* bits with long division. This optimization is especially noticeable */
+ /* for smaller dividends that barely use the high register. */
+
+ i = 31 - FT_MSB( hi );
+ r = ( hi << i ) | ( lo >> ( 32 - i ) ); lo <<= i; /* left 64-bit shift */
+ q = r / y;
+ r -= q * y; /* remainder */
+
+ i = 32 - i; /* bits remaining in low register */
+ do
+ {
+ q <<= 1;
+ r = ( r << 1 ) | ( lo >> 31 ); lo <<= 1;
+
+ if ( r >= y )
+ {
+ r -= y;
+ q |= 1;
+ }
+ } while ( --i );
+
+ return q;
+ }
+
+
+ static void
+ FT_Add64( FT_Int64* x,
+ FT_Int64* y,
+ FT_Int64 *z )
+ {
+ FT_UInt32 lo, hi;
+
+
+ lo = x->lo + y->lo;
+ hi = x->hi + y->hi + ( lo < x->lo );
+
+ z->lo = lo;
+ z->hi = hi;
+ }
+
+
+ /* The FT_MulDiv function has been optimized thanks to ideas from */
+ /* Graham Asher and Alexei Podtelezhnikov. The trick is to optimize */
+ /* a rather common case when everything fits within 32-bits. */
+ /* */
+ /* We compute 'a*b+c/2', then divide it by 'c' (all positive values). */
+ /* */
+ /* The product of two positive numbers never exceeds the square of */
+ /* its mean values. Therefore, we always avoid the overflow by */
+ /* imposing */
+ /* */
+ /* (a + b) / 2 <= sqrt(X - c/2) , */
+ /* */
+ /* where X = 2^32 - 1, the maximum unsigned 32-bit value, and using */
+ /* unsigned arithmetic. Now we replace `sqrt' with a linear function */
+ /* that is smaller or equal for all values of c in the interval */
+ /* [0;X/2]; it should be equal to sqrt(X) and sqrt(3X/4) at the */
+ /* endpoints. Substituting the linear solution and explicit numbers */
+ /* we get */
+ /* */
+ /* a + b <= 131071.99 - c / 122291.84 . */
+ /* */
+ /* In practice, we should use a faster and even stronger inequality */
+ /* */
+ /* a + b <= 131071 - (c >> 16) */
+ /* */
+ /* or, alternatively, */
+ /* */
+ /* a + b <= 129894 - (c >> 17) . */
+ /* */
+ /* FT_MulFix, on the other hand, is optimized for a small value of */
+ /* the first argument, when the second argument can be much larger. */
+ /* This can be achieved by scaling the second argument and the limit */
+ /* in the above inequalities. For example, */
+ /* */
+ /* a + (b >> 8) <= (131071 >> 4) */
+ /* */
+ /* covers the practical range of use. The actual test below is a bit */
+ /* tighter to avoid the border case overflows. */
+ /* */
+ /* In the case of FT_DivFix, the exact overflow check */
+ /* */
+ /* a << 16 <= X - c/2 */
+ /* */
+ /* is scaled down by 2^16 and we use */
+ /* */
+ /* a <= 65535 - (c >> 17) . */
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_MulDiv( FT_Long a_,
+ FT_Long b_,
+ FT_Long c_ )
+ {
+ FT_Int s = 1;
+ FT_UInt32 a, b, c;
+
+
+ /* XXX: this function does not allow 64-bit arguments */
+
+ a = (FT_UInt32)a_;
+ b = (FT_UInt32)b_;
+ c = (FT_UInt32)c_;
+
+ FT_MOVE_SIGN( a_, a, s );
+ FT_MOVE_SIGN( b_, b, s );
+ FT_MOVE_SIGN( c_, c, s );
+
+ if ( c == 0 )
+ a = 0x7FFFFFFFUL;
+
+ else if ( a + b <= 129894UL - ( c >> 17 ) )
+ a = ( a * b + ( c >> 1 ) ) / c;
+
+ else
+ {
+ FT_Int64 temp, temp2;
+
+
+ ft_multo64( a, b, &temp );
+
+ temp2.hi = 0;
+ temp2.lo = c >> 1;
+
+ FT_Add64( &temp, &temp2, &temp );
+
+ /* last attempt to ditch long division */
+ a = ( temp.hi == 0 ) ? temp.lo / c
+ : ft_div64by32( temp.hi, temp.lo, c );
+ }
+
+ a_ = (FT_Long)a;
+
+ return s < 0 ? NEG_LONG( a_ ) : a_;
+ }
+
+
+ FT_BASE_DEF( FT_Long )
+ FT_MulDiv_No_Round( FT_Long a_,
+ FT_Long b_,
+ FT_Long c_ )
+ {
+ FT_Int s = 1;
+ FT_UInt32 a, b, c;
+
+
+ /* XXX: this function does not allow 64-bit arguments */
+
+ a = (FT_UInt32)a_;
+ b = (FT_UInt32)b_;
+ c = (FT_UInt32)c_;
+
+ FT_MOVE_SIGN( a_, a, s );
+ FT_MOVE_SIGN( b_, b, s );
+ FT_MOVE_SIGN( c_, c, s );
+
+ if ( c == 0 )
+ a = 0x7FFFFFFFUL;
+
+ else if ( a + b <= 131071UL )
+ a = a * b / c;
+
+ else
+ {
+ FT_Int64 temp;
+
+
+ ft_multo64( a, b, &temp );
+
+ /* last attempt to ditch long division */
+ a = ( temp.hi == 0 ) ? temp.lo / c
+ : ft_div64by32( temp.hi, temp.lo, c );
+ }
+
+ a_ = (FT_Long)a;
+
+ return s < 0 ? NEG_LONG( a_ ) : a_;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_MulFix( FT_Long a_,
+ FT_Long b_ )
+ {
+#ifdef FT_MULFIX_ASSEMBLER
+
+ return FT_MULFIX_ASSEMBLER( a_, b_ );
+
+#elif 0
+
+ /*
+ * This code is nonportable. See comment below.
+ *
+ * However, on a platform where right-shift of a signed quantity fills
+ * the leftmost bits by copying the sign bit, it might be faster.
+ */
+
+ FT_Long sa, sb;
+ FT_UInt32 a, b;
+
+
+ /*
+ * This is a clever way of converting a signed number `a' into its
+ * absolute value (stored back into `a') and its sign. The sign is
+ * stored in `sa'; 0 means `a' was positive or zero, and -1 means `a'
+ * was negative. (Similarly for `b' and `sb').
+ *
+ * Unfortunately, it doesn't work (at least not portably).
+ *
+ * It makes the assumption that right-shift on a negative signed value
+ * fills the leftmost bits by copying the sign bit. This is wrong.
+ * According to K&R 2nd ed, section `A7.8 Shift Operators' on page 206,
+ * the result of right-shift of a negative signed value is
+ * implementation-defined. At least one implementation fills the
+ * leftmost bits with 0s (i.e., it is exactly the same as an unsigned
+ * right shift). This means that when `a' is negative, `sa' ends up
+ * with the value 1 rather than -1. After that, everything else goes
+ * wrong.
+ */
+ sa = ( a_ >> ( sizeof ( a_ ) * 8 - 1 ) );
+ a = ( a_ ^ sa ) - sa;
+ sb = ( b_ >> ( sizeof ( b_ ) * 8 - 1 ) );
+ b = ( b_ ^ sb ) - sb;
+
+ a = (FT_UInt32)a_;
+ b = (FT_UInt32)b_;
+
+ if ( a + ( b >> 8 ) <= 8190UL )
+ a = ( a * b + 0x8000U ) >> 16;
+ else
+ {
+ FT_UInt32 al = a & 0xFFFFUL;
+
+
+ a = ( a >> 16 ) * b + al * ( b >> 16 ) +
+ ( ( al * ( b & 0xFFFFUL ) + 0x8000UL ) >> 16 );
+ }
+
+ sa ^= sb;
+ a = ( a ^ sa ) - sa;
+
+ return (FT_Long)a;
+
+#else /* 0 */
+
+ FT_Int s = 1;
+ FT_UInt32 a, b;
+
+
+ /* XXX: this function does not allow 64-bit arguments */
+
+ a = (FT_UInt32)a_;
+ b = (FT_UInt32)b_;
+
+ FT_MOVE_SIGN( a_, a, s );
+ FT_MOVE_SIGN( b_, b, s );
+
+ if ( a + ( b >> 8 ) <= 8190UL )
+ a = ( a * b + 0x8000UL ) >> 16;
+ else
+ {
+ FT_UInt32 al = a & 0xFFFFUL;
+
+
+ a = ( a >> 16 ) * b + al * ( b >> 16 ) +
+ ( ( al * ( b & 0xFFFFUL ) + 0x8000UL ) >> 16 );
+ }
+
+ a_ = (FT_Long)a;
+
+ return s < 0 ? NEG_LONG( a_ ) : a_;
+
+#endif /* 0 */
+
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_DivFix( FT_Long a_,
+ FT_Long b_ )
+ {
+ FT_Int s = 1;
+ FT_UInt32 a, b, q;
+ FT_Long q_;
+
+
+ /* XXX: this function does not allow 64-bit arguments */
+
+ a = (FT_UInt32)a_;
+ b = (FT_UInt32)b_;
+
+ FT_MOVE_SIGN( a_, a, s );
+ FT_MOVE_SIGN( b_, b, s );
+
+ if ( b == 0 )
+ {
+ /* check for division by 0 */
+ q = 0x7FFFFFFFUL;
+ }
+ else if ( a <= 65535UL - ( b >> 17 ) )
+ {
+ /* compute result directly */
+ q = ( ( a << 16 ) + ( b >> 1 ) ) / b;
+ }
+ else
+ {
+ /* we need more bits; we have to do it by hand */
+ FT_Int64 temp, temp2;
+
+
+ temp.hi = a >> 16;
+ temp.lo = a << 16;
+ temp2.hi = 0;
+ temp2.lo = b >> 1;
+
+ FT_Add64( &temp, &temp2, &temp );
+ q = ft_div64by32( temp.hi, temp.lo, b );
+ }
+
+ q_ = (FT_Long)q;
+
+ return s < 0 ? NEG_LONG( q_ ) : q_;
+ }
+
+
+#endif /* !FT_INT64 */
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Matrix_Multiply( const FT_Matrix* a,
+ FT_Matrix *b )
+ {
+ FT_Fixed xx, xy, yx, yy;
+
+
+ if ( !a || !b )
+ return;
+
+ xx = ADD_LONG( FT_MulFix( a->xx, b->xx ),
+ FT_MulFix( a->xy, b->yx ) );
+ xy = ADD_LONG( FT_MulFix( a->xx, b->xy ),
+ FT_MulFix( a->xy, b->yy ) );
+ yx = ADD_LONG( FT_MulFix( a->yx, b->xx ),
+ FT_MulFix( a->yy, b->yx ) );
+ yy = ADD_LONG( FT_MulFix( a->yx, b->xy ),
+ FT_MulFix( a->yy, b->yy ) );
+
+ b->xx = xx;
+ b->xy = xy;
+ b->yx = yx;
+ b->yy = yy;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Matrix_Invert( FT_Matrix* matrix )
+ {
+ FT_Pos delta, xx, yy;
+
+
+ if ( !matrix )
+ return FT_THROW( Invalid_Argument );
+
+ /* compute discriminant */
+ delta = FT_MulFix( matrix->xx, matrix->yy ) -
+ FT_MulFix( matrix->xy, matrix->yx );
+
+ if ( !delta )
+ return FT_THROW( Invalid_Argument ); /* matrix can't be inverted */
+
+ matrix->xy = -FT_DivFix( matrix->xy, delta );
+ matrix->yx = -FT_DivFix( matrix->yx, delta );
+
+ xx = matrix->xx;
+ yy = matrix->yy;
+
+ matrix->xx = FT_DivFix( yy, delta );
+ matrix->yy = FT_DivFix( xx, delta );
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( void )
+ FT_Matrix_Multiply_Scaled( const FT_Matrix* a,
+ FT_Matrix *b,
+ FT_Long scaling )
+ {
+ FT_Fixed xx, xy, yx, yy;
+
+ FT_Long val = 0x10000L * scaling;
+
+
+ if ( !a || !b )
+ return;
+
+ xx = ADD_LONG( FT_MulDiv( a->xx, b->xx, val ),
+ FT_MulDiv( a->xy, b->yx, val ) );
+ xy = ADD_LONG( FT_MulDiv( a->xx, b->xy, val ),
+ FT_MulDiv( a->xy, b->yy, val ) );
+ yx = ADD_LONG( FT_MulDiv( a->yx, b->xx, val ),
+ FT_MulDiv( a->yy, b->yx, val ) );
+ yy = ADD_LONG( FT_MulDiv( a->yx, b->xy, val ),
+ FT_MulDiv( a->yy, b->yy, val ) );
+
+ b->xx = xx;
+ b->xy = xy;
+ b->yx = yx;
+ b->yy = yy;
+ }
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Bool )
+ FT_Matrix_Check( const FT_Matrix* matrix )
+ {
+ FT_Matrix m;
+ FT_Fixed val[4];
+ FT_Fixed nonzero_minval, maxval;
+ FT_Fixed temp1, temp2;
+ FT_UInt i;
+
+
+ if ( !matrix )
+ return 0;
+
+ val[0] = FT_ABS( matrix->xx );
+ val[1] = FT_ABS( matrix->xy );
+ val[2] = FT_ABS( matrix->yx );
+ val[3] = FT_ABS( matrix->yy );
+
+ /*
+ * To avoid overflow, we ensure that each value is not larger than
+ *
+ * int(sqrt(2^31 / 4)) = 23170 ;
+ *
+ * we also check that no value becomes zero if we have to scale.
+ */
+
+ maxval = 0;
+ nonzero_minval = FT_LONG_MAX;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ if ( val[i] > maxval )
+ maxval = val[i];
+ if ( val[i] && val[i] < nonzero_minval )
+ nonzero_minval = val[i];
+ }
+
+ /* we only handle 32bit values */
+ if ( maxval > 0x7FFFFFFFL )
+ return 0;
+
+ if ( maxval > 23170 )
+ {
+ FT_Fixed scale = FT_DivFix( maxval, 23170 );
+
+
+ if ( !FT_DivFix( nonzero_minval, scale ) )
+ return 0; /* value range too large */
+
+ m.xx = FT_DivFix( matrix->xx, scale );
+ m.xy = FT_DivFix( matrix->xy, scale );
+ m.yx = FT_DivFix( matrix->yx, scale );
+ m.yy = FT_DivFix( matrix->yy, scale );
+ }
+ else
+ m = *matrix;
+
+ temp1 = FT_ABS( m.xx * m.yy - m.xy * m.yx );
+ temp2 = m.xx * m.xx + m.xy * m.xy + m.yx * m.yx + m.yy * m.yy;
+
+ if ( temp1 == 0 ||
+ temp2 / temp1 > 50 )
+ return 0;
+
+ return 1;
+ }
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( void )
+ FT_Vector_Transform_Scaled( FT_Vector* vector,
+ const FT_Matrix* matrix,
+ FT_Long scaling )
+ {
+ FT_Pos xz, yz;
+
+ FT_Long val = 0x10000L * scaling;
+
+
+ if ( !vector || !matrix )
+ return;
+
+ xz = ADD_LONG( FT_MulDiv( vector->x, matrix->xx, val ),
+ FT_MulDiv( vector->y, matrix->xy, val ) );
+ yz = ADD_LONG( FT_MulDiv( vector->x, matrix->yx, val ),
+ FT_MulDiv( vector->y, matrix->yy, val ) );
+
+ vector->x = xz;
+ vector->y = yz;
+ }
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_UInt32 )
+ FT_Vector_NormLen( FT_Vector* vector )
+ {
+ FT_Int32 x_ = vector->x;
+ FT_Int32 y_ = vector->y;
+ FT_Int32 b, z;
+ FT_UInt32 x, y, u, v, l;
+ FT_Int sx = 1, sy = 1, shift;
+
+
+ x = (FT_UInt32)x_;
+ y = (FT_UInt32)y_;
+
+ FT_MOVE_SIGN( x_, x, sx );
+ FT_MOVE_SIGN( y_, y, sy );
+
+ /* trivial cases */
+ if ( x == 0 )
+ {
+ if ( y > 0 )
+ vector->y = sy * 0x10000;
+ return y;
+ }
+ else if ( y == 0 )
+ {
+ if ( x > 0 )
+ vector->x = sx * 0x10000;
+ return x;
+ }
+
+ /* Estimate length and prenormalize by shifting so that */
+ /* the new approximate length is between 2/3 and 4/3. */
+ /* The magic constant 0xAAAAAAAAUL (2/3 of 2^32) helps */
+ /* achieve this in 16.16 fixed-point representation. */
+ l = x > y ? x + ( y >> 1 )
+ : y + ( x >> 1 );
+
+ shift = 31 - FT_MSB( l );
+ shift -= 15 + ( l >= ( 0xAAAAAAAAUL >> shift ) );
+
+ if ( shift > 0 )
+ {
+ x <<= shift;
+ y <<= shift;
+
+ /* re-estimate length for tiny vectors */
+ l = x > y ? x + ( y >> 1 )
+ : y + ( x >> 1 );
+ }
+ else
+ {
+ x >>= -shift;
+ y >>= -shift;
+ l >>= -shift;
+ }
+
+ /* lower linear approximation for reciprocal length minus one */
+ b = 0x10000 - (FT_Int32)l;
+
+ x_ = (FT_Int32)x;
+ y_ = (FT_Int32)y;
+
+ /* Newton's iterations */
+ do
+ {
+ u = (FT_UInt32)( x_ + ( x_ * b >> 16 ) );
+ v = (FT_UInt32)( y_ + ( y_ * b >> 16 ) );
+
+ /* Normalized squared length in the parentheses approaches 2^32. */
+ /* On two's complement systems, converting to signed gives the */
+ /* difference with 2^32 even if the expression wraps around. */
+ z = -(FT_Int32)( u * u + v * v ) / 0x200;
+ z = z * ( ( 0x10000 + b ) >> 8 ) / 0x10000;
+
+ b += z;
+
+ } while ( z > 0 );
+
+ vector->x = sx < 0 ? -(FT_Pos)u : (FT_Pos)u;
+ vector->y = sy < 0 ? -(FT_Pos)v : (FT_Pos)v;
+
+ /* Conversion to signed helps to recover from likely wrap around */
+ /* in calculating the prenormalized length, because it gives the */
+ /* correct difference with 2^32 on two's complement systems. */
+ l = (FT_UInt32)( 0x10000 + (FT_Int32)( u * x + v * y ) / 0x10000 );
+ if ( shift > 0 )
+ l = ( l + ( 1 << ( shift - 1 ) ) ) >> shift;
+ else
+ l <<= -shift;
+
+ return l;
+ }
+
+
+#if 0
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Int32 )
+ FT_SqrtFixed( FT_Int32 x )
+ {
+ FT_UInt32 root, rem_hi, rem_lo, test_div;
+ FT_Int count;
+
+
+ root = 0;
+
+ if ( x > 0 )
+ {
+ rem_hi = 0;
+ rem_lo = (FT_UInt32)x;
+ count = 24;
+ do
+ {
+ rem_hi = ( rem_hi << 2 ) | ( rem_lo >> 30 );
+ rem_lo <<= 2;
+ root <<= 1;
+ test_div = ( root << 1 ) + 1;
+
+ if ( rem_hi >= test_div )
+ {
+ rem_hi -= test_div;
+ root += 1;
+ }
+ } while ( --count );
+ }
+
+ return (FT_Int32)root;
+ }
+
+#endif /* 0 */
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Int )
+ ft_corner_orientation( FT_Pos in_x,
+ FT_Pos in_y,
+ FT_Pos out_x,
+ FT_Pos out_y )
+ {
+ /* we silently ignore overflow errors since such large values */
+ /* lead to even more (harmless) rendering errors later on */
+
+#ifdef FT_INT64
+
+ FT_Int64 delta = SUB_INT64( MUL_INT64( in_x, out_y ),
+ MUL_INT64( in_y, out_x ) );
+
+
+ return ( delta > 0 ) - ( delta < 0 );
+
+#else
+
+ FT_Int result;
+
+
+ if ( ADD_LONG( FT_ABS( in_x ), FT_ABS( out_y ) ) <= 131071L &&
+ ADD_LONG( FT_ABS( in_y ), FT_ABS( out_x ) ) <= 131071L )
+ {
+ FT_Long z1 = MUL_LONG( in_x, out_y );
+ FT_Long z2 = MUL_LONG( in_y, out_x );
+
+
+ if ( z1 > z2 )
+ result = +1;
+ else if ( z1 < z2 )
+ result = -1;
+ else
+ result = 0;
+ }
+ else /* products might overflow 32 bits */
+ {
+ FT_Int64 z1, z2;
+
+
+ /* XXX: this function does not allow 64-bit arguments */
+ ft_multo64( (FT_UInt32)in_x, (FT_UInt32)out_y, &z1 );
+ ft_multo64( (FT_UInt32)in_y, (FT_UInt32)out_x, &z2 );
+
+ if ( z1.hi > z2.hi )
+ result = +1;
+ else if ( z1.hi < z2.hi )
+ result = -1;
+ else if ( z1.lo > z2.lo )
+ result = +1;
+ else if ( z1.lo < z2.lo )
+ result = -1;
+ else
+ result = 0;
+ }
+
+ /* XXX: only the sign of return value, +1/0/-1 must be used */
+ return result;
+
+#endif
+ }
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Int )
+ ft_corner_is_flat( FT_Pos in_x,
+ FT_Pos in_y,
+ FT_Pos out_x,
+ FT_Pos out_y )
+ {
+ FT_Pos ax = in_x + out_x;
+ FT_Pos ay = in_y + out_y;
+
+ FT_Pos d_in, d_out, d_hypot;
+
+
+ /* The idea of this function is to compare the length of the */
+ /* hypotenuse with the `in' and `out' length. The `corner' */
+ /* represented by `in' and `out' is flat if the hypotenuse's */
+ /* length isn't too large. */
+ /* */
+ /* This approach has the advantage that the angle between */
+ /* `in' and `out' is not checked. In case one of the two */
+ /* vectors is `dominant', this is, much larger than the */
+ /* other vector, we thus always have a flat corner. */
+ /* */
+ /* hypotenuse */
+ /* x---------------------------x */
+ /* \ / */
+ /* \ / */
+ /* in \ / out */
+ /* \ / */
+ /* o */
+ /* Point */
+
+ d_in = FT_HYPOT( in_x, in_y );
+ d_out = FT_HYPOT( out_x, out_y );
+ d_hypot = FT_HYPOT( ax, ay );
+
+ /* now do a simple length comparison: */
+ /* */
+ /* d_in + d_out < 17/16 d_hypot */
+
+ return ( d_in + d_out - d_hypot ) < ( d_hypot >> 4 );
+ }
+
+
+ FT_BASE_DEF( FT_Int32 )
+ FT_MulAddFix( FT_Fixed* s,
+ FT_Int32* f,
+ FT_UInt count )
+ {
+ FT_UInt i;
+ FT_Int64 temp;
+#ifndef FT_INT64
+ FT_Int64 halfUnit;
+#endif
+
+
+#ifdef FT_INT64
+ temp = 0;
+
+ for ( i = 0; i < count; ++i )
+ temp += (FT_Int64)s[i] * f[i];
+
+ return ( temp + 0x8000 ) >> 16;
+#else
+ temp.hi = 0;
+ temp.lo = 0;
+
+ for ( i = 0; i < count; ++i )
+ {
+ FT_Int64 multResult;
+
+ FT_Int sign = 1;
+ FT_UInt32 carry = 0;
+
+ FT_UInt32 scalar;
+ FT_UInt32 factor;
+
+
+ scalar = (FT_UInt32)s[i];
+ factor = (FT_UInt32)f[i];
+
+ FT_MOVE_SIGN( s[i], scalar, sign );
+ FT_MOVE_SIGN( f[i], factor, sign );
+
+ ft_multo64( scalar, factor, &multResult );
+
+ if ( sign < 0 )
+ {
+ /* Emulated `FT_Int64` negation. */
+ carry = ( multResult.lo == 0 );
+
+ multResult.lo = ~multResult.lo + 1;
+ multResult.hi = ~multResult.hi + carry;
+ }
+
+ FT_Add64( &temp, &multResult, &temp );
+ }
+
+ /* Round value. */
+ halfUnit.hi = 0;
+ halfUnit.lo = 0x8000;
+ FT_Add64( &temp, &halfUnit, &temp );
+
+ return (FT_Int32)( ( (FT_Int32)( temp.hi & 0xFFFF ) << 16 ) |
+ ( temp.lo >> 16 ) );
+
+#endif /* !FT_INT64 */
+
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftcid.c b/modules/freetype2/src/base/ftcid.c
new file mode 100644
index 0000000000..866cd23e91
--- /dev/null
+++ b/modules/freetype2/src/base/ftcid.c
@@ -0,0 +1,117 @@
+/****************************************************************************
+ *
+ * ftcid.c
+ *
+ * FreeType API for accessing CID font information.
+ *
+ * Copyright (C) 2007-2023 by
+ * Derek Clegg and Michael Toftdal.
+ *
+ * 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/ftcid.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/services/svcid.h>
+
+
+ /* documentation is in ftcid.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_CID_Registry_Ordering_Supplement( FT_Face face,
+ const char* *registry,
+ const char* *ordering,
+ FT_Int *supplement)
+ {
+ FT_Error error;
+ const char* r = NULL;
+ const char* o = NULL;
+ FT_Int s = 0;
+
+
+ error = FT_ERR( Invalid_Argument );
+
+ if ( face )
+ {
+ FT_Service_CID service;
+
+
+ FT_FACE_FIND_SERVICE( face, service, CID );
+
+ if ( service && service->get_ros )
+ error = service->get_ros( face, &r, &o, &s );
+ }
+
+ if ( registry )
+ *registry = r;
+
+ if ( ordering )
+ *ordering = o;
+
+ if ( supplement )
+ *supplement = s;
+
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_CID_Is_Internally_CID_Keyed( FT_Face face,
+ FT_Bool *is_cid )
+ {
+ FT_Error error = FT_ERR( Invalid_Argument );
+ FT_Bool ic = 0;
+
+
+ if ( face )
+ {
+ FT_Service_CID service;
+
+
+ FT_FACE_FIND_SERVICE( face, service, CID );
+
+ if ( service && service->get_is_cid )
+ error = service->get_is_cid( face, &ic);
+ }
+
+ if ( is_cid )
+ *is_cid = ic;
+
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_CID_From_Glyph_Index( FT_Face face,
+ FT_UInt glyph_index,
+ FT_UInt *cid )
+ {
+ FT_Error error = FT_ERR( Invalid_Argument );
+ FT_UInt c = 0;
+
+
+ if ( face )
+ {
+ FT_Service_CID service;
+
+
+ FT_FACE_FIND_SERVICE( face, service, CID );
+
+ if ( service && service->get_cid_from_glyph_index )
+ error = service->get_cid_from_glyph_index( face, glyph_index, &c);
+ }
+
+ if ( cid )
+ *cid = c;
+
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftcolor.c b/modules/freetype2/src/base/ftcolor.c
new file mode 100644
index 0000000000..bcd6e893d4
--- /dev/null
+++ b/modules/freetype2/src/base/ftcolor.c
@@ -0,0 +1,156 @@
+/****************************************************************************
+ *
+ * ftcolor.c
+ *
+ * FreeType's glyph color management (body).
+ *
+ * Copyright (C) 2018-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/tttypes.h>
+#include <freetype/ftcolor.h>
+
+
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+
+ static
+ const FT_Palette_Data null_palette_data = { 0, NULL, NULL, 0, NULL };
+
+
+ /* documentation is in ftcolor.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Palette_Data_Get( FT_Face face,
+ FT_Palette_Data *apalette_data )
+ {
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+ if ( !apalette_data)
+ return FT_THROW( Invalid_Argument );
+
+ if ( FT_IS_SFNT( face ) )
+ *apalette_data = ( (TT_Face)face )->palette_data;
+ else
+ *apalette_data = null_palette_data;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftcolor.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Palette_Select( FT_Face face,
+ FT_UShort palette_index,
+ FT_Color* *apalette )
+ {
+ FT_Error error;
+
+ TT_Face ttface;
+ SFNT_Service sfnt;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !FT_IS_SFNT( face ) )
+ {
+ if ( apalette )
+ *apalette = NULL;
+
+ return FT_Err_Ok;
+ }
+
+ ttface = (TT_Face)face;
+ sfnt = (SFNT_Service)ttface->sfnt;
+
+ error = sfnt->set_palette( ttface, palette_index );
+ if ( error )
+ return error;
+
+ ttface->palette_index = palette_index;
+
+ if ( apalette )
+ *apalette = ttface->palette;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftcolor.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Palette_Set_Foreground_Color( FT_Face face,
+ FT_Color foreground_color )
+ {
+ TT_Face ttface;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !FT_IS_SFNT( face ) )
+ return FT_Err_Ok;
+
+ ttface = (TT_Face)face;
+
+ ttface->foreground_color = foreground_color;
+ ttface->have_foreground_color = 1;
+
+ return FT_Err_Ok;
+ }
+
+#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Palette_Data_Get( FT_Face face,
+ FT_Palette_Data *apalette_data )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( apalette_data );
+
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Palette_Select( FT_Face face,
+ FT_UShort palette_index,
+ FT_Color* *apalette )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( palette_index );
+ FT_UNUSED( apalette );
+
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Palette_Set_Foreground_Color( FT_Face face,
+ FT_Color foreground_color )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( foreground_color );
+
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftdbgmem.c b/modules/freetype2/src/base/ftdbgmem.c
new file mode 100644
index 0000000000..6730c4c8d3
--- /dev/null
+++ b/modules/freetype2/src/base/ftdbgmem.c
@@ -0,0 +1,971 @@
+/****************************************************************************
+ *
+ * ftdbgmem.c
+ *
+ * Memory debugger (body).
+ *
+ * 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.
+ *
+ */
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftmemory.h>
+#include <freetype/ftsystem.h>
+#include <freetype/fterrors.h>
+#include <freetype/fttypes.h>
+
+
+#ifdef FT_DEBUG_MEMORY
+
+#define KEEPALIVE /* `Keep alive' means that freed blocks aren't released
+ * to the heap. This is useful to detect double-frees
+ * or weird heap corruption, but it uses large amounts of
+ * memory, however.
+ */
+
+#include FT_CONFIG_STANDARD_LIBRARY_H
+
+ FT_BASE_DEF( const char* ) ft_debug_file_ = NULL;
+ FT_BASE_DEF( long ) ft_debug_lineno_ = 0;
+
+ extern void
+ FT_DumpMemory( FT_Memory memory );
+
+
+ typedef struct FT_MemSourceRec_* FT_MemSource;
+ typedef struct FT_MemNodeRec_* FT_MemNode;
+ typedef struct FT_MemTableRec_* FT_MemTable;
+
+
+#define FT_MEM_VAL( addr ) ( (FT_PtrDist)(FT_Pointer)( addr ) )
+
+ /*
+ * This structure holds statistics for a single allocation/release
+ * site. This is useful to know where memory operations happen the
+ * most.
+ */
+ typedef struct FT_MemSourceRec_
+ {
+ const char* file_name;
+ long line_no;
+
+ FT_Long cur_blocks; /* current number of allocated blocks */
+ FT_Long max_blocks; /* max. number of allocated blocks */
+ FT_Long all_blocks; /* total number of blocks allocated */
+
+ FT_Long cur_size; /* current cumulative allocated size */
+ FT_Long max_size; /* maximum cumulative allocated size */
+ FT_Long all_size; /* total cumulative allocated size */
+
+ FT_Long cur_max; /* current maximum allocated size */
+
+ FT_UInt32 hash;
+ FT_MemSource link;
+
+ } FT_MemSourceRec;
+
+
+ /*
+ * We don't need a resizable array for the memory sources because
+ * their number is pretty limited within FreeType.
+ */
+#define FT_MEM_SOURCE_BUCKETS 128
+
+ /*
+ * This structure holds information related to a single allocated
+ * memory block. If KEEPALIVE is defined, blocks that are freed by
+ * FreeType are never released to the system. Instead, their `size'
+ * field is set to `-size'. This is mainly useful to detect double
+ * frees, at the price of a large memory footprint during execution.
+ */
+ typedef struct FT_MemNodeRec_
+ {
+ FT_Byte* address;
+ FT_Long size; /* < 0 if the block was freed */
+
+ FT_MemSource source;
+
+#ifdef KEEPALIVE
+ const char* free_file_name;
+ FT_Long free_line_no;
+#endif
+
+ FT_MemNode link;
+
+ } FT_MemNodeRec;
+
+
+ /*
+ * The global structure, containing compound statistics and all hash
+ * tables.
+ */
+ typedef struct FT_MemTableRec_
+ {
+ FT_Long size;
+ FT_Long nodes;
+ FT_MemNode* buckets;
+
+ FT_Long alloc_total;
+ FT_Long alloc_current;
+ FT_Long alloc_max;
+ FT_Long alloc_count;
+
+ FT_Bool bound_total;
+ FT_Long alloc_total_max;
+
+ FT_Bool bound_count;
+ FT_Long alloc_count_max;
+
+ FT_MemSource sources[FT_MEM_SOURCE_BUCKETS];
+
+ FT_Bool keep_alive;
+
+ FT_Memory memory;
+ FT_Pointer memory_user;
+ FT_Alloc_Func alloc;
+ FT_Free_Func free;
+ FT_Realloc_Func realloc;
+
+ } FT_MemTableRec;
+
+
+#define FT_MEM_SIZE_MIN 7
+#define FT_MEM_SIZE_MAX 13845163
+
+#define FT_FILENAME( x ) ( (x) ? (x) : "unknown file" )
+
+
+ /*
+ * Prime numbers are ugly to handle. It would be better to implement
+ * L-Hashing, which is 10% faster and doesn't require divisions.
+ */
+ static const FT_Int ft_mem_primes[] =
+ {
+ 7,
+ 11,
+ 19,
+ 37,
+ 73,
+ 109,
+ 163,
+ 251,
+ 367,
+ 557,
+ 823,
+ 1237,
+ 1861,
+ 2777,
+ 4177,
+ 6247,
+ 9371,
+ 14057,
+ 21089,
+ 31627,
+ 47431,
+ 71143,
+ 106721,
+ 160073,
+ 240101,
+ 360163,
+ 540217,
+ 810343,
+ 1215497,
+ 1823231,
+ 2734867,
+ 4102283,
+ 6153409,
+ 9230113,
+ 13845163,
+ };
+
+
+ static FT_Long
+ ft_mem_closest_prime( FT_Long num )
+ {
+ size_t i;
+
+
+ for ( i = 0;
+ i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ )
+ if ( ft_mem_primes[i] > num )
+ return ft_mem_primes[i];
+
+ return FT_MEM_SIZE_MAX;
+ }
+
+
+ static void
+ ft_mem_debug_panic( const char* fmt,
+ ... )
+ {
+ va_list ap;
+
+
+ printf( "FreeType.Debug: " );
+
+ va_start( ap, fmt );
+ vprintf( fmt, ap );
+ va_end( ap );
+
+ printf( "\n" );
+ exit( EXIT_FAILURE );
+ }
+
+
+ static FT_Pointer
+ ft_mem_table_alloc( FT_MemTable table,
+ FT_Long size )
+ {
+ FT_Memory memory = table->memory;
+ FT_Pointer block;
+
+
+ memory->user = table->memory_user;
+ block = table->alloc( memory, size );
+ memory->user = table;
+
+ return block;
+ }
+
+
+ static void
+ ft_mem_table_free( FT_MemTable table,
+ FT_Pointer block )
+ {
+ FT_Memory memory = table->memory;
+
+
+ memory->user = table->memory_user;
+ table->free( memory, block );
+ memory->user = table;
+ }
+
+
+ static void
+ ft_mem_table_resize( FT_MemTable table )
+ {
+ FT_Long new_size;
+
+
+ new_size = ft_mem_closest_prime( table->nodes );
+ if ( new_size != table->size )
+ {
+ FT_MemNode* new_buckets;
+ FT_Long i;
+
+
+ new_buckets = (FT_MemNode *)
+ ft_mem_table_alloc(
+ table,
+ new_size * (FT_Long)sizeof ( FT_MemNode ) );
+ if ( !new_buckets )
+ return;
+
+ FT_ARRAY_ZERO( new_buckets, new_size );
+
+ for ( i = 0; i < table->size; i++ )
+ {
+ FT_MemNode node, next, *pnode;
+ FT_PtrDist hash;
+
+
+ node = table->buckets[i];
+ while ( node )
+ {
+ next = node->link;
+ hash = FT_MEM_VAL( node->address ) % (FT_PtrDist)new_size;
+ pnode = new_buckets + hash;
+
+ node->link = pnode[0];
+ pnode[0] = node;
+
+ node = next;
+ }
+ }
+
+ if ( table->buckets )
+ ft_mem_table_free( table, table->buckets );
+
+ table->buckets = new_buckets;
+ table->size = new_size;
+ }
+ }
+
+
+ static void
+ ft_mem_table_destroy( FT_MemTable table )
+ {
+ FT_Long i;
+ FT_Long leak_count = 0;
+ FT_Long leaks = 0;
+
+
+ /* remove all blocks from the table, revealing leaked ones */
+ for ( i = 0; i < table->size; i++ )
+ {
+ FT_MemNode *pnode = table->buckets + i, next, node = *pnode;
+
+
+ while ( node )
+ {
+ next = node->link;
+ node->link = NULL;
+
+ if ( node->size > 0 )
+ {
+ printf(
+ "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
+ (void*)node->address,
+ node->size,
+ FT_FILENAME( node->source->file_name ),
+ node->source->line_no );
+
+ leak_count++;
+ leaks += node->size;
+
+ ft_mem_table_free( table, node->address );
+ }
+
+ node->address = NULL;
+ node->size = 0;
+
+ ft_mem_table_free( table, node );
+ node = next;
+ }
+ table->buckets[i] = NULL;
+ }
+
+ ft_mem_table_free( table, table->buckets );
+ table->buckets = NULL;
+
+ table->size = 0;
+ table->nodes = 0;
+
+ /* remove all sources */
+ for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
+ {
+ FT_MemSource source, next;
+
+
+ for ( source = table->sources[i]; source != NULL; source = next )
+ {
+ next = source->link;
+ ft_mem_table_free( table, source );
+ }
+
+ table->sources[i] = NULL;
+ }
+
+ printf( "FreeType: total memory allocations = %ld\n",
+ table->alloc_total );
+ printf( "FreeType: maximum memory footprint = %ld\n",
+ table->alloc_max );
+
+ if ( leak_count > 0 )
+ ft_mem_debug_panic(
+ "FreeType: %ld bytes of memory leaked in %ld blocks\n",
+ leaks, leak_count );
+
+ printf( "FreeType: no memory leaks detected\n" );
+ }
+
+
+ static FT_MemNode*
+ ft_mem_table_get_nodep( FT_MemTable table,
+ FT_Byte* address )
+ {
+ FT_PtrDist hash;
+ FT_MemNode *pnode, node;
+
+
+ hash = FT_MEM_VAL( address );
+ pnode = table->buckets + ( hash % (FT_PtrDist)table->size );
+
+ for (;;)
+ {
+ node = pnode[0];
+ if ( !node )
+ break;
+
+ if ( node->address == address )
+ break;
+
+ pnode = &node->link;
+ }
+ return pnode;
+ }
+
+
+ static FT_MemSource
+ ft_mem_table_get_source( FT_MemTable table )
+ {
+ FT_UInt32 hash;
+ FT_MemSource node, *pnode;
+
+
+ /* cast to FT_PtrDist first since void* can be larger */
+ /* than FT_UInt32 and GCC 4.1.1 emits a warning */
+ hash = (FT_UInt32)(FT_PtrDist)(void*)ft_debug_file_ +
+ (FT_UInt32)( 5 * ft_debug_lineno_ );
+ pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];
+
+ for (;;)
+ {
+ node = *pnode;
+ if ( !node )
+ break;
+
+ if ( node->file_name == ft_debug_file_ &&
+ node->line_no == ft_debug_lineno_ )
+ goto Exit;
+
+ pnode = &node->link;
+ }
+
+ node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );
+ if ( !node )
+ ft_mem_debug_panic(
+ "not enough memory to perform memory debugging\n" );
+
+ node->file_name = ft_debug_file_;
+ node->line_no = ft_debug_lineno_;
+
+ node->cur_blocks = 0;
+ node->max_blocks = 0;
+ node->all_blocks = 0;
+
+ node->cur_size = 0;
+ node->max_size = 0;
+ node->all_size = 0;
+
+ node->cur_max = 0;
+
+ node->link = NULL;
+ node->hash = hash;
+ *pnode = node;
+
+ Exit:
+ return node;
+ }
+
+
+ static void
+ ft_mem_table_set( FT_MemTable table,
+ FT_Byte* address,
+ FT_Long size,
+ FT_Long delta )
+ {
+ FT_MemNode *pnode, node;
+
+
+ if ( table )
+ {
+ FT_MemSource source;
+
+
+ pnode = ft_mem_table_get_nodep( table, address );
+ node = *pnode;
+ if ( node )
+ {
+ if ( node->size < 0 )
+ {
+ /* This block was already freed. Our memory is now completely */
+ /* corrupted! */
+ /* This can only happen in keep-alive mode. */
+ ft_mem_debug_panic(
+ "memory heap corrupted (allocating freed block)" );
+ }
+ else
+ {
+ /* This block was already allocated. This means that our memory */
+ /* is also corrupted! */
+ ft_mem_debug_panic(
+ "memory heap corrupted (re-allocating allocated block at"
+ " %p, of size %ld)\n"
+ "org=%s:%d new=%s:%d\n",
+ node->address, node->size,
+ FT_FILENAME( node->source->file_name ), node->source->line_no,
+ FT_FILENAME( ft_debug_file_ ), ft_debug_lineno_ );
+ }
+ }
+
+ /* we need to create a new node in this table */
+ node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
+ if ( !node )
+ ft_mem_debug_panic( "not enough memory to run memory tests" );
+
+ node->address = address;
+ node->size = size;
+ node->source = source = ft_mem_table_get_source( table );
+
+ if ( delta == 0 )
+ {
+ /* this is an allocation */
+ source->all_blocks++;
+ source->cur_blocks++;
+ if ( source->cur_blocks > source->max_blocks )
+ source->max_blocks = source->cur_blocks;
+ }
+
+ if ( size > source->cur_max )
+ source->cur_max = size;
+
+ if ( delta != 0 )
+ {
+ /* we are growing or shrinking a reallocated block */
+ source->cur_size += delta;
+ table->alloc_current += delta;
+ }
+ else
+ {
+ /* we are allocating a new block */
+ source->cur_size += size;
+ table->alloc_current += size;
+ }
+
+ source->all_size += size;
+
+ if ( source->cur_size > source->max_size )
+ source->max_size = source->cur_size;
+
+ node->free_file_name = NULL;
+ node->free_line_no = 0;
+
+ node->link = pnode[0];
+
+ pnode[0] = node;
+ table->nodes++;
+
+ table->alloc_total += size;
+
+ if ( table->alloc_current > table->alloc_max )
+ table->alloc_max = table->alloc_current;
+
+ if ( table->nodes * 3 < table->size ||
+ table->size * 3 < table->nodes )
+ ft_mem_table_resize( table );
+ }
+ }
+
+
+ static void
+ ft_mem_table_remove( FT_MemTable table,
+ FT_Byte* address,
+ FT_Long delta )
+ {
+ if ( table )
+ {
+ FT_MemNode *pnode, node;
+
+
+ pnode = ft_mem_table_get_nodep( table, address );
+ node = *pnode;
+ if ( node )
+ {
+ FT_MemSource source;
+
+
+ if ( node->size < 0 )
+ ft_mem_debug_panic(
+ "freeing memory block at %p more than once\n"
+ " at (%s:%ld)!\n"
+ " Block was allocated at (%s:%ld)\n"
+ " and released at (%s:%ld).",
+ address,
+ FT_FILENAME( ft_debug_file_ ), ft_debug_lineno_,
+ FT_FILENAME( node->source->file_name ), node->source->line_no,
+ FT_FILENAME( node->free_file_name ), node->free_line_no );
+
+ /* scramble the node's content for additional safety */
+ FT_MEM_SET( address, 0xF3, node->size );
+
+ if ( delta == 0 )
+ {
+ source = node->source;
+
+ source->cur_blocks--;
+ source->cur_size -= node->size;
+
+ table->alloc_current -= node->size;
+ }
+
+ if ( table->keep_alive )
+ {
+ /* we simply invert the node's size to indicate that the node */
+ /* was freed. */
+ node->size = -node->size;
+ node->free_file_name = ft_debug_file_;
+ node->free_line_no = ft_debug_lineno_;
+ }
+ else
+ {
+ table->nodes--;
+
+ *pnode = node->link;
+
+ node->size = 0;
+ node->source = NULL;
+
+ ft_mem_table_free( table, node );
+
+ if ( table->nodes * 3 < table->size ||
+ table->size * 3 < table->nodes )
+ ft_mem_table_resize( table );
+ }
+ }
+ else
+ ft_mem_debug_panic(
+ "trying to free unknown block at %p in (%s:%ld)\n",
+ address,
+ FT_FILENAME( ft_debug_file_ ), ft_debug_lineno_ );
+ }
+ }
+
+
+ static FT_Pointer
+ ft_mem_debug_alloc( FT_Memory memory,
+ FT_Long size )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+ FT_Byte* block;
+
+
+ if ( size <= 0 )
+ ft_mem_debug_panic( "negative block size allocation (%ld)", size );
+
+ /* return NULL if the maximum number of allocations was reached */
+ if ( table->bound_count &&
+ table->alloc_count >= table->alloc_count_max )
+ return NULL;
+
+ /* return NULL if this allocation would overflow the maximum heap size */
+ if ( table->bound_total &&
+ table->alloc_total_max - table->alloc_current > size )
+ return NULL;
+
+ block = (FT_Byte *)ft_mem_table_alloc( table, size );
+ if ( block )
+ {
+ ft_mem_table_set( table, block, size, 0 );
+
+ table->alloc_count++;
+ }
+
+ ft_debug_file_ = "<unknown>";
+ ft_debug_lineno_ = 0;
+
+ return (FT_Pointer)block;
+ }
+
+
+ static void
+ ft_mem_debug_free( FT_Memory memory,
+ FT_Pointer block )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+
+
+ if ( !block )
+ ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
+ FT_FILENAME( ft_debug_file_ ),
+ ft_debug_lineno_ );
+
+ ft_mem_table_remove( table, (FT_Byte*)block, 0 );
+
+ if ( !table->keep_alive )
+ ft_mem_table_free( table, block );
+
+ table->alloc_count--;
+
+ ft_debug_file_ = "<unknown>";
+ ft_debug_lineno_ = 0;
+ }
+
+
+ static FT_Pointer
+ ft_mem_debug_realloc( FT_Memory memory,
+ FT_Long cur_size,
+ FT_Long new_size,
+ FT_Pointer block )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+ FT_MemNode node, *pnode;
+ FT_Pointer new_block;
+ FT_Long delta;
+
+ const char* file_name = FT_FILENAME( ft_debug_file_ );
+ FT_Long line_no = ft_debug_lineno_;
+
+
+ /* unlikely, but possible */
+ if ( new_size == cur_size )
+ return block;
+
+ /* the following is valid according to ANSI C */
+#if 0
+ if ( !block || !cur_size )
+ ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
+ file_name, line_no );
+#endif
+
+ /* while the following is allowed in ANSI C also, we abort since */
+ /* such case should be handled by FreeType. */
+ if ( new_size <= 0 )
+ ft_mem_debug_panic(
+ "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
+ block, cur_size, file_name, line_no );
+
+ /* check `cur_size' value */
+ pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
+ node = *pnode;
+ if ( !node )
+ ft_mem_debug_panic(
+ "trying to reallocate unknown block at %p in (%s:%ld)",
+ block, file_name, line_no );
+
+ if ( node->size <= 0 )
+ ft_mem_debug_panic(
+ "trying to reallocate freed block at %p in (%s:%ld)",
+ block, file_name, line_no );
+
+ if ( node->size != cur_size )
+ ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
+ "%ld instead of %ld in (%s:%ld)",
+ block, cur_size, node->size, file_name, line_no );
+
+ /* return NULL if the maximum number of allocations was reached */
+ if ( table->bound_count &&
+ table->alloc_count >= table->alloc_count_max )
+ return NULL;
+
+ delta = new_size - cur_size;
+
+ /* return NULL if this allocation would overflow the maximum heap size */
+ if ( delta > 0 &&
+ table->bound_total &&
+ table->alloc_current + delta > table->alloc_total_max )
+ return NULL;
+
+ new_block = (FT_Pointer)ft_mem_table_alloc( table, new_size );
+ if ( !new_block )
+ return NULL;
+
+ ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );
+
+ ft_memcpy( new_block, block, cur_size < new_size ? (size_t)cur_size
+ : (size_t)new_size );
+
+ ft_mem_table_remove( table, (FT_Byte*)block, delta );
+
+ ft_debug_file_ = "<unknown>";
+ ft_debug_lineno_ = 0;
+
+ if ( !table->keep_alive )
+ ft_mem_table_free( table, block );
+
+ return new_block;
+ }
+
+
+ extern void
+ ft_mem_debug_init( FT_Memory memory )
+ {
+ FT_MemTable table;
+
+
+ if ( !ft_getenv( "FT2_DEBUG_MEMORY" ) )
+ return;
+
+ table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );
+
+ if ( table )
+ {
+ FT_ZERO( table );
+
+ table->memory = memory;
+ table->memory_user = memory->user;
+ table->alloc = memory->alloc;
+ table->realloc = memory->realloc;
+ table->free = memory->free;
+
+ ft_mem_table_resize( table );
+
+ if ( table->size )
+ {
+ const char* p;
+
+
+ memory->user = table;
+ memory->alloc = ft_mem_debug_alloc;
+ memory->realloc = ft_mem_debug_realloc;
+ memory->free = ft_mem_debug_free;
+
+ p = ft_getenv( "FT2_ALLOC_TOTAL_MAX" );
+ if ( p )
+ {
+ FT_Long total_max = ft_strtol( p, NULL, 10 );
+
+
+ if ( total_max > 0 )
+ {
+ table->bound_total = 1;
+ table->alloc_total_max = total_max;
+ }
+ }
+
+ p = ft_getenv( "FT2_ALLOC_COUNT_MAX" );
+ if ( p )
+ {
+ FT_Long total_count = ft_strtol( p, NULL, 10 );
+
+
+ if ( total_count > 0 )
+ {
+ table->bound_count = 1;
+ table->alloc_count_max = total_count;
+ }
+ }
+
+ p = ft_getenv( "FT2_KEEP_ALIVE" );
+ if ( p )
+ {
+ FT_Long keep_alive = ft_strtol( p, NULL, 10 );
+
+
+ if ( keep_alive > 0 )
+ table->keep_alive = 1;
+ }
+ }
+ else
+ memory->free( memory, table );
+ }
+ }
+
+
+ extern void
+ ft_mem_debug_done( FT_Memory memory )
+ {
+ if ( memory->free == ft_mem_debug_free )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+
+
+ FT_DumpMemory( memory );
+
+ ft_mem_table_destroy( table );
+
+ memory->free = table->free;
+ memory->realloc = table->realloc;
+ memory->alloc = table->alloc;
+ memory->user = table->memory_user;
+
+ memory->free( memory, table );
+ }
+ }
+
+
+ FT_COMPARE_DEF( int )
+ ft_mem_source_compare( const void* p1,
+ const void* p2 )
+ {
+ FT_MemSource s1 = *(FT_MemSource*)p1;
+ FT_MemSource s2 = *(FT_MemSource*)p2;
+
+
+ if ( s2->max_size > s1->max_size )
+ return 1;
+ else if ( s2->max_size < s1->max_size )
+ return -1;
+ else
+ return 0;
+ }
+
+
+ extern void
+ FT_DumpMemory( FT_Memory memory )
+ {
+ if ( memory->free == ft_mem_debug_free )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+ FT_MemSource* bucket = table->sources;
+ FT_MemSource* limit = bucket + FT_MEM_SOURCE_BUCKETS;
+ FT_MemSource* sources;
+ FT_Int nn, count;
+ const char* fmt;
+
+
+ count = 0;
+ for ( ; bucket < limit; bucket++ )
+ {
+ FT_MemSource source = *bucket;
+
+
+ for ( ; source; source = source->link )
+ count++;
+ }
+
+ sources = (FT_MemSource*)
+ ft_mem_table_alloc(
+ table, count * (FT_Long)sizeof ( *sources ) );
+
+ count = 0;
+ for ( bucket = table->sources; bucket < limit; bucket++ )
+ {
+ FT_MemSource source = *bucket;
+
+
+ for ( ; source; source = source->link )
+ sources[count++] = source;
+ }
+
+ ft_qsort( sources,
+ (size_t)count,
+ sizeof ( *sources ),
+ ft_mem_source_compare );
+
+ printf( "FreeType Memory Dump: "
+ "current=%ld max=%ld total=%ld count=%ld\n",
+ table->alloc_current, table->alloc_max,
+ table->alloc_total, table->alloc_count );
+ printf( " block block sizes sizes sizes source\n" );
+ printf( " count high sum highsum max location\n" );
+ printf( "-------------------------------------------------\n" );
+
+ fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
+
+ for ( nn = 0; nn < count; nn++ )
+ {
+ FT_MemSource source = sources[nn];
+
+
+ printf( fmt,
+ source->cur_blocks, source->max_blocks,
+ source->cur_size, source->max_size, source->cur_max,
+ FT_FILENAME( source->file_name ),
+ source->line_no );
+ }
+ printf( "------------------------------------------------\n" );
+
+ ft_mem_table_free( table, sources );
+ }
+ }
+
+#else /* !FT_DEBUG_MEMORY */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _debug_mem_dummy;
+
+#endif /* !FT_DEBUG_MEMORY */
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftdebug.c b/modules/freetype2/src/base/ftdebug.c
new file mode 100644
index 0000000000..61c4563b0c
--- /dev/null
+++ b/modules/freetype2/src/base/ftdebug.c
@@ -0,0 +1,644 @@
+/****************************************************************************
+ *
+ * ftdebug.c
+ *
+ * Debugging and logging component (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.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * This component contains various macros and functions used to ease the
+ * debugging of the FreeType engine. Its main purpose is in assertion
+ * checking, tracing, and error detection.
+ *
+ * There are now three debugging modes:
+ *
+ * - trace mode
+ *
+ * Error and trace messages are sent to the log file (which can be the
+ * standard error output).
+ *
+ * - error mode
+ *
+ * Only error messages are generated.
+ *
+ * - release mode:
+ *
+ * No error message is sent or generated. The code is free from any
+ * debugging parts.
+ *
+ */
+
+
+#include <freetype/freetype.h>
+#include <freetype/ftlogging.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftobjs.h>
+
+
+#ifdef FT_DEBUG_LOGGING
+
+ /**************************************************************************
+ *
+ * Variables used to control logging.
+ *
+ * 1. `ft_default_trace_level` stores the value of trace levels, which are
+ * provided to FreeType using the `FT2_DEBUG` environment variable.
+ *
+ * 2. `ft_fileptr` stores the `FILE*` handle.
+ *
+ * 3. `ft_component` is a string that holds the name of `FT_COMPONENT`.
+ *
+ * 4. The flag `ft_component_flag` prints the name of `FT_COMPONENT` along
+ * with the actual log message if set to true.
+ *
+ * 5. The flag `ft_timestamp_flag` prints time along with the actual log
+ * message if set to ture.
+ *
+ * 6. `ft_have_newline_char` is used to differentiate between a log
+ * message with and without a trailing newline character.
+ *
+ * 7. `ft_custom_trace_level` stores the custom trace level value, which
+ * is provided by the user at run-time.
+ *
+ * We use `static` to avoid 'unused variable' warnings.
+ *
+ */
+ static const char* ft_default_trace_level = NULL;
+ static FILE* ft_fileptr = NULL;
+ static const char* ft_component = NULL;
+ static FT_Bool ft_component_flag = FALSE;
+ static FT_Bool ft_timestamp_flag = FALSE;
+ static FT_Bool ft_have_newline_char = TRUE;
+ static const char* ft_custom_trace_level = NULL;
+
+ /* declared in ftdebug.h */
+
+ dlg_handler ft_default_log_handler = NULL;
+ FT_Custom_Log_Handler custom_output_handler = NULL;
+
+#endif /* FT_DEBUG_LOGGING */
+
+
+#ifdef FT_DEBUG_LEVEL_ERROR
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( void )
+ FT_Message( const char* fmt,
+ ... )
+ {
+ va_list ap;
+
+
+ va_start( ap, fmt );
+ vfprintf( stderr, fmt, ap );
+ va_end( ap );
+ }
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( void )
+ FT_Panic( const char* fmt,
+ ... )
+ {
+ va_list ap;
+
+
+ va_start( ap, fmt );
+ vfprintf( stderr, fmt, ap );
+ va_end( ap );
+
+ exit( EXIT_FAILURE );
+ }
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( int )
+ FT_Throw( FT_Error error,
+ int line,
+ const char* file )
+ {
+#if 0
+ /* activating the code in this block makes FreeType very chatty */
+ fprintf( stderr,
+ "%s:%d: error 0x%02x: %s\n",
+ file,
+ line,
+ error,
+ FT_Error_String( error ) );
+#else
+ FT_UNUSED( error );
+ FT_UNUSED( line );
+ FT_UNUSED( file );
+#endif
+
+ return 0;
+ }
+
+#endif /* FT_DEBUG_LEVEL_ERROR */
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+ /* array of trace levels, initialized to 0; */
+ /* this gets adjusted at run-time */
+ static int ft_trace_levels_enabled[trace_count];
+
+ /* array of trace levels, always initialized to 0 */
+ static int ft_trace_levels_disabled[trace_count];
+
+ /* a pointer to either `ft_trace_levels_enabled' */
+ /* or `ft_trace_levels_disabled' */
+ int* ft_trace_levels;
+
+ /* define array of trace toggle names */
+#define FT_TRACE_DEF( x ) #x ,
+
+ static const char* ft_trace_toggles[trace_count + 1] =
+ {
+#include <freetype/internal/fttrace.h>
+ NULL
+ };
+
+#undef FT_TRACE_DEF
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( FT_Int )
+ FT_Trace_Get_Count( void )
+ {
+ return trace_count;
+ }
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( const char * )
+ FT_Trace_Get_Name( FT_Int idx )
+ {
+ int max = FT_Trace_Get_Count();
+
+
+ if ( idx < max )
+ return ft_trace_toggles[idx];
+ else
+ return NULL;
+ }
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( void )
+ FT_Trace_Disable( void )
+ {
+ ft_trace_levels = ft_trace_levels_disabled;
+ }
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( void )
+ FT_Trace_Enable( void )
+ {
+ ft_trace_levels = ft_trace_levels_enabled;
+ }
+
+
+ /**************************************************************************
+ *
+ * Initialize the tracing sub-system. This is done by retrieving the
+ * value of the `FT2_DEBUG' environment variable. It must be a list of
+ * toggles, separated by spaces, `;', or `,'. Example:
+ *
+ * export FT2_DEBUG="any:3 memory:7 stream:5"
+ *
+ * This requests that all levels be set to 3, except the trace level for
+ * the memory and stream components which are set to 7 and 5,
+ * respectively.
+ *
+ * See the file `include/freetype/internal/fttrace.h' for details of
+ * the available toggle names.
+ *
+ * The level must be between 0 and 7; 0 means quiet (except for serious
+ * runtime errors), and 7 means _very_ verbose.
+ */
+ FT_BASE_DEF( void )
+ ft_debug_init( void )
+ {
+ const char* ft2_debug = NULL;
+
+
+#ifdef FT_DEBUG_LOGGING
+ if ( ft_custom_trace_level != NULL )
+ ft2_debug = ft_custom_trace_level;
+ else
+ ft2_debug = ft_default_trace_level;
+#else
+ ft2_debug = ft_getenv( "FT2_DEBUG" );
+#endif
+
+ if ( ft2_debug )
+ {
+ const char* p = ft2_debug;
+ const char* q;
+
+
+ for ( ; *p; p++ )
+ {
+ /* skip leading whitespace and separators */
+ if ( *p == ' ' || *p == '\t' || *p == ',' || *p == ';' || *p == '=' )
+ continue;
+
+#ifdef FT_DEBUG_LOGGING
+
+ /* check extra arguments for logging */
+ if ( *p == '-' )
+ {
+ const char* r = ++p;
+
+
+ if ( *r == 'v' )
+ {
+ const char* s = ++r;
+
+
+ ft_component_flag = TRUE;
+
+ if ( *s == 't' )
+ {
+ ft_timestamp_flag = TRUE;
+ p++;
+ }
+
+ p++;
+ }
+
+ else if ( *r == 't' )
+ {
+ const char* s = ++r;
+
+
+ ft_timestamp_flag = TRUE;
+
+ if ( *s == 'v' )
+ {
+ ft_component_flag = TRUE;
+ p++;
+ }
+
+ p++;
+ }
+ }
+
+#endif /* FT_DEBUG_LOGGING */
+
+ /* read toggle name, followed by ':' */
+ q = p;
+ while ( *p && *p != ':' )
+ p++;
+
+ if ( !*p )
+ break;
+
+ if ( *p == ':' && p > q )
+ {
+ FT_Int n, i, len = (FT_Int)( p - q );
+ FT_Int level = -1, found = -1;
+
+
+ for ( n = 0; n < trace_count; n++ )
+ {
+ const char* toggle = ft_trace_toggles[n];
+
+
+ for ( i = 0; i < len; i++ )
+ {
+ if ( toggle[i] != q[i] )
+ break;
+ }
+
+ if ( i == len && toggle[i] == 0 )
+ {
+ found = n;
+ break;
+ }
+ }
+
+ /* read level */
+ p++;
+ if ( *p )
+ {
+ level = *p - '0';
+ if ( level < 0 || level > 7 )
+ level = -1;
+ }
+
+ if ( found >= 0 && level >= 0 )
+ {
+ if ( found == trace_any )
+ {
+ /* special case for `any' */
+ for ( n = 0; n < trace_count; n++ )
+ ft_trace_levels_enabled[n] = level;
+ }
+ else
+ ft_trace_levels_enabled[found] = level;
+ }
+ }
+ }
+ }
+
+ ft_trace_levels = ft_trace_levels_enabled;
+ }
+
+
+#else /* !FT_DEBUG_LEVEL_TRACE */
+
+
+ FT_BASE_DEF( void )
+ ft_debug_init( void )
+ {
+ /* nothing */
+ }
+
+
+ FT_BASE_DEF( FT_Int )
+ FT_Trace_Get_Count( void )
+ {
+ return 0;
+ }
+
+
+ FT_BASE_DEF( const char * )
+ FT_Trace_Get_Name( FT_Int idx )
+ {
+ FT_UNUSED( idx );
+
+ return NULL;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Trace_Disable( void )
+ {
+ /* nothing */
+ }
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( void )
+ FT_Trace_Enable( void )
+ {
+ /* nothing */
+ }
+
+#endif /* !FT_DEBUG_LEVEL_TRACE */
+
+
+#ifdef FT_DEBUG_LOGGING
+
+ /**************************************************************************
+ *
+ * Initialize and de-initialize 'dlg' library.
+ *
+ */
+
+ FT_BASE_DEF( void )
+ ft_logging_init( void )
+ {
+ ft_default_log_handler = ft_log_handler;
+ ft_default_trace_level = ft_getenv( "FT2_DEBUG" );
+
+ if ( ft_getenv( "FT_LOGGING_FILE" ) )
+ ft_fileptr = ft_fopen( ft_getenv( "FT_LOGGING_FILE" ), "w" );
+ else
+ ft_fileptr = stderr;
+
+ ft_debug_init();
+
+ /* Set the default output handler for 'dlg'. */
+ dlg_set_handler( ft_default_log_handler, NULL );
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_logging_deinit( void )
+ {
+ if ( ft_fileptr != stderr )
+ ft_fclose( ft_fileptr );
+ }
+
+
+ /**************************************************************************
+ *
+ * An output log handler for FreeType.
+ *
+ */
+ FT_BASE_DEF( void )
+ ft_log_handler( const struct dlg_origin* origin,
+ const char* string,
+ void* data )
+ {
+ char features_buf[128];
+ char* bufp = features_buf;
+
+ FT_UNUSED( data );
+
+
+ if ( ft_have_newline_char )
+ {
+ const char* features = NULL;
+ size_t features_length = 0;
+
+
+#define FEATURES_TIMESTAMP "[%h:%m] "
+#define FEATURES_COMPONENT "[%t] "
+#define FEATURES_TIMESTAMP_COMPONENT "[%h:%m %t] "
+
+ if ( ft_timestamp_flag && ft_component_flag )
+ {
+ features = FEATURES_TIMESTAMP_COMPONENT;
+ features_length = sizeof ( FEATURES_TIMESTAMP_COMPONENT );
+ }
+ else if ( ft_timestamp_flag )
+ {
+ features = FEATURES_TIMESTAMP;
+ features_length = sizeof ( FEATURES_TIMESTAMP );
+ }
+ else if ( ft_component_flag )
+ {
+ features = FEATURES_COMPONENT;
+ features_length = sizeof ( FEATURES_COMPONENT );
+ }
+
+ if ( ft_component_flag || ft_timestamp_flag )
+ {
+ ft_strncpy( features_buf, features, features_length );
+ bufp += features_length - 1;
+ }
+
+ if ( ft_component_flag )
+ {
+ size_t tag_length = ft_strlen( *origin->tags );
+ size_t i;
+
+
+ /* To vertically align tracing messages we compensate the */
+ /* different FT_COMPONENT string lengths by inserting an */
+ /* appropriate amount of space characters. */
+ for ( i = 0;
+ i < FT_MAX_TRACE_LEVEL_LENGTH - tag_length;
+ i++ )
+ *bufp++ = ' ';
+ }
+ }
+
+ /* Finally add the format string for the tracing message. */
+ *bufp++ = '%';
+ *bufp++ = 'c';
+ *bufp = '\0';
+
+ dlg_generic_outputf_stream( ft_fileptr,
+ (const char*)features_buf,
+ origin,
+ string,
+ dlg_default_output_styles,
+ true );
+
+ if ( ft_strrchr( string, '\n' ) )
+ ft_have_newline_char = TRUE;
+ else
+ ft_have_newline_char = FALSE;
+ }
+
+
+ /* documentation is in ftdebug.h */
+ FT_BASE_DEF( void )
+ ft_add_tag( const char* tag )
+ {
+ ft_component = tag;
+
+ dlg_add_tag( tag, NULL );
+ }
+
+
+ /* documentation is in ftdebug.h */
+ FT_BASE_DEF( void )
+ ft_remove_tag( const char* tag )
+ {
+ dlg_remove_tag( tag, NULL );
+ }
+
+
+ /* documentation is in ftlogging.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Trace_Set_Level( const char* level )
+ {
+ ft_component_flag = FALSE;
+ ft_timestamp_flag = FALSE;
+ ft_custom_trace_level = level;
+
+ ft_debug_init();
+ }
+
+
+ /* documentation is in ftlogging.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Trace_Set_Default_Level( void )
+ {
+ ft_component_flag = FALSE;
+ ft_timestamp_flag = FALSE;
+ ft_custom_trace_level = NULL;
+
+ ft_debug_init();
+ }
+
+
+ /**************************************************************************
+ *
+ * Functions to handle a custom log handler.
+ *
+ */
+
+ /* documentation is in ftlogging.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Log_Handler( FT_Custom_Log_Handler handler )
+ {
+ custom_output_handler = handler;
+ }
+
+
+ /* documentation is in ftlogging.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Default_Log_Handler( void )
+ {
+ custom_output_handler = NULL;
+ }
+
+
+ /* documentation is in ftdebug.h */
+ FT_BASE_DEF( void )
+ FT_Logging_Callback( const char* fmt,
+ ... )
+ {
+ va_list ap;
+
+
+ va_start( ap, fmt );
+ custom_output_handler( ft_component, fmt, ap );
+ va_end( ap );
+ }
+
+#else /* !FT_DEBUG_LOGGING */
+
+ FT_EXPORT_DEF( void )
+ FT_Trace_Set_Level( const char* level )
+ {
+ FT_UNUSED( level );
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_Trace_Set_Default_Level( void )
+ {
+ /* nothing */
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Log_Handler( FT_Custom_Log_Handler handler )
+ {
+ FT_UNUSED( handler );
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Default_Log_Handler( void )
+ {
+ /* nothing */
+ }
+
+#endif /* !FT_DEBUG_LOGGING */
+
+
+/* END */
diff --git a/modules/freetype2/src/base/fterrors.c b/modules/freetype2/src/base/fterrors.c
new file mode 100644
index 0000000000..5ad9709c80
--- /dev/null
+++ b/modules/freetype2/src/base/fterrors.c
@@ -0,0 +1,45 @@
+/****************************************************************************
+ *
+ * fterrors.c
+ *
+ * FreeType API for error code handling.
+ *
+ * Copyright (C) 2018-2023 by
+ * Armin Hasitzka, 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/fterrors.h>
+
+
+ /* documentation is in fterrors.h */
+
+ FT_EXPORT_DEF( const char* )
+ FT_Error_String( FT_Error error_code )
+ {
+ if ( error_code < 0 ||
+ error_code >= FT_ERR_CAT( FT_ERR_PREFIX, Max ) )
+ return NULL;
+
+#if defined( FT_CONFIG_OPTION_ERROR_STRINGS ) || \
+ defined( FT_DEBUG_LEVEL_ERROR )
+
+#undef FTERRORS_H_
+#define FT_ERROR_START_LIST switch ( FT_ERROR_BASE( error_code ) ) {
+#define FT_ERRORDEF( e, v, s ) case v: return s;
+#define FT_ERROR_END_LIST }
+
+#include <freetype/fterrors.h>
+
+#endif /* defined( FT_CONFIG_OPTION_ERROR_STRINGS ) || ... */
+
+ return NULL;
+ }
diff --git a/modules/freetype2/src/base/ftfntfmt.c b/modules/freetype2/src/base/ftfntfmt.c
new file mode 100644
index 0000000000..0b41f7cc83
--- /dev/null
+++ b/modules/freetype2/src/base/ftfntfmt.c
@@ -0,0 +1,54 @@
+/****************************************************************************
+ *
+ * ftfntfmt.c
+ *
+ * FreeType utility file for font formats (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/ftfntfmt.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/services/svfntfmt.h>
+
+
+ /* documentation is in ftfntfmt.h */
+
+ FT_EXPORT_DEF( const char* )
+ FT_Get_Font_Format( FT_Face face )
+ {
+ const char* result = NULL;
+
+
+ if ( face )
+ FT_FACE_FIND_SERVICE( face, result, FONT_FORMAT );
+
+ return result;
+ }
+
+
+ /* deprecated function name; retained for ABI compatibility */
+
+ FT_EXPORT_DEF( const char* )
+ FT_Get_X11_Font_Format( FT_Face face )
+ {
+ const char* result = NULL;
+
+
+ if ( face )
+ FT_FACE_FIND_SERVICE( face, result, FONT_FORMAT );
+
+ return result;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftfstype.c b/modules/freetype2/src/base/ftfstype.c
new file mode 100644
index 0000000000..ea24e64c6e
--- /dev/null
+++ b/modules/freetype2/src/base/ftfstype.c
@@ -0,0 +1,61 @@
+/****************************************************************************
+ *
+ * ftfstype.c
+ *
+ * FreeType utility file to access FSType data (body).
+ *
+ * Copyright (C) 2008-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/t1tables.h>
+#include <freetype/tttables.h>
+#include <freetype/internal/ftserv.h>
+#include <freetype/internal/services/svpsinfo.h>
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UShort )
+ FT_Get_FSType_Flags( FT_Face face )
+ {
+ TT_OS2* os2;
+
+
+ /* first, try to get the fs_type directly from the font */
+ if ( face )
+ {
+ FT_Service_PsInfo service = NULL;
+
+
+ FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO );
+
+ if ( service && service->ps_get_font_extra )
+ {
+ PS_FontExtraRec extra;
+
+
+ if ( !service->ps_get_font_extra( face, &extra ) &&
+ extra.fs_type != 0 )
+ return extra.fs_type;
+ }
+ }
+
+ /* look at FSType before fsType for Type42 */
+
+ if ( ( os2 = (TT_OS2*)FT_Get_Sfnt_Table( face, FT_SFNT_OS2 ) ) != NULL &&
+ os2->version != 0xFFFFU )
+ return os2->fsType;
+
+ return 0;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftgasp.c b/modules/freetype2/src/base/ftgasp.c
new file mode 100644
index 0000000000..29b7b08b78
--- /dev/null
+++ b/modules/freetype2/src/base/ftgasp.c
@@ -0,0 +1,60 @@
+/****************************************************************************
+ *
+ * ftgasp.c
+ *
+ * Access of TrueType's `gasp' table (body).
+ *
+ * Copyright (C) 2007-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/ftgasp.h>
+#include <freetype/internal/tttypes.h>
+
+
+ FT_EXPORT_DEF( FT_Int )
+ FT_Get_Gasp( FT_Face face,
+ FT_UInt ppem )
+ {
+ FT_Int result = FT_GASP_NO_TABLE;
+
+
+ if ( face && FT_IS_SFNT( face ) )
+ {
+ TT_Face ttface = (TT_Face)face;
+
+
+ if ( ttface->gasp.numRanges > 0 )
+ {
+ TT_GaspRange range = ttface->gasp.gaspRanges;
+ TT_GaspRange range_end = range + ttface->gasp.numRanges;
+
+
+ while ( ppem > range->maxPPEM )
+ {
+ range++;
+ if ( range >= range_end )
+ goto Exit;
+ }
+
+ result = range->gaspFlag;
+
+ /* ensure that we don't have spurious bits */
+ if ( ttface->gasp.version == 0 )
+ result &= 3;
+ }
+ }
+ Exit:
+ return result;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftgloadr.c b/modules/freetype2/src/base/ftgloadr.c
new file mode 100644
index 0000000000..9823d09e41
--- /dev/null
+++ b/modules/freetype2/src/base/ftgloadr.c
@@ -0,0 +1,392 @@
+/****************************************************************************
+ *
+ * ftgloadr.c
+ *
+ * The FreeType glyph loader (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 <freetype/internal/ftgloadr.h>
+#include <freetype/internal/ftmemory.h>
+#include <freetype/internal/ftobjs.h>
+
+#undef FT_COMPONENT
+#define FT_COMPONENT gloader
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** *****/
+ /***** G L Y P H L O A D E R *****/
+ /***** *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * The glyph loader is a simple object which is used to load a set of
+ * glyphs easily. It is critical for the correct loading of composites.
+ *
+ * Ideally, one can see it as a stack of abstract `glyph' objects.
+ *
+ * loader.base Is really the bottom of the stack. It describes a
+ * single glyph image made of the juxtaposition of
+ * several glyphs (those `in the stack').
+ *
+ * loader.current Describes the top of the stack, on which a new
+ * glyph can be loaded.
+ *
+ * Rewind Clears the stack.
+ * Prepare Set up `loader.current' for addition of a new glyph
+ * image.
+ * Add Add the `current' glyph image to the `base' one,
+ * and prepare for another one.
+ *
+ * The glyph loader is now a base object. Each driver used to
+ * re-implement it in one way or the other, which wasted code and
+ * energy.
+ *
+ */
+
+
+ /* create a new glyph loader */
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_New( FT_Memory memory,
+ FT_GlyphLoader *aloader )
+ {
+ FT_GlyphLoader loader = NULL;
+ FT_Error error;
+
+
+ if ( !FT_NEW( loader ) )
+ {
+ loader->memory = memory;
+ *aloader = loader;
+ }
+ return error;
+ }
+
+
+ /* rewind the glyph loader - reset counters to 0 */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Rewind( FT_GlyphLoader loader )
+ {
+ FT_GlyphLoad base = &loader->base;
+ FT_GlyphLoad current = &loader->current;
+
+
+ base->outline.n_points = 0;
+ base->outline.n_contours = 0;
+ base->outline.flags = 0;
+ base->num_subglyphs = 0;
+
+ *current = *base;
+ }
+
+
+ /* reset glyph loader, free all allocated tables, */
+ /* and start from zero */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Reset( FT_GlyphLoader loader )
+ {
+ FT_Memory memory = loader->memory;
+
+
+ FT_FREE( loader->base.outline.points );
+ FT_FREE( loader->base.outline.tags );
+ FT_FREE( loader->base.outline.contours );
+ FT_FREE( loader->base.extra_points );
+ FT_FREE( loader->base.subglyphs );
+
+ loader->base.extra_points2 = NULL;
+
+ loader->max_points = 0;
+ loader->max_contours = 0;
+ loader->max_subglyphs = 0;
+
+ FT_GlyphLoader_Rewind( loader );
+ }
+
+
+ /* delete a glyph loader */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Done( FT_GlyphLoader loader )
+ {
+ if ( loader )
+ {
+ FT_Memory memory = loader->memory;
+
+
+ FT_GlyphLoader_Reset( loader );
+ FT_FREE( loader );
+ }
+ }
+
+
+ /* re-adjust the `current' outline fields */
+ static void
+ FT_GlyphLoader_Adjust_Points( FT_GlyphLoader loader )
+ {
+ FT_Outline* base = &loader->base.outline;
+ FT_Outline* current = &loader->current.outline;
+
+
+ current->points = FT_OFFSET( base->points, base->n_points );
+ current->tags = FT_OFFSET( base->tags, base->n_points );
+ current->contours = FT_OFFSET( base->contours, base->n_contours );
+
+ /* handle extra points table - if any */
+ if ( loader->use_extra )
+ {
+ loader->current.extra_points = loader->base.extra_points +
+ base->n_points;
+
+ loader->current.extra_points2 = loader->base.extra_points2 +
+ base->n_points;
+ }
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_CreateExtra( FT_GlyphLoader loader )
+ {
+ FT_Error error;
+ FT_Memory memory = loader->memory;
+
+
+ if ( loader->max_points == 0 ||
+ loader->base.extra_points != NULL )
+ return FT_Err_Ok;
+
+ if ( !FT_NEW_ARRAY( loader->base.extra_points, 2 * loader->max_points ) )
+ {
+ loader->use_extra = 1;
+ loader->base.extra_points2 = loader->base.extra_points +
+ loader->max_points;
+
+ FT_GlyphLoader_Adjust_Points( loader );
+ }
+ return error;
+ }
+
+
+ /* re-adjust the `current' subglyphs field */
+ static void
+ FT_GlyphLoader_Adjust_Subglyphs( FT_GlyphLoader loader )
+ {
+ FT_GlyphLoad base = &loader->base;
+ FT_GlyphLoad current = &loader->current;
+
+
+ current->subglyphs = FT_OFFSET( base->subglyphs, base->num_subglyphs );
+ }
+
+
+ /* Ensure that we can add `n_points' and `n_contours' to our glyph. */
+ /* This function reallocates its outline tables if necessary. Note that */
+ /* it DOESN'T change the number of points within the loader! */
+ /* */
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_CheckPoints( FT_GlyphLoader loader,
+ FT_UInt n_points,
+ FT_UInt n_contours )
+ {
+ FT_Memory memory = loader->memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Outline* base = &loader->base.outline;
+ FT_Outline* current = &loader->current.outline;
+ FT_Bool adjust = 0;
+
+ FT_UInt new_max, old_max, min_new_max;
+
+
+ error = FT_GlyphLoader_CreateExtra( loader );
+ if ( error )
+ goto Exit;
+
+ /* check points & tags */
+ new_max = (FT_UInt)base->n_points + (FT_UInt)current->n_points +
+ n_points;
+ old_max = loader->max_points;
+
+ if ( new_max > old_max )
+ {
+ if ( new_max > FT_OUTLINE_POINTS_MAX )
+ {
+ error = FT_THROW( Array_Too_Large );
+ goto Exit;
+ }
+
+ min_new_max = old_max + ( old_max >> 1 );
+ if ( new_max < min_new_max )
+ new_max = min_new_max;
+ new_max = FT_PAD_CEIL( new_max, 8 );
+ if ( new_max > FT_OUTLINE_POINTS_MAX )
+ new_max = FT_OUTLINE_POINTS_MAX;
+
+ if ( FT_RENEW_ARRAY( base->points, old_max, new_max ) ||
+ FT_RENEW_ARRAY( base->tags, old_max, new_max ) )
+ goto Exit;
+
+ if ( loader->use_extra )
+ {
+ if ( FT_RENEW_ARRAY( loader->base.extra_points,
+ old_max * 2, new_max * 2 ) )
+ goto Exit;
+
+ FT_ARRAY_MOVE( loader->base.extra_points + new_max,
+ loader->base.extra_points + old_max,
+ old_max );
+
+ loader->base.extra_points2 = loader->base.extra_points + new_max;
+ }
+
+ adjust = 1;
+ loader->max_points = new_max;
+ }
+
+ error = FT_GlyphLoader_CreateExtra( loader );
+ if ( error )
+ goto Exit;
+
+ /* check contours */
+ old_max = loader->max_contours;
+ new_max = (FT_UInt)base->n_contours + (FT_UInt)current->n_contours +
+ n_contours;
+ if ( new_max > old_max )
+ {
+ if ( new_max > FT_OUTLINE_CONTOURS_MAX )
+ {
+ error = FT_THROW( Array_Too_Large );
+ goto Exit;
+ }
+
+ min_new_max = old_max + ( old_max >> 1 );
+ if ( new_max < min_new_max )
+ new_max = min_new_max;
+ new_max = FT_PAD_CEIL( new_max, 4 );
+ if ( new_max > FT_OUTLINE_CONTOURS_MAX )
+ new_max = FT_OUTLINE_CONTOURS_MAX;
+
+ if ( FT_RENEW_ARRAY( base->contours, old_max, new_max ) )
+ goto Exit;
+
+ adjust = 1;
+ loader->max_contours = new_max;
+ }
+
+ if ( adjust )
+ FT_GlyphLoader_Adjust_Points( loader );
+
+ Exit:
+ if ( error )
+ FT_GlyphLoader_Reset( loader );
+
+ return error;
+ }
+
+
+ /* Ensure that we can add `n_subglyphs' to our glyph. this function */
+ /* reallocates its subglyphs table if necessary. Note that it DOES */
+ /* NOT change the number of subglyphs within the loader! */
+ /* */
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_CheckSubGlyphs( FT_GlyphLoader loader,
+ FT_UInt n_subs )
+ {
+ FT_Memory memory = loader->memory;
+ FT_Error error = FT_Err_Ok;
+ FT_UInt new_max, old_max;
+
+ FT_GlyphLoad base = &loader->base;
+ FT_GlyphLoad current = &loader->current;
+
+
+ new_max = base->num_subglyphs + current->num_subglyphs + n_subs;
+ old_max = loader->max_subglyphs;
+ if ( new_max > old_max )
+ {
+ new_max = FT_PAD_CEIL( new_max, 2 );
+ if ( FT_RENEW_ARRAY( base->subglyphs, old_max, new_max ) )
+ goto Exit;
+
+ loader->max_subglyphs = new_max;
+
+ FT_GlyphLoader_Adjust_Subglyphs( loader );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* prepare loader for the addition of a new glyph on top of the base one */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Prepare( FT_GlyphLoader loader )
+ {
+ FT_GlyphLoad current = &loader->current;
+
+
+ current->outline.n_points = 0;
+ current->outline.n_contours = 0;
+ current->num_subglyphs = 0;
+
+ FT_GlyphLoader_Adjust_Points ( loader );
+ FT_GlyphLoader_Adjust_Subglyphs( loader );
+ }
+
+
+ /* add current glyph to the base image -- and prepare for another */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Add( FT_GlyphLoader loader )
+ {
+ FT_GlyphLoad base;
+ FT_GlyphLoad current;
+
+ FT_Int n_curr_contours;
+ FT_Int n_base_points;
+ FT_Int n;
+
+
+ if ( !loader )
+ return;
+
+ base = &loader->base;
+ current = &loader->current;
+
+ n_curr_contours = current->outline.n_contours;
+ n_base_points = base->outline.n_points;
+
+ base->outline.n_points =
+ (short)( base->outline.n_points + current->outline.n_points );
+ base->outline.n_contours =
+ (short)( base->outline.n_contours + current->outline.n_contours );
+
+ base->num_subglyphs += current->num_subglyphs;
+
+ /* adjust contours count in newest outline */
+ for ( n = 0; n < n_curr_contours; n++ )
+ current->outline.contours[n] =
+ (short)( current->outline.contours[n] + n_base_points );
+
+ /* prepare for another new glyph image */
+ FT_GlyphLoader_Prepare( loader );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftglyph.c b/modules/freetype2/src/base/ftglyph.c
new file mode 100644
index 0000000000..393d4949f8
--- /dev/null
+++ b/modules/freetype2/src/base/ftglyph.c
@@ -0,0 +1,911 @@
+/****************************************************************************
+ *
+ * ftglyph.c
+ *
+ * FreeType convenience functions to handle glyphs (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.
+ *
+ */
+
+ /**************************************************************************
+ *
+ * This file contains the definition of several convenience functions
+ * that can be used by client applications to easily retrieve glyph
+ * bitmaps and outlines from a given face.
+ *
+ * These functions should be optional if you are writing a font server
+ * or text layout engine on top of FreeType. However, they are pretty
+ * handy for many other simple uses of the library.
+ *
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+
+#include <freetype/ftglyph.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftbitmap.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/otsvg.h>
+
+#include "ftbase.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 glyph
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** FT_BitmapGlyph support ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_bitmap_glyph_init( FT_Glyph bitmap_glyph,
+ FT_GlyphSlot slot )
+ {
+ FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;
+ FT_Error error = FT_Err_Ok;
+ FT_Library library = FT_GLYPH( glyph )->library;
+
+
+ if ( slot->format != FT_GLYPH_FORMAT_BITMAP )
+ {
+ error = FT_THROW( Invalid_Glyph_Format );
+ goto Exit;
+ }
+
+ glyph->left = slot->bitmap_left;
+ glyph->top = slot->bitmap_top;
+
+ /* do lazy copying whenever possible */
+ if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
+ {
+ glyph->bitmap = slot->bitmap;
+ slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
+ }
+ else
+ {
+ FT_Bitmap_Init( &glyph->bitmap );
+ error = FT_Bitmap_Copy( library, &slot->bitmap, &glyph->bitmap );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_bitmap_glyph_copy( FT_Glyph bitmap_source,
+ FT_Glyph bitmap_target )
+ {
+ FT_Library library = bitmap_source->library;
+ FT_BitmapGlyph source = (FT_BitmapGlyph)bitmap_source;
+ FT_BitmapGlyph target = (FT_BitmapGlyph)bitmap_target;
+
+
+ target->left = source->left;
+ target->top = source->top;
+
+ return FT_Bitmap_Copy( library, &source->bitmap, &target->bitmap );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_bitmap_glyph_done( FT_Glyph bitmap_glyph )
+ {
+ FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;
+ FT_Library library = FT_GLYPH( glyph )->library;
+
+
+ FT_Bitmap_Done( library, &glyph->bitmap );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_bitmap_glyph_bbox( FT_Glyph bitmap_glyph,
+ FT_BBox* cbox )
+ {
+ FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;
+
+
+ cbox->xMin = glyph->left * 64;
+ cbox->xMax = cbox->xMin + (FT_Pos)( glyph->bitmap.width * 64 );
+ cbox->yMax = glyph->top * 64;
+ cbox->yMin = cbox->yMax - (FT_Pos)( glyph->bitmap.rows * 64 );
+ }
+
+
+ FT_DEFINE_GLYPH(
+ ft_bitmap_glyph_class,
+
+ sizeof ( FT_BitmapGlyphRec ),
+ FT_GLYPH_FORMAT_BITMAP,
+
+ ft_bitmap_glyph_init, /* FT_Glyph_InitFunc glyph_init */
+ ft_bitmap_glyph_done, /* FT_Glyph_DoneFunc glyph_done */
+ ft_bitmap_glyph_copy, /* FT_Glyph_CopyFunc glyph_copy */
+ NULL, /* FT_Glyph_TransformFunc glyph_transform */
+ ft_bitmap_glyph_bbox, /* FT_Glyph_GetBBoxFunc glyph_bbox */
+ NULL /* FT_Glyph_PrepareFunc glyph_prepare */
+ )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** FT_OutlineGlyph support ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_outline_glyph_init( FT_Glyph outline_glyph,
+ FT_GlyphSlot slot )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+ FT_Error error = FT_Err_Ok;
+ FT_Library library = FT_GLYPH( glyph )->library;
+ FT_Outline* source = &slot->outline;
+ FT_Outline* target = &glyph->outline;
+
+
+ /* check format in glyph slot */
+ if ( slot->format != FT_GLYPH_FORMAT_OUTLINE )
+ {
+ error = FT_THROW( Invalid_Glyph_Format );
+ goto Exit;
+ }
+
+ /* allocate new outline */
+ error = FT_Outline_New( library,
+ (FT_UInt)source->n_points,
+ source->n_contours,
+ &glyph->outline );
+ if ( error )
+ goto Exit;
+
+ FT_Outline_Copy( source, target );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_outline_glyph_done( FT_Glyph outline_glyph )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+
+
+ FT_Outline_Done( FT_GLYPH( glyph )->library, &glyph->outline );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_outline_glyph_copy( FT_Glyph outline_source,
+ FT_Glyph outline_target )
+ {
+ FT_OutlineGlyph source = (FT_OutlineGlyph)outline_source;
+ FT_OutlineGlyph target = (FT_OutlineGlyph)outline_target;
+ FT_Error error;
+ FT_Library library = FT_GLYPH( source )->library;
+
+
+ error = FT_Outline_New( library,
+ (FT_UInt)source->outline.n_points,
+ source->outline.n_contours,
+ &target->outline );
+ if ( !error )
+ FT_Outline_Copy( &source->outline, &target->outline );
+
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_outline_glyph_transform( FT_Glyph outline_glyph,
+ const FT_Matrix* matrix,
+ const FT_Vector* delta )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+
+
+ if ( matrix )
+ FT_Outline_Transform( &glyph->outline, matrix );
+
+ if ( delta )
+ FT_Outline_Translate( &glyph->outline, delta->x, delta->y );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_outline_glyph_bbox( FT_Glyph outline_glyph,
+ FT_BBox* bbox )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+
+
+ FT_Outline_Get_CBox( &glyph->outline, bbox );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_outline_glyph_prepare( FT_Glyph outline_glyph,
+ FT_GlyphSlot slot )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+
+
+ slot->format = FT_GLYPH_FORMAT_OUTLINE;
+ slot->outline = glyph->outline;
+ slot->outline.flags &= ~FT_OUTLINE_OWNER;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_DEFINE_GLYPH(
+ ft_outline_glyph_class,
+
+ sizeof ( FT_OutlineGlyphRec ),
+ FT_GLYPH_FORMAT_OUTLINE,
+
+ ft_outline_glyph_init, /* FT_Glyph_InitFunc glyph_init */
+ ft_outline_glyph_done, /* FT_Glyph_DoneFunc glyph_done */
+ ft_outline_glyph_copy, /* FT_Glyph_CopyFunc glyph_copy */
+ ft_outline_glyph_transform, /* FT_Glyph_TransformFunc glyph_transform */
+ ft_outline_glyph_bbox, /* FT_Glyph_GetBBoxFunc glyph_bbox */
+ ft_outline_glyph_prepare /* FT_Glyph_PrepareFunc glyph_prepare */
+ )
+
+
+#ifdef FT_CONFIG_OPTION_SVG
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** FT_SvgGlyph support ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_svg_glyph_init( FT_Glyph svg_glyph,
+ FT_GlyphSlot slot )
+ {
+ FT_ULong doc_length;
+ FT_SVG_Document document;
+ FT_SvgGlyph glyph = (FT_SvgGlyph)svg_glyph;
+
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory = FT_GLYPH( glyph )->library->memory;
+
+
+ if ( slot->format != FT_GLYPH_FORMAT_SVG )
+ {
+ error = FT_THROW( Invalid_Glyph_Format );
+ goto Exit;
+ }
+
+ if ( slot->other == NULL )
+ {
+ error = FT_THROW( Invalid_Slot_Handle );
+ goto Exit;
+ }
+
+ document = (FT_SVG_Document)slot->other;
+
+ if ( document->svg_document_length == 0 )
+ {
+ error = FT_THROW( Invalid_Slot_Handle );
+ goto Exit;
+ }
+
+ /* allocate a new document */
+ doc_length = document->svg_document_length;
+ if ( FT_QALLOC( glyph->svg_document, doc_length ) )
+ goto Exit;
+ glyph->svg_document_length = doc_length;
+
+ glyph->glyph_index = slot->glyph_index;
+
+ glyph->metrics = document->metrics;
+ glyph->units_per_EM = document->units_per_EM;
+
+ glyph->start_glyph_id = document->start_glyph_id;
+ glyph->end_glyph_id = document->end_glyph_id;
+
+ glyph->transform = document->transform;
+ glyph->delta = document->delta;
+
+ /* copy the document into glyph */
+ FT_MEM_COPY( glyph->svg_document, document->svg_document, doc_length );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_svg_glyph_done( FT_Glyph svg_glyph )
+ {
+ FT_SvgGlyph glyph = (FT_SvgGlyph)svg_glyph;
+ FT_Memory memory = svg_glyph->library->memory;
+
+
+ /* just free the memory */
+ FT_FREE( glyph->svg_document );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_svg_glyph_copy( FT_Glyph svg_source,
+ FT_Glyph svg_target )
+ {
+ FT_SvgGlyph source = (FT_SvgGlyph)svg_source;
+ FT_SvgGlyph target = (FT_SvgGlyph)svg_target;
+
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory = FT_GLYPH( source )->library->memory;
+
+
+ if ( svg_source->format != FT_GLYPH_FORMAT_SVG )
+ {
+ error = FT_THROW( Invalid_Glyph_Format );
+ goto Exit;
+ }
+
+ if ( source->svg_document_length == 0 )
+ {
+ error = FT_THROW( Invalid_Slot_Handle );
+ goto Exit;
+ }
+
+ target->glyph_index = source->glyph_index;
+
+ target->svg_document_length = source->svg_document_length;
+
+ target->metrics = source->metrics;
+ target->units_per_EM = source->units_per_EM;
+
+ target->start_glyph_id = source->start_glyph_id;
+ target->end_glyph_id = source->end_glyph_id;
+
+ target->transform = source->transform;
+ target->delta = source->delta;
+
+ /* allocate space for the SVG document */
+ if ( FT_QALLOC( target->svg_document, target->svg_document_length ) )
+ goto Exit;
+
+ /* copy the document */
+ FT_MEM_COPY( target->svg_document,
+ source->svg_document,
+ target->svg_document_length );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_svg_glyph_transform( FT_Glyph svg_glyph,
+ const FT_Matrix* _matrix,
+ const FT_Vector* _delta )
+ {
+ FT_SvgGlyph glyph = (FT_SvgGlyph)svg_glyph;
+ FT_Matrix* matrix = (FT_Matrix*)_matrix;
+ FT_Vector* delta = (FT_Vector*)_delta;
+
+ FT_Matrix tmp_matrix;
+ FT_Vector tmp_delta;
+
+ FT_Matrix a, b;
+ FT_Pos x, y;
+
+
+ if ( !matrix )
+ {
+ tmp_matrix.xx = 0x10000;
+ tmp_matrix.xy = 0;
+ tmp_matrix.yx = 0;
+ tmp_matrix.yy = 0x10000;
+
+ matrix = &tmp_matrix;
+ }
+
+ if ( !delta )
+ {
+ tmp_delta.x = 0;
+ tmp_delta.y = 0;
+
+ delta = &tmp_delta;
+ }
+
+ a = glyph->transform;
+ b = *matrix;
+ FT_Matrix_Multiply( &b, &a );
+
+ x = ADD_LONG( ADD_LONG( FT_MulFix( matrix->xx, glyph->delta.x ),
+ FT_MulFix( matrix->xy, glyph->delta.y ) ),
+ delta->x );
+ y = ADD_LONG( ADD_LONG( FT_MulFix( matrix->yx, glyph->delta.x ),
+ FT_MulFix( matrix->yy, glyph->delta.y ) ),
+ delta->y );
+
+ glyph->delta.x = x;
+ glyph->delta.y = y;
+
+ glyph->transform = a;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_svg_glyph_prepare( FT_Glyph svg_glyph,
+ FT_GlyphSlot slot )
+ {
+ FT_SvgGlyph glyph = (FT_SvgGlyph)svg_glyph;
+
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory = svg_glyph->library->memory;
+
+ FT_SVG_Document document = NULL;
+
+
+ if ( FT_NEW( document ) )
+ return error;
+
+ document->svg_document = glyph->svg_document;
+ document->svg_document_length = glyph->svg_document_length;
+
+ document->metrics = glyph->metrics;
+ document->units_per_EM = glyph->units_per_EM;
+
+ document->start_glyph_id = glyph->start_glyph_id;
+ document->end_glyph_id = glyph->end_glyph_id;
+
+ document->transform = glyph->transform;
+ document->delta = glyph->delta;
+
+ slot->format = FT_GLYPH_FORMAT_SVG;
+ slot->glyph_index = glyph->glyph_index;
+ slot->other = document;
+
+ return error;
+ }
+
+
+ FT_DEFINE_GLYPH(
+ ft_svg_glyph_class,
+
+ sizeof ( FT_SvgGlyphRec ),
+ FT_GLYPH_FORMAT_SVG,
+
+ ft_svg_glyph_init, /* FT_Glyph_InitFunc glyph_init */
+ ft_svg_glyph_done, /* FT_Glyph_DoneFunc glyph_done */
+ ft_svg_glyph_copy, /* FT_Glyph_CopyFunc glyph_copy */
+ ft_svg_glyph_transform, /* FT_Glyph_TransformFunc glyph_transform */
+ NULL, /* FT_Glyph_GetBBoxFunc glyph_bbox */
+ ft_svg_glyph_prepare /* FT_Glyph_PrepareFunc glyph_prepare */
+ )
+
+#endif /* FT_CONFIG_OPTION_SVG */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** FT_Glyph class and API ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_Error
+ ft_new_glyph( FT_Library library,
+ const FT_Glyph_Class* clazz,
+ FT_Glyph* aglyph )
+ {
+ FT_Memory memory = library->memory;
+ FT_Error error;
+ FT_Glyph glyph = NULL;
+
+
+ *aglyph = NULL;
+
+ if ( !FT_ALLOC( glyph, clazz->glyph_size ) )
+ {
+ glyph->library = library;
+ glyph->clazz = clazz;
+ glyph->format = clazz->glyph_format;
+
+ *aglyph = glyph;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_Copy( FT_Glyph source,
+ FT_Glyph *target )
+ {
+ FT_Glyph copy;
+ FT_Error error;
+ const FT_Glyph_Class* clazz;
+
+
+ /* check arguments */
+ if ( !target || !source || !source->clazz )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ *target = NULL;
+
+ if ( !source || !source->clazz )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ clazz = source->clazz;
+ error = ft_new_glyph( source->library, clazz, &copy );
+ if ( error )
+ goto Exit;
+
+ copy->advance = source->advance;
+ copy->format = source->format;
+
+ if ( clazz->glyph_copy )
+ error = clazz->glyph_copy( source, copy );
+
+ if ( error )
+ FT_Done_Glyph( copy );
+ else
+ *target = copy;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT( FT_Error )
+ FT_New_Glyph( FT_Library library,
+ FT_Glyph_Format format,
+ FT_Glyph *aglyph )
+ {
+ const FT_Glyph_Class* clazz = NULL;
+
+ if ( !library || !aglyph )
+ return FT_THROW( Invalid_Argument );
+
+ /* if it is a bitmap, that's easy :-) */
+ if ( format == FT_GLYPH_FORMAT_BITMAP )
+ clazz = &ft_bitmap_glyph_class;
+
+ /* if it is an outline */
+ else if ( format == FT_GLYPH_FORMAT_OUTLINE )
+ clazz = &ft_outline_glyph_class;
+
+#ifdef FT_CONFIG_OPTION_SVG
+ /* if it is an SVG glyph */
+ else if ( format == FT_GLYPH_FORMAT_SVG )
+ clazz = &ft_svg_glyph_class;
+#endif
+
+ else
+ {
+ /* try to find a renderer that supports the glyph image format */
+ FT_Renderer render = FT_Lookup_Renderer( library, format, 0 );
+
+
+ if ( render )
+ clazz = &render->glyph_class;
+ }
+
+ if ( !clazz )
+ return FT_THROW( Invalid_Glyph_Format );
+
+ /* create FT_Glyph object */
+ return ft_new_glyph( library, clazz, aglyph );
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Glyph( FT_GlyphSlot slot,
+ FT_Glyph *aglyph )
+ {
+ FT_Error error;
+ FT_Glyph glyph;
+
+
+ if ( !slot )
+ return FT_THROW( Invalid_Slot_Handle );
+
+ if ( !aglyph )
+ return FT_THROW( Invalid_Argument );
+
+ /* create FT_Glyph object */
+ error = FT_New_Glyph( slot->library, slot->format, &glyph );
+ if ( error )
+ goto Exit;
+
+ /* copy advance while converting 26.6 to 16.16 format */
+ if ( slot->advance.x >= 0x8000L * 64 ||
+ slot->advance.x <= -0x8000L * 64 )
+ {
+ FT_ERROR(( "FT_Get_Glyph: advance width too large\n" ));
+ error = FT_THROW( Invalid_Argument );
+ goto Exit2;
+ }
+ if ( slot->advance.y >= 0x8000L * 64 ||
+ slot->advance.y <= -0x8000L * 64 )
+ {
+ FT_ERROR(( "FT_Get_Glyph: advance height too large\n" ));
+ error = FT_THROW( Invalid_Argument );
+ goto Exit2;
+ }
+
+ glyph->advance.x = slot->advance.x * 1024;
+ glyph->advance.y = slot->advance.y * 1024;
+
+ /* now import the image from the glyph slot */
+ error = glyph->clazz->glyph_init( glyph, slot );
+
+ Exit2:
+ /* if an error occurred, destroy the glyph */
+ if ( error )
+ {
+ FT_Done_Glyph( glyph );
+ *aglyph = NULL;
+ }
+ else
+ *aglyph = glyph;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_Transform( FT_Glyph glyph,
+ const FT_Matrix* matrix,
+ const FT_Vector* delta )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( !glyph || !glyph->clazz )
+ error = FT_THROW( Invalid_Argument );
+ else
+ {
+ const FT_Glyph_Class* clazz = glyph->clazz;
+
+
+ if ( clazz->glyph_transform )
+ {
+ /* transform glyph image */
+ clazz->glyph_transform( glyph, matrix, delta );
+
+ /* transform advance vector */
+ if ( matrix )
+ FT_Vector_Transform( &glyph->advance, matrix );
+ }
+ else
+ error = FT_THROW( Invalid_Glyph_Format );
+ }
+ return error;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Glyph_Get_CBox( FT_Glyph glyph,
+ FT_UInt bbox_mode,
+ FT_BBox *acbox )
+ {
+ const FT_Glyph_Class* clazz;
+
+
+ if ( !acbox )
+ return;
+
+ acbox->xMin = acbox->yMin = acbox->xMax = acbox->yMax = 0;
+
+ if ( !glyph || !glyph->clazz )
+ return;
+
+ clazz = glyph->clazz;
+ if ( !clazz->glyph_bbox )
+ return;
+
+ /* retrieve bbox in 26.6 coordinates */
+ clazz->glyph_bbox( glyph, acbox );
+
+ /* perform grid fitting if needed */
+ if ( bbox_mode == FT_GLYPH_BBOX_GRIDFIT ||
+ bbox_mode == FT_GLYPH_BBOX_PIXELS )
+ {
+ acbox->xMin = FT_PIX_FLOOR( acbox->xMin );
+ acbox->yMin = FT_PIX_FLOOR( acbox->yMin );
+ acbox->xMax = FT_PIX_CEIL_LONG( acbox->xMax );
+ acbox->yMax = FT_PIX_CEIL_LONG( acbox->yMax );
+ }
+
+ /* convert to integer pixels if needed */
+ if ( bbox_mode == FT_GLYPH_BBOX_TRUNCATE ||
+ bbox_mode == FT_GLYPH_BBOX_PIXELS )
+ {
+ acbox->xMin >>= 6;
+ acbox->yMin >>= 6;
+ acbox->xMax >>= 6;
+ acbox->yMax >>= 6;
+ }
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_To_Bitmap( FT_Glyph* the_glyph,
+ FT_Render_Mode render_mode,
+ const FT_Vector* origin,
+ FT_Bool destroy )
+ {
+ FT_GlyphSlotRec dummy;
+ FT_GlyphSlot_InternalRec dummy_internal;
+ FT_Error error = FT_Err_Ok;
+ FT_Glyph b, glyph;
+ FT_BitmapGlyph bitmap = NULL;
+ const FT_Glyph_Class* clazz;
+
+ FT_Library library;
+
+
+ /* check argument */
+ if ( !the_glyph )
+ goto Bad;
+ glyph = *the_glyph;
+ if ( !glyph )
+ goto Bad;
+
+ clazz = glyph->clazz;
+ library = glyph->library;
+ if ( !library || !clazz )
+ goto Bad;
+
+ /* when called with a bitmap glyph, do nothing and return successfully */
+ if ( clazz == &ft_bitmap_glyph_class )
+ goto Exit;
+
+ if ( !clazz->glyph_prepare )
+ goto Bad;
+
+ /* we render the glyph into a glyph bitmap using a `dummy' glyph slot */
+ /* then calling FT_Render_Glyph_Internal() */
+
+ FT_ZERO( &dummy );
+ FT_ZERO( &dummy_internal );
+ dummy.internal = &dummy_internal;
+ dummy.library = library;
+ dummy.format = clazz->glyph_format;
+
+ /* create result bitmap glyph */
+ error = ft_new_glyph( library, &ft_bitmap_glyph_class, &b );
+ if ( error )
+ goto Exit;
+ bitmap = (FT_BitmapGlyph)b;
+
+#if 1
+ /* if `origin' is set, translate the glyph image */
+ if ( origin )
+ FT_Glyph_Transform( glyph, NULL, origin );
+#else
+ FT_UNUSED( origin );
+#endif
+
+ /* prepare dummy slot for rendering */
+ error = clazz->glyph_prepare( glyph, &dummy );
+ if ( !error )
+ error = FT_Render_Glyph_Internal( glyph->library, &dummy, render_mode );
+
+#ifdef FT_CONFIG_OPTION_SVG
+ if ( clazz == &ft_svg_glyph_class )
+ {
+ FT_Memory memory = library->memory;
+
+
+ FT_FREE( dummy.other );
+ }
+#endif
+
+#if 1
+ if ( !destroy && origin )
+ {
+ FT_Vector v;
+
+
+ v.x = -origin->x;
+ v.y = -origin->y;
+ FT_Glyph_Transform( glyph, NULL, &v );
+ }
+#endif
+
+ if ( error )
+ goto Exit;
+
+ /* in case of success, copy the bitmap to the glyph bitmap */
+ error = ft_bitmap_glyph_init( (FT_Glyph)bitmap, &dummy );
+ if ( error )
+ goto Exit;
+
+ /* copy advance */
+ bitmap->root.advance = glyph->advance;
+
+ if ( destroy )
+ FT_Done_Glyph( glyph );
+
+ *the_glyph = FT_GLYPH( bitmap );
+
+ Exit:
+ if ( error && bitmap )
+ FT_Done_Glyph( FT_GLYPH( bitmap ) );
+
+ return error;
+
+ Bad:
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Done_Glyph( FT_Glyph glyph )
+ {
+ if ( glyph )
+ {
+ FT_Memory memory = glyph->library->memory;
+ const FT_Glyph_Class* clazz = glyph->clazz;
+
+
+ if ( clazz->glyph_done )
+ clazz->glyph_done( glyph );
+
+ FT_FREE( glyph );
+ }
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftgxval.c b/modules/freetype2/src/base/ftgxval.c
new file mode 100644
index 0000000000..6b3c5d2484
--- /dev/null
+++ b/modules/freetype2/src/base/ftgxval.c
@@ -0,0 +1,141 @@
+/****************************************************************************
+ *
+ * ftgxval.c
+ *
+ * FreeType API for validating TrueTypeGX/AAT tables (body).
+ *
+ * Copyright (C) 2004-2023 by
+ * Masatake YAMATO, Redhat K.K,
+ * 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.
+ *
+ */
+
+/****************************************************************************
+ *
+ * gxvalid is derived from both gxlayout module and otvalid module.
+ * Development of gxlayout is supported by the Information-technology
+ * Promotion Agency(IPA), Japan.
+ *
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/services/svgxval.h>
+
+
+ /* documentation is in ftgxval.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_TrueTypeGX_Validate( FT_Face face,
+ FT_UInt validation_flags,
+ FT_Bytes tables[FT_VALIDATE_GX_LENGTH],
+ FT_UInt table_length )
+ {
+ FT_Service_GXvalidate service;
+ FT_Error error;
+
+
+ if ( !face )
+ {
+ error = FT_THROW( Invalid_Face_Handle );
+ goto Exit;
+ }
+
+ if ( !tables )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, service, GX_VALIDATE );
+
+ if ( service )
+ error = service->validate( face,
+ validation_flags,
+ tables,
+ table_length );
+ else
+ error = FT_THROW( Unimplemented_Feature );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_TrueTypeGX_Free( FT_Face face,
+ FT_Bytes table )
+ {
+ FT_Memory memory;
+
+
+ if ( !face )
+ return;
+
+ memory = FT_FACE_MEMORY( face );
+
+ FT_FREE( table );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_ClassicKern_Validate( FT_Face face,
+ FT_UInt validation_flags,
+ FT_Bytes *ckern_table )
+ {
+ FT_Service_CKERNvalidate service;
+ FT_Error error;
+
+
+ if ( !face )
+ {
+ error = FT_THROW( Invalid_Face_Handle );
+ goto Exit;
+ }
+
+ if ( !ckern_table )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, service, CLASSICKERN_VALIDATE );
+
+ if ( service )
+ error = service->validate( face,
+ validation_flags,
+ ckern_table );
+ else
+ error = FT_THROW( Unimplemented_Feature );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_ClassicKern_Free( FT_Face face,
+ FT_Bytes table )
+ {
+ FT_Memory memory;
+
+
+ if ( !face )
+ return;
+
+ memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( table );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/fthash.c b/modules/freetype2/src/base/fthash.c
new file mode 100644
index 0000000000..313bbbb4b2
--- /dev/null
+++ b/modules/freetype2/src/base/fthash.c
@@ -0,0 +1,338 @@
+/****************************************************************************
+ *
+ * fthash.c
+ *
+ * Hashing functions (body).
+ *
+ */
+
+/*
+ * Copyright 2000 Computing Research Labs, New Mexico State University
+ * Copyright 2001-2015
+ * Francesco Zappa Nardelli
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+ /**************************************************************************
+ *
+ * This file is based on code from bdf.c,v 1.22 2000/03/16 20:08:50
+ *
+ * taken from Mark Leisher's xmbdfed package
+ *
+ */
+
+
+#include <freetype/internal/fthash.h>
+#include <freetype/internal/ftmemory.h>
+
+
+#define INITIAL_HT_SIZE 241
+
+
+ static FT_ULong
+ hash_str_lookup( FT_Hashkey* key )
+ {
+ const char* kp = key->str;
+ FT_ULong res = 0;
+
+
+ /* Mocklisp hash function. */
+ while ( *kp )
+ res = ( res << 5 ) - res + (FT_ULong)*kp++;
+
+ return res;
+ }
+
+
+ static FT_ULong
+ hash_num_lookup( FT_Hashkey* key )
+ {
+ FT_ULong num = (FT_ULong)key->num;
+ FT_ULong res;
+
+
+ /* Mocklisp hash function. */
+ res = num & 0xFF;
+ res = ( res << 5 ) - res + ( ( num >> 8 ) & 0xFF );
+ res = ( res << 5 ) - res + ( ( num >> 16 ) & 0xFF );
+ res = ( res << 5 ) - res + ( ( num >> 24 ) & 0xFF );
+
+ return res;
+ }
+
+
+ static FT_Bool
+ hash_str_compare( FT_Hashkey* a,
+ FT_Hashkey* b )
+ {
+ if ( a->str[0] == b->str[0] &&
+ ft_strcmp( a->str, b->str ) == 0 )
+ return 1;
+
+ return 0;
+ }
+
+
+ static FT_Bool
+ hash_num_compare( FT_Hashkey* a,
+ FT_Hashkey* b )
+ {
+ if ( a->num == b->num )
+ return 1;
+
+ return 0;
+ }
+
+
+ static FT_Hashnode*
+ hash_bucket( FT_Hashkey key,
+ FT_Hash hash )
+ {
+ FT_ULong res = 0;
+ FT_Hashnode* bp = hash->table;
+ FT_Hashnode* ndp;
+
+
+ res = (hash->lookup)( &key );
+
+ ndp = bp + ( res % hash->size );
+ while ( *ndp )
+ {
+ if ( (hash->compare)( &(*ndp)->key, &key ) )
+ break;
+
+ ndp--;
+ if ( ndp < bp )
+ ndp = bp + ( hash->size - 1 );
+ }
+
+ return ndp;
+ }
+
+
+ static FT_Error
+ hash_rehash( FT_Hash hash,
+ FT_Memory memory )
+ {
+ FT_Hashnode* obp = hash->table;
+ FT_Hashnode* bp;
+ FT_Hashnode* nbp;
+
+ FT_UInt i, sz = hash->size;
+ FT_Error error = FT_Err_Ok;
+
+
+ hash->size <<= 1;
+ hash->limit = hash->size / 3;
+
+ if ( FT_NEW_ARRAY( hash->table, hash->size ) )
+ goto Exit;
+
+ for ( i = 0, bp = obp; i < sz; i++, bp++ )
+ {
+ if ( *bp )
+ {
+ nbp = hash_bucket( (*bp)->key, hash );
+ *nbp = *bp;
+ }
+ }
+
+ FT_FREE( obp );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ hash_init( FT_Hash hash,
+ FT_Bool is_num,
+ FT_Memory memory )
+ {
+ FT_UInt sz = INITIAL_HT_SIZE;
+ FT_Error error;
+
+
+ hash->size = sz;
+ hash->limit = sz / 3;
+ hash->used = 0;
+
+ if ( is_num )
+ {
+ hash->lookup = hash_num_lookup;
+ hash->compare = hash_num_compare;
+ }
+ else
+ {
+ hash->lookup = hash_str_lookup;
+ hash->compare = hash_str_compare;
+ }
+
+ FT_MEM_NEW_ARRAY( hash->table, sz );
+
+ return error;
+ }
+
+
+ FT_Error
+ ft_hash_str_init( FT_Hash hash,
+ FT_Memory memory )
+ {
+ return hash_init( hash, 0, memory );
+ }
+
+
+ FT_Error
+ ft_hash_num_init( FT_Hash hash,
+ FT_Memory memory )
+ {
+ return hash_init( hash, 1, memory );
+ }
+
+
+ void
+ ft_hash_str_free( FT_Hash hash,
+ FT_Memory memory )
+ {
+ if ( hash )
+ {
+ FT_UInt sz = hash->size;
+ FT_Hashnode* bp = hash->table;
+ FT_UInt i;
+
+
+ for ( i = 0; i < sz; i++, bp++ )
+ FT_FREE( *bp );
+
+ FT_FREE( hash->table );
+ }
+ }
+
+
+ /* `ft_hash_num_free' is the same as `ft_hash_str_free' */
+
+
+ static FT_Error
+ hash_insert( FT_Hashkey key,
+ size_t data,
+ FT_Hash hash,
+ FT_Memory memory )
+ {
+ FT_Hashnode nn;
+ FT_Hashnode* bp = hash_bucket( key, hash );
+ FT_Error error = FT_Err_Ok;
+
+
+ nn = *bp;
+ if ( !nn )
+ {
+ if ( FT_QNEW( nn ) )
+ goto Exit;
+ *bp = nn;
+
+ nn->key = key;
+ nn->data = data;
+
+ if ( hash->used >= hash->limit )
+ {
+ error = hash_rehash( hash, memory );
+ if ( error )
+ goto Exit;
+ }
+
+ hash->used++;
+ }
+ else
+ nn->data = data;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_Error
+ ft_hash_str_insert( const char* key,
+ size_t data,
+ FT_Hash hash,
+ FT_Memory memory )
+ {
+ FT_Hashkey hk;
+
+
+ hk.str = key;
+
+ return hash_insert( hk, data, hash, memory );
+ }
+
+
+ FT_Error
+ ft_hash_num_insert( FT_Int num,
+ size_t data,
+ FT_Hash hash,
+ FT_Memory memory )
+ {
+ FT_Hashkey hk;
+
+
+ hk.num = num;
+
+ return hash_insert( hk, data, hash, memory );
+ }
+
+
+ static size_t*
+ hash_lookup( FT_Hashkey key,
+ FT_Hash hash )
+ {
+ FT_Hashnode* np = hash_bucket( key, hash );
+
+
+ return (*np) ? &(*np)->data
+ : NULL;
+ }
+
+
+ size_t*
+ ft_hash_str_lookup( const char* key,
+ FT_Hash hash )
+ {
+ FT_Hashkey hk;
+
+
+ hk.str = key;
+
+ return hash_lookup( hk, hash );
+ }
+
+
+ size_t*
+ ft_hash_num_lookup( FT_Int num,
+ FT_Hash hash )
+ {
+ FT_Hashkey hk;
+
+
+ hk.num = num;
+
+ return hash_lookup( hk, hash );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftinit.c b/modules/freetype2/src/base/ftinit.c
new file mode 100644
index 0000000000..c9c71d24bf
--- /dev/null
+++ b/modules/freetype2/src/base/ftinit.c
@@ -0,0 +1,263 @@
+/****************************************************************************
+ *
+ * ftinit.c
+ *
+ * FreeType initialization layer (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 purpose of this file is to implement the following two
+ * functions:
+ *
+ * FT_Add_Default_Modules():
+ * This function is used to add the set of default modules to a
+ * fresh new library object. The set is taken from the header file
+ * `freetype/config/ftmodule.h'. See the document `FreeType 2.0
+ * Build System' for more information.
+ *
+ * FT_Init_FreeType():
+ * This function creates a system object for the current platform,
+ * builds a library out of it, then calls FT_Default_Drivers().
+ *
+ * Note that even if FT_Init_FreeType() uses the implementation of the
+ * system object defined at build time, client applications are still
+ * able to provide their own `ftsystem.c'.
+ *
+ */
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/ftmodapi.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 init
+
+
+#undef FT_USE_MODULE
+#ifdef __cplusplus
+#define FT_USE_MODULE( type, x ) extern "C" const type x;
+#else
+#define FT_USE_MODULE( type, x ) extern const type x;
+#endif
+
+#include FT_CONFIG_MODULES_H
+
+#undef FT_USE_MODULE
+#define FT_USE_MODULE( type, x ) (const FT_Module_Class*)&(x),
+
+ static
+ const FT_Module_Class* const ft_default_modules[] =
+ {
+#include FT_CONFIG_MODULES_H
+ 0
+ };
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Add_Default_Modules( FT_Library library )
+ {
+ FT_Error error;
+ const FT_Module_Class* const* cur;
+
+
+ /* GCC 4.6 warns the type difference:
+ * FT_Module_Class** != const FT_Module_Class* const*
+ */
+ cur = (const FT_Module_Class* const*)ft_default_modules;
+
+ /* test for valid `library' delayed to FT_Add_Module() */
+ while ( *cur )
+ {
+ error = FT_Add_Module( library, *cur );
+ /* notify errors, but don't stop */
+ if ( error )
+ FT_TRACE0(( "FT_Add_Default_Module:"
+ " Cannot install `%s', error = 0x%x\n",
+ (*cur)->module_name, error ));
+ cur++;
+ }
+ }
+
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+
+#define MAX_LENGTH 128
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Default_Properties( FT_Library library )
+ {
+ const char* env;
+ const char* p;
+ const char* q;
+
+ char module_name[MAX_LENGTH + 1];
+ char property_name[MAX_LENGTH + 1];
+ char property_value[MAX_LENGTH + 1];
+
+ int i;
+
+
+ env = ft_getenv( "FREETYPE_PROPERTIES" );
+ if ( !env )
+ return;
+
+ for ( p = env; *p; p++ )
+ {
+ /* skip leading whitespace and separators */
+ if ( *p == ' ' || *p == '\t' )
+ continue;
+
+ /* read module name, followed by `:' */
+ q = p;
+ for ( i = 0; i < MAX_LENGTH; i++ )
+ {
+ if ( !*p || *p == ':' )
+ break;
+ module_name[i] = *p++;
+ }
+ module_name[i] = '\0';
+
+ if ( !*p || *p != ':' || p == q )
+ break;
+
+ /* read property name, followed by `=' */
+ q = ++p;
+ for ( i = 0; i < MAX_LENGTH; i++ )
+ {
+ if ( !*p || *p == '=' )
+ break;
+ property_name[i] = *p++;
+ }
+ property_name[i] = '\0';
+
+ if ( !*p || *p != '=' || p == q )
+ break;
+
+ /* read property value, followed by whitespace (if any) */
+ q = ++p;
+ for ( i = 0; i < MAX_LENGTH; i++ )
+ {
+ if ( !*p || *p == ' ' || *p == '\t' )
+ break;
+ property_value[i] = *p++;
+ }
+ property_value[i] = '\0';
+
+ if ( !( *p == '\0' || *p == ' ' || *p == '\t' ) || p == q )
+ break;
+
+ /* we completely ignore errors */
+ ft_property_string_set( library,
+ module_name,
+ property_name,
+ property_value );
+
+ if ( !*p )
+ break;
+ }
+ }
+
+#else
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Default_Properties( FT_Library library )
+ {
+ FT_UNUSED( library );
+ }
+
+#endif
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Init_FreeType( FT_Library *alibrary )
+ {
+ FT_Error error;
+ FT_Memory memory;
+
+
+#ifdef FT_DEBUG_LOGGING
+ ft_logging_init();
+#endif
+
+ /* check of `alibrary' delayed to `FT_New_Library' */
+
+ /* First of all, allocate a new system object -- this function is part */
+ /* of the system-specific component, i.e. `ftsystem.c'. */
+
+ memory = FT_New_Memory();
+ if ( !memory )
+ {
+ FT_ERROR(( "FT_Init_FreeType: cannot find memory manager\n" ));
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+ /* build a library out of it, then fill it with the set of */
+ /* default drivers. */
+
+ error = FT_New_Library( memory, alibrary );
+ if ( error )
+ FT_Done_Memory( memory );
+ else
+ FT_Add_Default_Modules( *alibrary );
+
+ FT_Set_Default_Properties( *alibrary );
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_FreeType( FT_Library library )
+ {
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ memory = library->memory;
+
+ /* Discard the library object */
+ FT_Done_Library( library );
+
+ /* discard memory manager */
+ FT_Done_Memory( memory );
+
+#ifdef FT_DEBUG_LOGGING
+ ft_logging_deinit();
+#endif
+
+ return FT_Err_Ok;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftlcdfil.c b/modules/freetype2/src/base/ftlcdfil.c
new file mode 100644
index 0000000000..6c3fd66e0b
--- /dev/null
+++ b/modules/freetype2/src/base/ftlcdfil.c
@@ -0,0 +1,437 @@
+/****************************************************************************
+ *
+ * ftlcdfil.c
+ *
+ * FreeType API for color filtering of subpixel bitmap glyphs (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/ftlcdfil.h>
+#include <freetype/ftimage.h>
+#include <freetype/internal/ftobjs.h>
+
+
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+
+/* define USE_LEGACY to implement the legacy filter */
+#define USE_LEGACY
+
+#define FT_SHIFTCLAMP( x ) ( x >>= 8, (FT_Byte)( x > 255 ? 255 : x ) )
+
+
+ /* add padding according to filter weights */
+ FT_BASE_DEF( void )
+ ft_lcd_padding( FT_BBox* cbox,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode )
+ {
+ FT_Byte* lcd_weights;
+ FT_Bitmap_LcdFilterFunc lcd_filter_func;
+
+
+ /* Per-face LCD filtering takes priority if set up. */
+ if ( slot->face && slot->face->internal->lcd_filter_func )
+ {
+ lcd_weights = slot->face->internal->lcd_weights;
+ lcd_filter_func = slot->face->internal->lcd_filter_func;
+ }
+ else
+ {
+ lcd_weights = slot->library->lcd_weights;
+ lcd_filter_func = slot->library->lcd_filter_func;
+ }
+
+ if ( lcd_filter_func == ft_lcd_filter_fir )
+ {
+ if ( mode == FT_RENDER_MODE_LCD )
+ {
+ cbox->xMin -= lcd_weights[0] ? 43 :
+ lcd_weights[1] ? 22 : 0;
+ cbox->xMax += lcd_weights[4] ? 43 :
+ lcd_weights[3] ? 22 : 0;
+ }
+ else if ( mode == FT_RENDER_MODE_LCD_V )
+ {
+ cbox->yMin -= lcd_weights[0] ? 43 :
+ lcd_weights[1] ? 22 : 0;
+ cbox->yMax += lcd_weights[4] ? 43 :
+ lcd_weights[3] ? 22 : 0;
+ }
+ }
+ }
+
+
+ /* FIR filter used by the default and light filters */
+ FT_BASE_DEF( void )
+ ft_lcd_filter_fir( FT_Bitmap* bitmap,
+ FT_LcdFiveTapFilter weights )
+ {
+ FT_UInt width = (FT_UInt)bitmap->width;
+ FT_UInt height = (FT_UInt)bitmap->rows;
+ FT_Int pitch = bitmap->pitch;
+ FT_Byte* origin = bitmap->buffer;
+ FT_Byte mode = bitmap->pixel_mode;
+
+
+ /* take care of bitmap flow */
+ if ( pitch > 0 && height > 0 )
+ origin += pitch * (FT_Int)( height - 1 );
+
+ /* horizontal in-place FIR filter */
+ if ( mode == FT_PIXEL_MODE_LCD && width >= 2 )
+ {
+ FT_Byte* line = origin;
+
+
+ /* `fir' must be at least 32 bit wide, since the sum of */
+ /* the values in `weights' can exceed 0xFF */
+
+ for ( ; height > 0; height--, line -= pitch )
+ {
+ FT_UInt fir[5];
+ FT_UInt val, xx;
+
+
+ val = line[0];
+ fir[2] = weights[2] * val;
+ fir[3] = weights[3] * val;
+ fir[4] = weights[4] * val;
+
+ val = line[1];
+ fir[1] = fir[2] + weights[1] * val;
+ fir[2] = fir[3] + weights[2] * val;
+ fir[3] = fir[4] + weights[3] * val;
+ fir[4] = weights[4] * val;
+
+ for ( xx = 2; xx < width; xx++ )
+ {
+ val = line[xx];
+ fir[0] = fir[1] + weights[0] * val;
+ fir[1] = fir[2] + weights[1] * val;
+ fir[2] = fir[3] + weights[2] * val;
+ fir[3] = fir[4] + weights[3] * val;
+ fir[4] = weights[4] * val;
+
+ line[xx - 2] = FT_SHIFTCLAMP( fir[0] );
+ }
+
+ line[xx - 2] = FT_SHIFTCLAMP( fir[1] );
+ line[xx - 1] = FT_SHIFTCLAMP( fir[2] );
+ }
+ }
+
+ /* vertical in-place FIR filter */
+ else if ( mode == FT_PIXEL_MODE_LCD_V && height >= 2 )
+ {
+ FT_Byte* column = origin;
+
+
+ for ( ; width > 0; width--, column++ )
+ {
+ FT_Byte* col = column;
+ FT_UInt fir[5];
+ FT_UInt val, yy;
+
+
+ val = col[0];
+ fir[2] = weights[2] * val;
+ fir[3] = weights[3] * val;
+ fir[4] = weights[4] * val;
+ col -= pitch;
+
+ val = col[0];
+ fir[1] = fir[2] + weights[1] * val;
+ fir[2] = fir[3] + weights[2] * val;
+ fir[3] = fir[4] + weights[3] * val;
+ fir[4] = weights[4] * val;
+ col -= pitch;
+
+ for ( yy = 2; yy < height; yy++, col -= pitch )
+ {
+ val = col[0];
+ fir[0] = fir[1] + weights[0] * val;
+ fir[1] = fir[2] + weights[1] * val;
+ fir[2] = fir[3] + weights[2] * val;
+ fir[3] = fir[4] + weights[3] * val;
+ fir[4] = weights[4] * val;
+
+ col[pitch * 2] = FT_SHIFTCLAMP( fir[0] );
+ }
+
+ col[pitch * 2] = FT_SHIFTCLAMP( fir[1] );
+ col[pitch] = FT_SHIFTCLAMP( fir[2] );
+ }
+ }
+ }
+
+
+#ifdef USE_LEGACY
+
+ /* intra-pixel filter used by the legacy filter */
+ static void
+ _ft_lcd_filter_legacy( FT_Bitmap* bitmap,
+ FT_Byte* weights )
+ {
+ FT_UInt width = (FT_UInt)bitmap->width;
+ FT_UInt height = (FT_UInt)bitmap->rows;
+ FT_Int pitch = bitmap->pitch;
+ FT_Byte* origin = bitmap->buffer;
+ FT_Byte mode = bitmap->pixel_mode;
+
+ static const unsigned int filters[3][3] =
+ {
+ { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 },
+ { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 },
+ { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
+ };
+
+ FT_UNUSED( weights );
+
+
+ /* take care of bitmap flow */
+ if ( pitch > 0 && height > 0 )
+ origin += pitch * (FT_Int)( height - 1 );
+
+ /* horizontal in-place intra-pixel filter */
+ if ( mode == FT_PIXEL_MODE_LCD && width >= 3 )
+ {
+ FT_Byte* line = origin;
+
+
+ for ( ; height > 0; height--, line -= pitch )
+ {
+ FT_UInt xx;
+
+
+ for ( xx = 0; xx < width; xx += 3 )
+ {
+ FT_UInt r, g, b;
+ FT_UInt p;
+
+
+ p = line[xx];
+ r = filters[0][0] * p;
+ g = filters[0][1] * p;
+ b = filters[0][2] * p;
+
+ p = line[xx + 1];
+ r += filters[1][0] * p;
+ g += filters[1][1] * p;
+ b += filters[1][2] * p;
+
+ p = line[xx + 2];
+ r += filters[2][0] * p;
+ g += filters[2][1] * p;
+ b += filters[2][2] * p;
+
+ line[xx] = (FT_Byte)( r / 65536 );
+ line[xx + 1] = (FT_Byte)( g / 65536 );
+ line[xx + 2] = (FT_Byte)( b / 65536 );
+ }
+ }
+ }
+ else if ( mode == FT_PIXEL_MODE_LCD_V && height >= 3 )
+ {
+ FT_Byte* column = origin;
+
+
+ for ( ; width > 0; width--, column++ )
+ {
+ FT_Byte* col = column - 2 * pitch;
+
+
+ for ( ; height > 0; height -= 3, col -= 3 * pitch )
+ {
+ FT_UInt r, g, b;
+ FT_UInt p;
+
+
+ p = col[0];
+ r = filters[0][0] * p;
+ g = filters[0][1] * p;
+ b = filters[0][2] * p;
+
+ p = col[pitch];
+ r += filters[1][0] * p;
+ g += filters[1][1] * p;
+ b += filters[1][2] * p;
+
+ p = col[pitch * 2];
+ r += filters[2][0] * p;
+ g += filters[2][1] * p;
+ b += filters[2][2] * p;
+
+ col[0] = (FT_Byte)( r / 65536 );
+ col[pitch] = (FT_Byte)( g / 65536 );
+ col[pitch * 2] = (FT_Byte)( b / 65536 );
+ }
+ }
+ }
+ }
+
+#endif /* USE_LEGACY */
+
+
+ /* documentation in ftlcdfil.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Library_SetLcdFilterWeights( FT_Library library,
+ unsigned char *weights )
+ {
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !weights )
+ return FT_THROW( Invalid_Argument );
+
+ ft_memcpy( library->lcd_weights, weights, FT_LCD_FILTER_FIVE_TAPS );
+ library->lcd_filter_func = ft_lcd_filter_fir;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation in ftlcdfil.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Library_SetLcdFilter( FT_Library library,
+ FT_LcdFilter filter )
+ {
+ static const FT_LcdFiveTapFilter default_weights =
+ { 0x08, 0x4d, 0x56, 0x4d, 0x08 };
+ static const FT_LcdFiveTapFilter light_weights =
+ { 0x00, 0x55, 0x56, 0x55, 0x00 };
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ switch ( filter )
+ {
+ case FT_LCD_FILTER_NONE:
+ library->lcd_filter_func = NULL;
+ break;
+
+ case FT_LCD_FILTER_DEFAULT:
+ ft_memcpy( library->lcd_weights,
+ default_weights,
+ FT_LCD_FILTER_FIVE_TAPS );
+ library->lcd_filter_func = ft_lcd_filter_fir;
+ break;
+
+ case FT_LCD_FILTER_LIGHT:
+ ft_memcpy( library->lcd_weights,
+ light_weights,
+ FT_LCD_FILTER_FIVE_TAPS );
+ library->lcd_filter_func = ft_lcd_filter_fir;
+ break;
+
+#ifdef USE_LEGACY
+
+ case FT_LCD_FILTER_LEGACY:
+ case FT_LCD_FILTER_LEGACY1:
+ library->lcd_filter_func = _ft_lcd_filter_legacy;
+ break;
+
+#endif
+
+ default:
+ return FT_THROW( Invalid_Argument );
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Library_SetLcdGeometry( FT_Library library,
+ FT_Vector sub[3] )
+ {
+ FT_UNUSED( library );
+ FT_UNUSED( sub );
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
+ /* add padding to accommodate outline shifts */
+ FT_BASE_DEF( void )
+ ft_lcd_padding( FT_BBox* cbox,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode )
+ {
+ FT_Vector* sub = slot->library->lcd_geometry;
+
+ if ( mode == FT_RENDER_MODE_LCD )
+ {
+ cbox->xMin -= FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
+ cbox->xMax -= FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
+ cbox->yMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
+ cbox->yMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
+ }
+ else if ( mode == FT_RENDER_MODE_LCD_V )
+ {
+ cbox->xMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
+ cbox->xMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
+ cbox->yMin += FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
+ cbox->yMax += FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
+ }
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Library_SetLcdFilterWeights( FT_Library library,
+ unsigned char *weights )
+ {
+ FT_UNUSED( library );
+ FT_UNUSED( weights );
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Library_SetLcdFilter( FT_Library library,
+ FT_LcdFilter filter )
+ {
+ FT_UNUSED( library );
+ FT_UNUSED( filter );
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+
+ /* documentation in ftlcdfil.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Library_SetLcdGeometry( FT_Library library,
+ FT_Vector sub[3] )
+ {
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !sub )
+ return FT_THROW( Invalid_Argument );
+
+ ft_memcpy( library->lcd_geometry, sub, 3 * sizeof( FT_Vector ) );
+
+ return FT_Err_Ok;
+ }
+
+#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftmac.c b/modules/freetype2/src/base/ftmac.c
new file mode 100644
index 0000000000..de34e834f2
--- /dev/null
+++ b/modules/freetype2/src/base/ftmac.c
@@ -0,0 +1,1090 @@
+/****************************************************************************
+ *
+ * ftmac.c
+ *
+ * Mac FOND support. Written by just@letterror.com.
+ * Heavily modified by mpsuzuki, George Williams, and Sean McBride.
+ *
+ * This file is for Mac OS X only; see builds/mac/ftoldmac.c for
+ * classic platforms built by MPW.
+ *
+ * Copyright (C) 1996-2023 by
+ * Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+ /*
+ Notes
+
+ Mac suitcase files can (and often do!) contain multiple fonts. To
+ support this I use the face_index argument of FT_(Open|New)_Face()
+ functions, and pretend the suitcase file is a collection.
+
+ Warning: fbit and NFNT bitmap resources are not supported yet. In old
+ sfnt fonts, bitmap glyph data for each size is stored in each `NFNT'
+ resources instead of the `bdat' table in the sfnt resource. Therefore,
+ face->num_fixed_sizes is set to 0, because bitmap data in `NFNT'
+ resource is unavailable at present.
+
+ The Mac FOND support works roughly like this:
+
+ - Check whether the offered stream points to a Mac suitcase file. This
+ is done by checking the file type: it has to be 'FFIL' or 'tfil'. The
+ stream that gets passed to our init_face() routine is a stdio stream,
+ which isn't usable for us, since the FOND resources live in the
+ resource fork. So we just grab the stream->pathname field.
+
+ - Read the FOND resource into memory, then check whether there is a
+ TrueType font and/or(!) a Type 1 font available.
+
+ - If there is a Type 1 font available (as a separate `LWFN' file), read
+ its data into memory, massage it slightly so it becomes PFB data, wrap
+ it into a memory stream, load the Type 1 driver and delegate the rest
+ of the work to it by calling FT_Open_Face(). (XXX TODO: after this
+ has been done, the kerning data from the FOND resource should be
+ appended to the face: On the Mac there are usually no AFM files
+ available. However, this is tricky since we need to map Mac char
+ codes to ps glyph names to glyph ID's...)
+
+ - If there is a TrueType font (an `sfnt' resource), read it into memory,
+ wrap it into a memory stream, load the TrueType driver and delegate
+ the rest of the work to it, by calling FT_Open_Face().
+
+ - Some suitcase fonts (notably Onyx) might point the `LWFN' file to
+ itself, even though it doesn't contains `POST' resources. To handle
+ this special case without opening the file an extra time, we just
+ ignore errors from the `LWFN' and fallback to the `sfnt' if both are
+ available.
+ */
+
+
+#include <freetype/freetype.h>
+#include <freetype/tttags.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include "ftbase.h"
+
+
+#ifdef FT_MACINTOSH
+
+ /* This is for Mac OS X. Without redefinition, OS_INLINE */
+ /* expands to `static inline' which doesn't survive the */
+ /* -ansi compilation flag of GCC. */
+#if !HAVE_ANSI_OS_INLINE
+#undef OS_INLINE
+#define OS_INLINE static __inline__
+#endif
+
+ /* `configure' checks the availability of `ResourceIndex' strictly */
+ /* and sets HAVE_TYPE_RESOURCE_INDEX 1 or 0 always. If it is */
+ /* not set (e.g., a build without `configure'), the availability */
+ /* is guessed from the SDK version. */
+#ifndef HAVE_TYPE_RESOURCE_INDEX
+#if !defined( MAC_OS_X_VERSION_10_5 ) || \
+ ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 )
+#define HAVE_TYPE_RESOURCE_INDEX 0
+#else
+#define HAVE_TYPE_RESOURCE_INDEX 1
+#endif
+#endif /* !HAVE_TYPE_RESOURCE_INDEX */
+
+#if ( HAVE_TYPE_RESOURCE_INDEX == 0 )
+ typedef short ResourceIndex;
+#endif
+
+#include <CoreServices/CoreServices.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <sys/syslimits.h> /* PATH_MAX */
+
+ /* Don't want warnings about our own use of deprecated functions. */
+#define FT_DEPRECATED_ATTRIBUTE
+
+#include <freetype/ftmac.h>
+
+#ifndef kATSOptionFlagsUnRestrictedScope /* since Mac OS X 10.1 */
+#define kATSOptionFlagsUnRestrictedScope kATSOptionFlagsDefault
+#endif
+
+
+ /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
+ TrueType in case *both* are available (this is not common,
+ but it *is* possible). */
+#ifndef PREFER_LWFN
+#define PREFER_LWFN 1
+#endif
+
+
+ /* This function is deprecated because FSSpec is deprecated in Mac OS X */
+ FT_EXPORT_DEF( FT_Error )
+ FT_GetFile_From_Mac_Name( const char* fontName,
+ FSSpec* pathSpec,
+ FT_Long* face_index )
+ {
+ FT_UNUSED( fontName );
+ FT_UNUSED( pathSpec );
+ FT_UNUSED( face_index );
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+
+ /* Private function. */
+ /* The FSSpec type has been discouraged for a long time, */
+ /* unfortunately an FSRef replacement API for */
+ /* ATSFontGetFileSpecification() is only available in */
+ /* Mac OS X 10.5 and later. */
+ static OSStatus
+ FT_ATSFontGetFileReference( ATSFontRef ats_font_id,
+ FSRef* ats_font_ref )
+ {
+#if defined( MAC_OS_X_VERSION_10_5 ) && \
+ ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+
+ OSStatus err;
+
+ err = ATSFontGetFileReference( ats_font_id, ats_font_ref );
+
+ return err;
+#elif __LP64__ /* No 64bit Carbon API on legacy platforms */
+ FT_UNUSED( ats_font_id );
+ FT_UNUSED( ats_font_ref );
+
+
+ return fnfErr;
+#else /* 32bit Carbon API on legacy platforms */
+ OSStatus err;
+ FSSpec spec;
+
+
+ err = ATSFontGetFileSpecification( ats_font_id, &spec );
+ if ( noErr == err )
+ err = FSpMakeFSRef( &spec, ats_font_ref );
+
+ return err;
+#endif
+ }
+
+
+ static FT_Error
+ FT_GetFileRef_From_Mac_ATS_Name( const char* fontName,
+ FSRef* ats_font_ref,
+ FT_Long* face_index )
+ {
+ CFStringRef cf_fontName;
+ ATSFontRef ats_font_id;
+
+
+ *face_index = 0;
+
+ cf_fontName = CFStringCreateWithCString( NULL, fontName,
+ kCFStringEncodingMacRoman );
+ ats_font_id = ATSFontFindFromName( cf_fontName,
+ kATSOptionFlagsUnRestrictedScope );
+ CFRelease( cf_fontName );
+
+ if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
+ return FT_THROW( Unknown_File_Format );
+
+ if ( noErr != FT_ATSFontGetFileReference( ats_font_id, ats_font_ref ) )
+ return FT_THROW( Unknown_File_Format );
+
+ /* face_index calculation by searching preceding fontIDs */
+ /* with same FSRef */
+ {
+ ATSFontRef id2 = ats_font_id - 1;
+ FSRef ref2;
+
+
+ while ( id2 > 0 )
+ {
+ if ( noErr != FT_ATSFontGetFileReference( id2, &ref2 ) )
+ break;
+ if ( noErr != FSCompareFSRefs( ats_font_ref, &ref2 ) )
+ break;
+
+ id2 --;
+ }
+ *face_index = ats_font_id - ( id2 + 1 );
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_GetFilePath_From_Mac_ATS_Name( const char* fontName,
+ UInt8* path,
+ UInt32 maxPathSize,
+ FT_Long* face_index )
+ {
+ FSRef ref;
+ FT_Error err;
+
+
+ if ( !fontName || !face_index )
+ return FT_THROW( Invalid_Argument);
+
+ err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
+ if ( err )
+ return err;
+
+ if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) )
+ return FT_THROW( Unknown_File_Format );
+
+ return FT_Err_Ok;
+ }
+
+
+ /* This function is deprecated because FSSpec is deprecated in Mac OS X */
+ FT_EXPORT_DEF( FT_Error )
+ FT_GetFile_From_Mac_ATS_Name( const char* fontName,
+ FSSpec* pathSpec,
+ FT_Long* face_index )
+ {
+#if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \
+ ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) )
+ FT_UNUSED( fontName );
+ FT_UNUSED( pathSpec );
+ FT_UNUSED( face_index );
+
+ return FT_THROW( Unimplemented_Feature );
+#else
+ FSRef ref;
+ FT_Error err;
+
+
+ if ( !fontName || !face_index )
+ return FT_THROW( Invalid_Argument );
+
+ err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
+ if ( err )
+ return err;
+
+ if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL,
+ pathSpec, NULL ) )
+ return FT_THROW( Unknown_File_Format );
+
+ return FT_Err_Ok;
+#endif
+ }
+
+
+ static OSErr
+ FT_FSPathMakeRes( const UInt8* pathname,
+ ResFileRefNum* res )
+ {
+ OSErr err;
+ FSRef ref;
+
+
+ if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
+ return FT_THROW( Cannot_Open_Resource );
+
+ /* at present, no support for dfont format */
+ err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res );
+ if ( noErr == err )
+ return err;
+
+ /* fallback to original resource-fork font */
+ *res = FSOpenResFile( &ref, fsRdPerm );
+ err = ResError();
+
+ return err;
+ }
+
+
+ /* Return the file type for given pathname */
+ static OSType
+ get_file_type_from_path( const UInt8* pathname )
+ {
+ FSRef ref;
+ FSCatalogInfo info;
+
+
+ if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
+ return ( OSType ) 0;
+
+ if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info,
+ NULL, NULL, NULL ) )
+ return ( OSType ) 0;
+
+ return ( (FInfo *)( info.finderInfo ) )->fdType;
+ }
+
+
+ /* Given a PostScript font name, create the Macintosh LWFN file name. */
+ static void
+ create_lwfn_name( char* ps_name,
+ Str255 lwfn_file_name )
+ {
+ int max = 5, count = 0;
+ FT_Byte* p = lwfn_file_name;
+ FT_Byte* q = (FT_Byte*)ps_name;
+
+
+ lwfn_file_name[0] = 0;
+
+ while ( *q )
+ {
+ if ( ft_isupper( *q ) )
+ {
+ if ( count )
+ max = 3;
+ count = 0;
+ }
+ if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
+ {
+ *++p = *q;
+ lwfn_file_name[0]++;
+ count++;
+ }
+ q++;
+ }
+ }
+
+
+ static short
+ count_faces_sfnt( char* fond_data )
+ {
+ /* The count is 1 greater than the value in the FOND. */
+ /* Isn't that cute? :-) */
+
+ return EndianS16_BtoN( *( (short*)( fond_data +
+ sizeof ( FamRec ) ) ) ) + 1;
+ }
+
+
+ static short
+ count_faces_scalable( char* fond_data )
+ {
+ AsscEntry* assoc;
+ short i, face, face_all;
+
+
+ face_all = EndianS16_BtoN( *( (short *)( fond_data +
+ sizeof ( FamRec ) ) ) ) + 1;
+ assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
+ face = 0;
+
+ for ( i = 0; i < face_all; i++ )
+ {
+ if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
+ face++;
+ }
+ return face;
+ }
+
+
+ /* Look inside the FOND data, answer whether there should be an SFNT
+ resource, and answer the name of a possible LWFN Type 1 file.
+
+ Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
+ to load a face OTHER than the first one in the FOND!
+ */
+
+
+ static void
+ parse_fond( char* fond_data,
+ short* have_sfnt,
+ ResID* sfnt_id,
+ Str255 lwfn_file_name,
+ short face_index )
+ {
+ AsscEntry* assoc;
+ AsscEntry* base_assoc;
+ FamRec* fond;
+
+
+ *sfnt_id = 0;
+ *have_sfnt = 0;
+ lwfn_file_name[0] = 0;
+
+ fond = (FamRec*)fond_data;
+ assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
+ base_assoc = assoc;
+
+ /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */
+ if ( 47 < face_index )
+ return;
+
+ /* Let's do a little range checking before we get too excited here */
+ if ( face_index < count_faces_sfnt( fond_data ) )
+ {
+ assoc += face_index; /* add on the face_index! */
+
+ /* if the face at this index is not scalable,
+ fall back to the first one (old behavior) */
+ if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
+ {
+ *have_sfnt = 1;
+ *sfnt_id = EndianS16_BtoN( assoc->fontID );
+ }
+ else if ( base_assoc->fontSize == 0 )
+ {
+ *have_sfnt = 1;
+ *sfnt_id = EndianS16_BtoN( base_assoc->fontID );
+ }
+ }
+
+ if ( EndianS32_BtoN( fond->ffStylOff ) )
+ {
+ unsigned char* p = (unsigned char*)fond_data;
+ StyleTable* style;
+ unsigned short string_count;
+ char ps_name[256];
+ unsigned char* names[64];
+ int i;
+
+
+ p += EndianS32_BtoN( fond->ffStylOff );
+ style = (StyleTable*)p;
+ p += sizeof ( StyleTable );
+ string_count = EndianS16_BtoN( *(short*)(p) );
+ string_count = FT_MIN( 64, string_count );
+ p += sizeof ( short );
+
+ for ( i = 0; i < string_count; i++ )
+ {
+ names[i] = p;
+ p += names[i][0];
+ p++;
+ }
+
+ {
+ size_t ps_name_len = (size_t)names[0][0];
+
+
+ if ( ps_name_len != 0 )
+ {
+ ft_memcpy( ps_name, names[0] + 1, ps_name_len );
+ ps_name[ps_name_len] = 0;
+ }
+ if ( style->indexes[face_index] > 1 &&
+ style->indexes[face_index] <= string_count )
+ {
+ unsigned char* suffixes = names[style->indexes[face_index] - 1];
+
+
+ for ( i = 1; i <= suffixes[0]; i++ )
+ {
+ unsigned char* s;
+ size_t j = suffixes[i] - 1;
+
+
+ if ( j < string_count && ( s = names[j] ) != NULL )
+ {
+ size_t s_len = (size_t)s[0];
+
+
+ if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
+ {
+ ft_memcpy( ps_name + ps_name_len, s + 1, s_len );
+ ps_name_len += s_len;
+ ps_name[ps_name_len] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ create_lwfn_name( ps_name, lwfn_file_name );
+ }
+ }
+
+
+ static FT_Error
+ lookup_lwfn_by_fond( const UInt8* path_fond,
+ ConstStr255Param base_lwfn,
+ UInt8* path_lwfn,
+ size_t path_size )
+ {
+ FSRef ref, par_ref;
+ size_t dirname_len;
+
+
+ /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */
+ /* We should not extract parent directory by string manipulation. */
+
+ if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) )
+ return FT_THROW( Invalid_Argument );
+
+ if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
+ NULL, NULL, NULL, &par_ref ) )
+ return FT_THROW( Invalid_Argument );
+
+ if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) )
+ return FT_THROW( Invalid_Argument );
+
+ if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size )
+ return FT_THROW( Invalid_Argument );
+
+ /* now we have absolute dirname in path_lwfn */
+ ft_strcat( (char *)path_lwfn, "/" );
+ dirname_len = ft_strlen( (char *)path_lwfn );
+ ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 );
+ path_lwfn[dirname_len + base_lwfn[0]] = '\0';
+
+ if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) )
+ return FT_THROW( Cannot_Open_Resource );
+
+ if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
+ NULL, NULL, NULL, NULL ) )
+ return FT_THROW( Cannot_Open_Resource );
+
+ return FT_Err_Ok;
+ }
+
+
+ static short
+ count_faces( Handle fond,
+ const UInt8* pathname )
+ {
+ ResID sfnt_id;
+ short have_sfnt, have_lwfn;
+ Str255 lwfn_file_name;
+ UInt8 buff[PATH_MAX];
+ FT_Error err;
+ short num_faces;
+
+
+ have_sfnt = have_lwfn = 0;
+
+ parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
+
+ if ( lwfn_file_name[0] )
+ {
+ err = lookup_lwfn_by_fond( pathname, lwfn_file_name,
+ buff, sizeof ( buff ) );
+ if ( !err )
+ have_lwfn = 1;
+ }
+
+ if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
+ num_faces = 1;
+ else
+ num_faces = count_faces_scalable( *fond );
+
+ return num_faces;
+ }
+
+
+ /* Read Type 1 data from the POST resources inside the LWFN file,
+ return a PFB buffer. This is somewhat convoluted because the FT2
+ PFB parser wants the ASCII header as one chunk, and the LWFN
+ chunks are often not organized that way, so we glue chunks
+ of the same type together. */
+ static FT_Error
+ read_lwfn( FT_Memory memory,
+ ResFileRefNum res,
+ FT_Byte** pfb_data,
+ FT_ULong* size )
+ {
+ FT_Error error = FT_Err_Ok;
+ ResID res_id;
+ unsigned char *buffer, *p, *size_p = NULL;
+ FT_ULong total_size = 0;
+ FT_ULong old_total_size = 0;
+ FT_ULong post_size, pfb_chunk_size;
+ Handle post_data;
+ char code, last_code;
+
+
+ UseResFile( res );
+
+ /* First pass: load all POST resources, and determine the size of */
+ /* the output buffer. */
+ res_id = 501;
+ last_code = -1;
+
+ for (;;)
+ {
+ post_data = Get1Resource( TTAG_POST, res_id++ );
+ if ( !post_data )
+ break; /* we are done */
+
+ code = (*post_data)[0];
+
+ if ( code != last_code )
+ {
+ if ( code == 5 )
+ total_size += 2; /* just the end code */
+ else
+ total_size += 6; /* code + 4 bytes chunk length */
+ }
+
+ total_size += (FT_ULong)GetHandleSize( post_data ) - 2;
+ last_code = code;
+
+ /* detect resource fork overflow */
+ if ( FT_MAC_RFORK_MAX_LEN < total_size )
+ {
+ error = FT_THROW( Array_Too_Large );
+ goto Error;
+ }
+
+ old_total_size = total_size;
+ }
+
+ if ( FT_QALLOC( buffer, (FT_Long)total_size ) )
+ goto Error;
+
+ /* Second pass: append all POST data to the buffer, add PFB fields. */
+ /* Glue all consecutive chunks of the same type together. */
+ p = buffer;
+ res_id = 501;
+ last_code = -1;
+ pfb_chunk_size = 0;
+
+ for (;;)
+ {
+ post_data = Get1Resource( TTAG_POST, res_id++ );
+ if ( !post_data )
+ break; /* we are done */
+
+ post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
+ code = (*post_data)[0];
+
+ if ( code != last_code )
+ {
+ if ( last_code != -1 )
+ {
+ /* we are done adding a chunk, fill in the size field */
+ if ( size_p )
+ {
+ *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF );
+ *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF );
+ *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
+ *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
+ }
+ pfb_chunk_size = 0;
+ }
+
+ *p++ = 0x80;
+ if ( code == 5 )
+ *p++ = 0x03; /* the end */
+ else if ( code == 2 )
+ *p++ = 0x02; /* binary segment */
+ else
+ *p++ = 0x01; /* ASCII segment */
+
+ if ( code != 5 )
+ {
+ size_p = p; /* save for later */
+ p += 4; /* make space for size field */
+ }
+ }
+
+ ft_memcpy( p, *post_data + 2, post_size );
+ pfb_chunk_size += post_size;
+ p += post_size;
+ last_code = code;
+ }
+
+ *pfb_data = buffer;
+ *size = total_size;
+
+ Error:
+ CloseResFile( res );
+ return error;
+ }
+
+
+ /* Create a new FT_Face from a file path to an LWFN file. */
+ static FT_Error
+ FT_New_Face_From_LWFN( FT_Library library,
+ const UInt8* pathname,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ FT_Byte* pfb_data;
+ FT_ULong pfb_size;
+ FT_Error error;
+ ResFileRefNum res;
+
+
+ if ( noErr != FT_FSPathMakeRes( pathname, &res ) )
+ return FT_THROW( Cannot_Open_Resource );
+
+ pfb_data = NULL;
+ pfb_size = 0;
+ error = read_lwfn( library->memory, res, &pfb_data, &pfb_size );
+ CloseResFile( res ); /* PFB is already loaded, useless anymore */
+ if ( error )
+ return error;
+
+ return open_face_from_buffer( library,
+ pfb_data,
+ pfb_size,
+ face_index,
+ "type1",
+ aface );
+ }
+
+
+ /* Create a new FT_Face from an SFNT resource, specified by res ID. */
+ static FT_Error
+ FT_New_Face_From_SFNT( FT_Library library,
+ ResID sfnt_id,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ Handle sfnt = NULL;
+ FT_Byte* sfnt_data;
+ size_t sfnt_size;
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory = library->memory;
+ int is_cff, is_sfnt_ps;
+
+
+ sfnt = GetResource( TTAG_sfnt, sfnt_id );
+ if ( !sfnt )
+ return FT_THROW( Invalid_Handle );
+
+ sfnt_size = (FT_ULong)GetHandleSize( sfnt );
+
+ /* detect resource fork overflow */
+ if ( FT_MAC_RFORK_MAX_LEN < sfnt_size )
+ return FT_THROW( Array_Too_Large );
+
+ if ( FT_QALLOC( sfnt_data, (FT_Long)sfnt_size ) )
+ {
+ ReleaseResource( sfnt );
+ return error;
+ }
+
+ ft_memcpy( sfnt_data, *sfnt, sfnt_size );
+ ReleaseResource( sfnt );
+
+ is_cff = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 );
+ is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 );
+
+ if ( is_sfnt_ps )
+ {
+ FT_Stream stream;
+
+
+ if ( FT_NEW( stream ) )
+ goto Try_OpenType;
+
+ FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size );
+ if ( !open_face_PS_from_sfnt_stream( library,
+ stream,
+ face_index,
+ 0, NULL,
+ aface ) )
+ {
+ FT_Stream_Close( stream );
+ FT_FREE( stream );
+ FT_FREE( sfnt_data );
+ goto Exit;
+ }
+
+ FT_FREE( stream );
+ }
+ Try_OpenType:
+ error = open_face_from_buffer( library,
+ sfnt_data,
+ sfnt_size,
+ face_index,
+ is_cff ? "cff" : "truetype",
+ aface );
+ Exit:
+ return error;
+ }
+
+
+ /* Create a new FT_Face from a file path to a suitcase file. */
+ static FT_Error
+ FT_New_Face_From_Suitcase( FT_Library library,
+ const UInt8* pathname,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ FT_Error error = FT_ERR( Cannot_Open_Resource );
+ ResFileRefNum res_ref;
+ ResourceIndex res_index;
+ Handle fond;
+ short num_faces_in_res;
+
+
+ if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
+ return FT_THROW( Cannot_Open_Resource );
+
+ UseResFile( res_ref );
+ if ( ResError() )
+ return FT_THROW( Cannot_Open_Resource );
+
+ num_faces_in_res = 0;
+ for ( res_index = 1; ; res_index++ )
+ {
+ short num_faces_in_fond;
+
+
+ fond = Get1IndResource( TTAG_FOND, res_index );
+ if ( ResError() )
+ break;
+
+ num_faces_in_fond = count_faces( fond, pathname );
+ num_faces_in_res += num_faces_in_fond;
+
+ if ( 0 <= face_index && face_index < num_faces_in_fond && error )
+ error = FT_New_Face_From_FOND( library, fond, face_index, aface );
+
+ face_index -= num_faces_in_fond;
+ }
+
+ CloseResFile( res_ref );
+ if ( !error && aface && *aface )
+ (*aface)->num_faces = num_faces_in_res;
+ return error;
+ }
+
+
+ /* documentation is in ftmac.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face_From_FOND( FT_Library library,
+ Handle fond,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ short have_sfnt, have_lwfn = 0;
+ ResID sfnt_id, fond_id;
+ OSType fond_type;
+ Str255 fond_name;
+ Str255 lwfn_file_name;
+ UInt8 path_lwfn[PATH_MAX];
+ OSErr err;
+ FT_Error error = FT_Err_Ok;
+
+
+ /* check of `library' and `aface' delayed to `FT_New_Face_From_XXX' */
+
+ GetResInfo( fond, &fond_id, &fond_type, fond_name );
+ if ( ResError() != noErr || fond_type != TTAG_FOND )
+ return FT_THROW( Invalid_File_Format );
+
+ parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
+
+ if ( lwfn_file_name[0] )
+ {
+ ResFileRefNum res;
+
+
+ res = HomeResFile( fond );
+ if ( noErr != ResError() )
+ goto found_no_lwfn_file;
+
+ {
+ UInt8 path_fond[PATH_MAX];
+ FSRef ref;
+
+
+ err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
+ NULL, NULL, NULL, &ref, NULL );
+ if ( noErr != err )
+ goto found_no_lwfn_file;
+
+ err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
+ if ( noErr != err )
+ goto found_no_lwfn_file;
+
+ error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
+ path_lwfn, sizeof ( path_lwfn ) );
+ if ( !error )
+ have_lwfn = 1;
+ }
+ }
+
+ if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
+ error = FT_New_Face_From_LWFN( library,
+ path_lwfn,
+ face_index,
+ aface );
+ else
+ error = FT_THROW( Unknown_File_Format );
+
+ found_no_lwfn_file:
+ if ( have_sfnt && error )
+ error = FT_New_Face_From_SFNT( library,
+ sfnt_id,
+ face_index,
+ aface );
+
+ return error;
+ }
+
+
+ /* Common function to load a new FT_Face from a resource file. */
+ static FT_Error
+ FT_New_Face_From_Resource( FT_Library library,
+ const UInt8* pathname,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ OSType file_type;
+ FT_Error error;
+
+
+ /* LWFN is a (very) specific file format, check for it explicitly */
+ file_type = get_file_type_from_path( pathname );
+ if ( file_type == TTAG_LWFN )
+ return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
+
+ /* Otherwise the file type doesn't matter (there are more than */
+ /* `FFIL' and `tfil'). Just try opening it as a font suitcase; */
+ /* if it works, fine. */
+
+ error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
+ if ( error )
+ {
+ /* let it fall through to normal loader (.ttf, .otf, etc.); */
+ /* we signal this by returning no error and no FT_Face */
+ *aface = NULL;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * FT_New_Face
+ *
+ * @Description:
+ * This is the Mac-specific implementation of FT_New_Face. In
+ * addition to the standard FT_New_Face() functionality, it also
+ * accepts pathnames to Mac suitcase files. For further
+ * documentation see the original FT_New_Face() in freetype.h.
+ */
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face( FT_Library library,
+ const char* pathname,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ FT_Open_Args args;
+ FT_Error error;
+
+
+ /* test for valid `library' and `aface' delayed to FT_Open_Face() */
+ if ( !pathname )
+ return FT_THROW( Invalid_Argument );
+
+ *aface = NULL;
+
+ /* try resourcefork based font: LWFN, FFIL */
+ error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
+ face_index, aface );
+ if ( error || *aface )
+ return error;
+
+ /* let it fall through to normal loader (.ttf, .otf, etc.) */
+ args.flags = FT_OPEN_PATHNAME;
+ args.pathname = (char*)pathname;
+
+ return FT_Open_Face( library, &args, face_index, aface );
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * FT_New_Face_From_FSRef
+ *
+ * @Description:
+ * FT_New_Face_From_FSRef is identical to FT_New_Face except it
+ * accepts an FSRef instead of a path.
+ *
+ * This function is deprecated because Carbon data types (FSRef)
+ * are not cross-platform, and thus not suitable for the FreeType API.
+ */
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face_From_FSRef( FT_Library library,
+ const FSRef* ref,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ FT_Error error;
+ FT_Open_Args args;
+
+ OSErr err;
+ UInt8 pathname[PATH_MAX];
+
+
+ /* check of `library' and `aface' delayed to */
+ /* `FT_New_Face_From_Resource' */
+
+ if ( !ref )
+ return FT_THROW( Invalid_Argument );
+
+ err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
+ if ( err )
+ error = FT_THROW( Cannot_Open_Resource );
+
+ error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
+ if ( error || *aface )
+ return error;
+
+ /* fallback to datafork font */
+ args.flags = FT_OPEN_PATHNAME;
+ args.pathname = (char*)pathname;
+ return FT_Open_Face( library, &args, face_index, aface );
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * FT_New_Face_From_FSSpec
+ *
+ * @Description:
+ * FT_New_Face_From_FSSpec is identical to FT_New_Face except it
+ * accepts an FSSpec instead of a path.
+ *
+ * This function is deprecated because FSSpec is deprecated in Mac OS X
+ */
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face_From_FSSpec( FT_Library library,
+ const FSSpec* spec,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+#if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \
+ ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) )
+ FT_UNUSED( library );
+ FT_UNUSED( spec );
+ FT_UNUSED( face_index );
+ FT_UNUSED( aface );
+
+ return FT_THROW( Unimplemented_Feature );
+#else
+ FSRef ref;
+
+
+ /* check of `library' and `aface' delayed to `FT_New_Face_From_FSRef' */
+
+ if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
+ return FT_THROW( Invalid_Argument );
+ else
+ return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
+#endif
+ }
+
+#else /* !FT_MACINTOSH */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _ft_mac_dummy;
+
+#endif /* !FT_MACINTOSH */
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftmm.c b/modules/freetype2/src/base/ftmm.c
new file mode 100644
index 0000000000..a2b4bd03d7
--- /dev/null
+++ b/modules/freetype2/src/base/ftmm.c
@@ -0,0 +1,568 @@
+/****************************************************************************
+ *
+ * ftmm.c
+ *
+ * Multiple Master font support (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/ftmm.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/services/svmm.h>
+#include <freetype/internal/services/svmetric.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 mm
+
+
+ static FT_Error
+ ft_face_get_mm_service( FT_Face face,
+ FT_Service_MultiMasters *aservice )
+ {
+ FT_Error error;
+
+
+ *aservice = NULL;
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ error = FT_ERR( Invalid_Argument );
+
+ if ( FT_HAS_MULTIPLE_MASTERS( face ) )
+ {
+ FT_FACE_LOOKUP_SERVICE( face,
+ *aservice,
+ MULTI_MASTERS );
+
+ if ( *aservice )
+ error = FT_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ ft_face_get_mvar_service( FT_Face face,
+ FT_Service_MetricsVariations *aservice )
+ {
+ FT_Error error;
+
+
+ *aservice = NULL;
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ error = FT_ERR( Invalid_Argument );
+
+ if ( FT_HAS_MULTIPLE_MASTERS( face ) )
+ {
+ FT_FACE_LOOKUP_SERVICE( face,
+ *aservice,
+ METRICS_VARIATIONS );
+
+ if ( *aservice )
+ error = FT_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Multi_Master( FT_Face face,
+ FT_Multi_Master *amaster )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( !amaster )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service->get_mm )
+ error = service->get_mm( face, amaster );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_MM_Var( FT_Face face,
+ FT_MM_Var* *amaster )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( !amaster )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service->get_mm_var )
+ error = service->get_mm_var( face, amaster );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_MM_Var( FT_Library library,
+ FT_MM_Var* amaster )
+ {
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ memory = library->memory;
+ FT_FREE( amaster );
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_MM_Design_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Long* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( num_coords && !coords )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service->set_mm_design )
+ error = service->set_mm_design( face, num_coords, coords );
+ }
+
+ /* enforce recomputation of auto-hinting data */
+ if ( !error && face->autohint.finalizer )
+ {
+ face->autohint.finalizer( face->autohint.data );
+ face->autohint.data = NULL;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_MM_WeightVector( FT_Face face,
+ FT_UInt len,
+ FT_Fixed* weightvector )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( len && !weightvector )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service->set_mm_weightvector )
+ error = service->set_mm_weightvector( face, len, weightvector );
+ }
+
+ /* enforce recomputation of auto-hinting data */
+ if ( !error && face->autohint.finalizer )
+ {
+ face->autohint.finalizer( face->autohint.data );
+ face->autohint.data = NULL;
+ }
+
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_MM_WeightVector( FT_Face face,
+ FT_UInt* len,
+ FT_Fixed* weightvector )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( len && !weightvector )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service->get_mm_weightvector )
+ error = service->get_mm_weightvector( face, len, weightvector );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Var_Design_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service_mm = NULL;
+ FT_Service_MetricsVariations service_mvar = NULL;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( num_coords && !coords )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service_mm );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service_mm->set_var_design )
+ error = service_mm->set_var_design( face, num_coords, coords );
+
+ /* internal error code -1 means `no change'; we can exit immediately */
+ if ( error == -1 )
+ return FT_Err_Ok;
+ }
+
+ if ( !error )
+ {
+ (void)ft_face_get_mvar_service( face, &service_mvar );
+
+ if ( service_mvar && service_mvar->metrics_adjust )
+ service_mvar->metrics_adjust( face );
+ }
+
+ /* enforce recomputation of auto-hinting data */
+ if ( !error && face->autohint.finalizer )
+ {
+ face->autohint.finalizer( face->autohint.data );
+ face->autohint.data = NULL;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Var_Design_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( !coords )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service->get_var_design )
+ error = service->get_var_design( face, num_coords, coords );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_MM_Blend_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service_mm = NULL;
+ FT_Service_MetricsVariations service_mvar = NULL;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( num_coords && !coords )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service_mm );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service_mm->set_mm_blend )
+ error = service_mm->set_mm_blend( face, num_coords, coords );
+
+ /* internal error code -1 means `no change'; we can exit immediately */
+ if ( error == -1 )
+ return FT_Err_Ok;
+ }
+
+ if ( !error )
+ {
+ (void)ft_face_get_mvar_service( face, &service_mvar );
+
+ if ( service_mvar && service_mvar->metrics_adjust )
+ service_mvar->metrics_adjust( face );
+ }
+
+ /* enforce recomputation of auto-hinting data */
+ if ( !error && face->autohint.finalizer )
+ {
+ face->autohint.finalizer( face->autohint.data );
+ face->autohint.data = NULL;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ /* This is exactly the same as the previous function. It exists for */
+ /* orthogonality. */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Var_Blend_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service_mm = NULL;
+ FT_Service_MetricsVariations service_mvar = NULL;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( num_coords && !coords )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service_mm );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service_mm->set_mm_blend )
+ error = service_mm->set_mm_blend( face, num_coords, coords );
+
+ /* internal error code -1 means `no change'; we can exit immediately */
+ if ( error == -1 )
+ return FT_Err_Ok;
+ }
+
+ if ( !error )
+ {
+ (void)ft_face_get_mvar_service( face, &service_mvar );
+
+ if ( service_mvar && service_mvar->metrics_adjust )
+ service_mvar->metrics_adjust( face );
+ }
+
+ /* enforce recomputation of auto-hinting data */
+ if ( !error && face->autohint.finalizer )
+ {
+ face->autohint.finalizer( face->autohint.data );
+ face->autohint.data = NULL;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_MM_Blend_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( !coords )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service->get_mm_blend )
+ error = service->get_mm_blend( face, num_coords, coords );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ /* This is exactly the same as the previous function. It exists for */
+ /* orthogonality. */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Var_Blend_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ if ( !coords )
+ return FT_THROW( Invalid_Argument );
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service->get_mm_blend )
+ error = service->get_mm_blend( face, num_coords, coords );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Var_Axis_Flags( FT_MM_Var* master,
+ FT_UInt axis_index,
+ FT_UInt* flags )
+ {
+ FT_UShort* axis_flags;
+
+
+ if ( !master || !flags )
+ return FT_THROW( Invalid_Argument );
+
+ if ( axis_index >= master->num_axis )
+ return FT_THROW( Invalid_Argument );
+
+ /* the axis flags array immediately follows the data of `master' */
+ axis_flags = (FT_UShort*)&( master[1] );
+ *flags = axis_flags[axis_index];
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Named_Instance( FT_Face face,
+ FT_UInt instance_index )
+ {
+ FT_Error error;
+
+ FT_Service_MultiMasters service_mm = NULL;
+ FT_Service_MetricsVariations service_mvar = NULL;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ error = ft_face_get_mm_service( face, &service_mm );
+ if ( !error )
+ {
+ error = FT_ERR( Invalid_Argument );
+ if ( service_mm->set_instance )
+ error = service_mm->set_instance( face, instance_index );
+ }
+
+ if ( !error )
+ {
+ (void)ft_face_get_mvar_service( face, &service_mvar );
+
+ if ( service_mvar && service_mvar->metrics_adjust )
+ service_mvar->metrics_adjust( face );
+ }
+
+ /* enforce recomputation of auto-hinting data */
+ if ( !error && face->autohint.finalizer )
+ {
+ face->autohint.finalizer( face->autohint.data );
+ face->autohint.data = NULL;
+ }
+
+ if ( !error )
+ {
+ face->face_index = ( instance_index << 16 ) |
+ ( face->face_index & 0xFFFFL );
+ face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftobjs.c b/modules/freetype2/src/base/ftobjs.c
new file mode 100644
index 0000000000..ad6ef0ae16
--- /dev/null
+++ b/modules/freetype2/src/base/ftobjs.c
@@ -0,0 +1,5906 @@
+/****************************************************************************
+ *
+ * ftobjs.c
+ *
+ * The FreeType private base classes (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/ftlist.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftfntfmt.h>
+#include <freetype/otsvg.h>
+
+#include <freetype/internal/ftvalid.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftrfork.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/sfnt.h> /* for SFNT_Load_Table_Func */
+#include <freetype/internal/psaux.h> /* for PS_Driver */
+#include <freetype/internal/svginterface.h>
+
+#include <freetype/tttables.h>
+#include <freetype/tttags.h>
+#include <freetype/ttnameid.h>
+
+#include <freetype/internal/services/svprop.h>
+#include <freetype/internal/services/svsfnt.h>
+#include <freetype/internal/services/svpostnm.h>
+#include <freetype/internal/services/svgldict.h>
+#include <freetype/internal/services/svttcmap.h>
+#include <freetype/internal/services/svkern.h>
+#include <freetype/internal/services/svtteng.h>
+
+#include <freetype/ftdriver.h>
+
+#ifdef FT_CONFIG_OPTION_MAC_FONTS
+#include "ftbase.h"
+#endif
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+#include <freetype/ftbitmap.h>
+
+#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */
+ /* We disable the warning `conversion from XXX to YYY, */
+ /* possible loss of data' in order to compile cleanly with */
+ /* the maximum level of warnings: `md5.c' is non-FreeType */
+ /* code, and it gets used during development builds only. */
+#pragma warning( push )
+#pragma warning( disable : 4244 )
+#endif /* _MSC_VER */
+
+ /* It's easiest to include `md5.c' directly. However, since OpenSSL */
+ /* also provides the same functions, there might be conflicts if */
+ /* both FreeType and OpenSSL are built as static libraries. For */
+ /* this reason, we put the MD5 stuff into the `FT_' namespace. */
+#define MD5_u32plus FT_MD5_u32plus
+#define MD5_CTX FT_MD5_CTX
+#define MD5_Init FT_MD5_Init
+#define MD5_Update FT_MD5_Update
+#define MD5_Final FT_MD5_Final
+
+#undef HAVE_OPENSSL
+
+#include "md5.c"
+
+#if defined( _MSC_VER )
+#pragma warning( pop )
+#endif
+
+ /* This array must stay in sync with the @FT_Pixel_Mode enumeration */
+ /* (in file `ftimage.h`). */
+
+ static const char* const pixel_modes[] =
+ {
+ "none",
+ "monochrome bitmap",
+ "gray 8-bit bitmap",
+ "gray 2-bit bitmap",
+ "gray 4-bit bitmap",
+ "LCD 8-bit bitmap",
+ "vertical LCD 8-bit bitmap",
+ "BGRA 32-bit color image bitmap",
+ "SDF 8-bit bitmap"
+ };
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+
+#define GRID_FIT_METRICS
+
+
+ /* forward declaration */
+ static FT_Error
+ ft_open_face_internal( FT_Library library,
+ const FT_Open_Args* args,
+ FT_Long face_index,
+ FT_Face *aface,
+ FT_Bool test_mac_fonts );
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_service_list_lookup( FT_ServiceDesc service_descriptors,
+ const char* service_id )
+ {
+ FT_Pointer result = NULL;
+ FT_ServiceDesc desc = service_descriptors;
+
+
+ if ( desc && service_id )
+ {
+ for ( ; desc->serv_id != NULL; desc++ )
+ {
+ if ( ft_strcmp( desc->serv_id, service_id ) == 0 )
+ {
+ result = (FT_Pointer)desc->serv_data;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_validator_init( FT_Validator valid,
+ const FT_Byte* base,
+ const FT_Byte* limit,
+ FT_ValidationLevel level )
+ {
+ valid->base = base;
+ valid->limit = limit;
+ valid->level = level;
+ valid->error = FT_Err_Ok;
+ }
+
+
+ FT_BASE_DEF( FT_Int )
+ ft_validator_run( FT_Validator valid )
+ {
+ /* This function doesn't work! None should call it. */
+ FT_UNUSED( valid );
+
+ return -1;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_validator_error( FT_Validator valid,
+ FT_Error error )
+ {
+ /* since the cast below also disables the compiler's */
+ /* type check, we introduce a dummy variable, which */
+ /* will be optimized away */
+ volatile ft_jmp_buf* jump_buffer = &valid->jump_buffer;
+
+
+ valid->error = error;
+
+ /* throw away volatileness; use `jump_buffer' or the */
+ /* compiler may warn about an unused local variable */
+ ft_longjmp( *(ft_jmp_buf*) jump_buffer, 1 );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** S T R E A M ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* create a new input stream from an FT_Open_Args structure */
+ /* */
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_New( FT_Library library,
+ const FT_Open_Args* args,
+ FT_Stream *astream )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Stream stream = NULL;
+ FT_UInt mode;
+
+
+ *astream = NULL;
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !args )
+ return FT_THROW( Invalid_Argument );
+
+ memory = library->memory;
+ mode = args->flags &
+ ( FT_OPEN_MEMORY | FT_OPEN_STREAM | FT_OPEN_PATHNAME );
+
+ if ( mode == FT_OPEN_MEMORY )
+ {
+ /* create a memory-based stream */
+ if ( FT_NEW( stream ) )
+ goto Exit;
+
+ FT_Stream_OpenMemory( stream,
+ (const FT_Byte*)args->memory_base,
+ (FT_ULong)args->memory_size );
+ stream->memory = memory;
+ }
+
+#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT
+
+ else if ( mode == FT_OPEN_PATHNAME )
+ {
+ /* create a normal system stream */
+ if ( FT_NEW( stream ) )
+ goto Exit;
+
+ stream->memory = memory;
+ error = FT_Stream_Open( stream, args->pathname );
+ if ( error )
+ FT_FREE( stream );
+ }
+ else if ( ( mode == FT_OPEN_STREAM ) && args->stream )
+ {
+ /* use an existing, user-provided stream */
+
+ /* in this case, we do not need to allocate a new stream object */
+ /* since the caller is responsible for closing it himself */
+ stream = args->stream;
+ stream->memory = memory;
+ error = FT_Err_Ok;
+ }
+
+#endif
+
+ else
+ {
+ error = FT_THROW( Invalid_Argument );
+ if ( ( args->flags & FT_OPEN_STREAM ) && args->stream )
+ FT_Stream_Close( args->stream );
+ }
+
+ if ( !error )
+ *astream = stream;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_Free( FT_Stream stream,
+ FT_Int external )
+ {
+ if ( stream )
+ {
+ FT_Memory memory = stream->memory;
+
+
+ FT_Stream_Close( stream );
+
+ if ( !external )
+ FT_FREE( stream );
+ }
+ }
+
+
+ /**************************************************************************
+ *
+ * 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 objs
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** FACE, SIZE & GLYPH SLOT OBJECTS ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static FT_Error
+ ft_glyphslot_init( FT_GlyphSlot slot )
+ {
+ FT_Driver driver = slot->face->driver;
+ FT_Driver_Class clazz = driver->clazz;
+ FT_Memory memory = driver->root.memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Slot_Internal internal = NULL;
+
+
+ slot->library = driver->root.library;
+
+ if ( FT_NEW( internal ) )
+ goto Exit;
+
+ slot->internal = internal;
+
+ if ( FT_DRIVER_USES_OUTLINES( driver ) )
+ error = FT_GlyphLoader_New( memory, &internal->loader );
+
+ if ( !error && clazz->init_slot )
+ error = clazz->init_slot( slot );
+
+#ifdef FT_CONFIG_OPTION_SVG
+ /* if SVG table exists, allocate the space in `slot->other` */
+ if ( slot->face->face_flags & FT_FACE_FLAG_SVG )
+ {
+ FT_SVG_Document document = NULL;
+
+
+ if ( FT_NEW( document ) )
+ goto Exit;
+ slot->other = document;
+ }
+#endif
+
+ Exit:
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_glyphslot_free_bitmap( FT_GlyphSlot slot )
+ {
+ if ( slot->internal && ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( slot->face );
+
+
+ FT_FREE( slot->bitmap.buffer );
+ slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
+ }
+ else
+ {
+ /* assume that the bitmap buffer was stolen or not */
+ /* allocated from the heap */
+ slot->bitmap.buffer = NULL;
+ }
+ }
+
+
+ /* overflow-resistant presetting of bitmap position and dimensions; */
+ /* also check whether the size is too large for rendering */
+ FT_BASE_DEF( FT_Bool )
+ ft_glyphslot_preset_bitmap( FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ const FT_Vector* origin )
+ {
+ FT_Outline* outline = &slot->outline;
+ FT_Bitmap* bitmap = &slot->bitmap;
+
+ FT_Pixel_Mode pixel_mode;
+
+ FT_BBox cbox, pbox;
+ FT_Pos x_shift = 0;
+ FT_Pos y_shift = 0;
+ FT_Pos x_left, y_top;
+ FT_Pos width, height, pitch;
+
+
+ if ( slot->format == FT_GLYPH_FORMAT_SVG )
+ {
+ FT_Module module;
+ SVG_Service svg_service;
+
+
+ module = FT_Get_Module( slot->library, "ot-svg" );
+ svg_service = (SVG_Service)module->clazz->module_interface;
+
+ return (FT_Bool)svg_service->preset_slot( module, slot, FALSE );
+ }
+ else if ( slot->format != FT_GLYPH_FORMAT_OUTLINE )
+ return 1;
+
+ if ( origin )
+ {
+ x_shift = origin->x;
+ y_shift = origin->y;
+ }
+
+ /* compute the control box, and grid-fit it, */
+ /* taking into account the origin shift */
+ FT_Outline_Get_CBox( outline, &cbox );
+
+ /* rough estimate of pixel box */
+ pbox.xMin = ( cbox.xMin >> 6 ) + ( x_shift >> 6 );
+ pbox.yMin = ( cbox.yMin >> 6 ) + ( y_shift >> 6 );
+ pbox.xMax = ( cbox.xMax >> 6 ) + ( x_shift >> 6 );
+ pbox.yMax = ( cbox.yMax >> 6 ) + ( y_shift >> 6 );
+
+ /* tiny remainder box */
+ cbox.xMin = ( cbox.xMin & 63 ) + ( x_shift & 63 );
+ cbox.yMin = ( cbox.yMin & 63 ) + ( y_shift & 63 );
+ cbox.xMax = ( cbox.xMax & 63 ) + ( x_shift & 63 );
+ cbox.yMax = ( cbox.yMax & 63 ) + ( y_shift & 63 );
+
+ switch ( mode )
+ {
+ case FT_RENDER_MODE_MONO:
+ pixel_mode = FT_PIXEL_MODE_MONO;
+#if 1
+ /* x */
+
+ /* undocumented but confirmed: bbox values get rounded; */
+ /* we do asymmetric rounding so that the center of a pixel */
+ /* gets always included */
+
+ pbox.xMin += ( cbox.xMin + 31 ) >> 6;
+ pbox.xMax += ( cbox.xMax + 32 ) >> 6;
+
+ /* if the bbox collapsed, we add a pixel based on the total */
+ /* rounding remainder to cover most of the original cbox */
+
+ if ( pbox.xMin == pbox.xMax )
+ {
+ if ( ( ( cbox.xMin + 31 ) & 63 ) - 31 +
+ ( ( cbox.xMax + 32 ) & 63 ) - 32 < 0 )
+ pbox.xMin -= 1;
+ else
+ pbox.xMax += 1;
+ }
+
+ /* y */
+
+ pbox.yMin += ( cbox.yMin + 31 ) >> 6;
+ pbox.yMax += ( cbox.yMax + 32 ) >> 6;
+
+ if ( pbox.yMin == pbox.yMax )
+ {
+ if ( ( ( cbox.yMin + 31 ) & 63 ) - 31 +
+ ( ( cbox.yMax + 32 ) & 63 ) - 32 < 0 )
+ pbox.yMin -= 1;
+ else
+ pbox.yMax += 1;
+ }
+
+ break;
+#else
+ goto Adjust;
+#endif
+
+ case FT_RENDER_MODE_LCD:
+ pixel_mode = FT_PIXEL_MODE_LCD;
+ ft_lcd_padding( &cbox, slot, mode );
+ goto Adjust;
+
+ case FT_RENDER_MODE_LCD_V:
+ pixel_mode = FT_PIXEL_MODE_LCD_V;
+ ft_lcd_padding( &cbox, slot, mode );
+ goto Adjust;
+
+ case FT_RENDER_MODE_NORMAL:
+ case FT_RENDER_MODE_LIGHT:
+ default:
+ pixel_mode = FT_PIXEL_MODE_GRAY;
+ Adjust:
+ pbox.xMin += cbox.xMin >> 6;
+ pbox.yMin += cbox.yMin >> 6;
+ pbox.xMax += ( cbox.xMax + 63 ) >> 6;
+ pbox.yMax += ( cbox.yMax + 63 ) >> 6;
+ }
+
+ x_left = pbox.xMin;
+ y_top = pbox.yMax;
+
+ width = pbox.xMax - pbox.xMin;
+ height = pbox.yMax - pbox.yMin;
+
+ switch ( pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ pitch = ( ( width + 15 ) >> 4 ) << 1;
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ width *= 3;
+ pitch = FT_PAD_CEIL( width, 4 );
+ break;
+
+ case FT_PIXEL_MODE_LCD_V:
+ height *= 3;
+ FALL_THROUGH;
+
+ case FT_PIXEL_MODE_GRAY:
+ default:
+ pitch = width;
+ }
+
+ slot->bitmap_left = (FT_Int)x_left;
+ slot->bitmap_top = (FT_Int)y_top;
+
+ bitmap->pixel_mode = (unsigned char)pixel_mode;
+ bitmap->num_grays = 256;
+ bitmap->width = (unsigned int)width;
+ bitmap->rows = (unsigned int)height;
+ bitmap->pitch = pitch;
+
+ if ( pbox.xMin < -0x8000 || pbox.xMax > 0x7FFF ||
+ pbox.yMin < -0x8000 || pbox.yMax > 0x7FFF )
+ {
+ FT_TRACE3(( "ft_glyphslot_preset_bitmap: [%ld %ld %ld %ld]\n",
+ pbox.xMin, pbox.yMin, pbox.xMax, pbox.yMax ));
+ return 1;
+ }
+
+ return 0;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_glyphslot_set_bitmap( FT_GlyphSlot slot,
+ FT_Byte* buffer )
+ {
+ ft_glyphslot_free_bitmap( slot );
+
+ slot->bitmap.buffer = buffer;
+
+ FT_ASSERT( (slot->internal->flags & FT_GLYPH_OWN_BITMAP) == 0 );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ ft_glyphslot_alloc_bitmap( FT_GlyphSlot slot,
+ FT_ULong size )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( slot->face );
+ FT_Error error;
+
+
+ if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
+ FT_FREE( slot->bitmap.buffer );
+ else
+ slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+
+ FT_MEM_ALLOC( slot->bitmap.buffer, size );
+ return error;
+ }
+
+
+ static void
+ ft_glyphslot_clear( FT_GlyphSlot slot )
+ {
+ /* free bitmap if needed */
+ ft_glyphslot_free_bitmap( slot );
+
+ /* clear all public fields in the glyph slot */
+ slot->glyph_index = 0;
+
+ FT_ZERO( &slot->metrics );
+ FT_ZERO( &slot->outline );
+
+ slot->bitmap.width = 0;
+ slot->bitmap.rows = 0;
+ slot->bitmap.pitch = 0;
+ slot->bitmap.pixel_mode = 0;
+ /* `slot->bitmap.buffer' has been handled by ft_glyphslot_free_bitmap */
+
+ slot->bitmap_left = 0;
+ slot->bitmap_top = 0;
+ slot->num_subglyphs = 0;
+ slot->subglyphs = NULL;
+ slot->control_data = NULL;
+ slot->control_len = 0;
+
+#ifndef FT_CONFIG_OPTION_SVG
+ slot->other = NULL;
+#else
+ if ( !( slot->face->face_flags & FT_FACE_FLAG_SVG ) )
+ slot->other = NULL;
+ else
+ {
+ if ( slot->internal->flags & FT_GLYPH_OWN_GZIP_SVG )
+ {
+ FT_Memory memory = slot->face->memory;
+ FT_SVG_Document doc = (FT_SVG_Document)slot->other;
+
+
+ FT_FREE( doc->svg_document );
+ slot->internal->flags &= ~FT_GLYPH_OWN_GZIP_SVG;
+ }
+ }
+#endif
+
+ slot->format = FT_GLYPH_FORMAT_NONE;
+
+ slot->linearHoriAdvance = 0;
+ slot->linearVertAdvance = 0;
+ slot->advance.x = 0;
+ slot->advance.y = 0;
+ slot->lsb_delta = 0;
+ slot->rsb_delta = 0;
+ }
+
+
+ static void
+ ft_glyphslot_done( FT_GlyphSlot slot )
+ {
+ FT_Driver driver = slot->face->driver;
+ FT_Driver_Class clazz = driver->clazz;
+ FT_Memory memory = driver->root.memory;
+
+#ifdef FT_CONFIG_OPTION_SVG
+ if ( slot->face->face_flags & FT_FACE_FLAG_SVG )
+ {
+ /* Free memory in case SVG was there. */
+ /* `slot->internal` might be NULL in out-of-memory situations. */
+ if ( slot->internal && slot->internal->flags & FT_GLYPH_OWN_GZIP_SVG )
+ {
+ FT_SVG_Document doc = (FT_SVG_Document)slot->other;
+
+
+ FT_FREE( doc->svg_document );
+
+ slot->internal->flags &= ~FT_GLYPH_OWN_GZIP_SVG;
+ }
+
+ FT_FREE( slot->other );
+ }
+#endif
+
+ if ( clazz->done_slot )
+ clazz->done_slot( slot );
+
+ /* free bitmap buffer if needed */
+ ft_glyphslot_free_bitmap( slot );
+
+ /* slot->internal might be NULL in out-of-memory situations */
+ if ( slot->internal )
+ {
+ /* free glyph loader */
+ if ( FT_DRIVER_USES_OUTLINES( driver ) )
+ {
+ FT_GlyphLoader_Done( slot->internal->loader );
+ slot->internal->loader = NULL;
+ }
+
+ FT_FREE( slot->internal );
+ }
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( FT_Error )
+ FT_New_GlyphSlot( FT_Face face,
+ FT_GlyphSlot *aslot )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_Driver_Class clazz;
+ FT_Memory memory;
+ FT_GlyphSlot slot = NULL;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !face->driver )
+ return FT_THROW( Invalid_Argument );
+
+ driver = face->driver;
+ clazz = driver->clazz;
+ memory = driver->root.memory;
+
+ FT_TRACE4(( "FT_New_GlyphSlot: Creating new slot object\n" ));
+ if ( !FT_ALLOC( slot, clazz->slot_object_size ) )
+ {
+ slot->face = face;
+
+ error = ft_glyphslot_init( slot );
+ if ( error )
+ {
+ ft_glyphslot_done( slot );
+ FT_FREE( slot );
+ goto Exit;
+ }
+
+ slot->next = face->glyph;
+ face->glyph = slot;
+
+ if ( aslot )
+ *aslot = slot;
+ }
+ else if ( aslot )
+ *aslot = NULL;
+
+
+ Exit:
+ FT_TRACE4(( "FT_New_GlyphSlot: Return 0x%x\n", error ));
+
+ return error;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( void )
+ FT_Done_GlyphSlot( FT_GlyphSlot slot )
+ {
+ if ( slot )
+ {
+ FT_Driver driver = slot->face->driver;
+ FT_Memory memory = driver->root.memory;
+ FT_GlyphSlot prev;
+ FT_GlyphSlot cur;
+
+
+ /* Remove slot from its parent face's list */
+ prev = NULL;
+ cur = slot->face->glyph;
+
+ while ( cur )
+ {
+ if ( cur == slot )
+ {
+ if ( !prev )
+ slot->face->glyph = cur->next;
+ else
+ prev->next = cur->next;
+
+ /* finalize client-specific data */
+ if ( slot->generic.finalizer )
+ slot->generic.finalizer( slot );
+
+ ft_glyphslot_done( slot );
+ FT_FREE( slot );
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ }
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Transform( FT_Face face,
+ FT_Matrix* matrix,
+ FT_Vector* delta )
+ {
+ FT_Face_Internal internal;
+
+
+ if ( !face )
+ return;
+
+ internal = face->internal;
+
+ internal->transform_flags = 0;
+
+ if ( !matrix )
+ {
+ internal->transform_matrix.xx = 0x10000L;
+ internal->transform_matrix.xy = 0;
+ internal->transform_matrix.yx = 0;
+ internal->transform_matrix.yy = 0x10000L;
+
+ matrix = &internal->transform_matrix;
+ }
+ else
+ internal->transform_matrix = *matrix;
+
+ /* set transform_flags bit flag 0 if `matrix' isn't the identity */
+ if ( ( matrix->xy | matrix->yx ) ||
+ matrix->xx != 0x10000L ||
+ matrix->yy != 0x10000L )
+ internal->transform_flags |= 1;
+
+ if ( !delta )
+ {
+ internal->transform_delta.x = 0;
+ internal->transform_delta.y = 0;
+
+ delta = &internal->transform_delta;
+ }
+ else
+ internal->transform_delta = *delta;
+
+ /* set transform_flags bit flag 1 if `delta' isn't the null vector */
+ if ( delta->x | delta->y )
+ internal->transform_flags |= 2;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Get_Transform( FT_Face face,
+ FT_Matrix* matrix,
+ FT_Vector* delta )
+ {
+ FT_Face_Internal internal;
+
+
+ if ( !face )
+ return;
+
+ internal = face->internal;
+
+ if ( matrix )
+ *matrix = internal->transform_matrix;
+
+ if ( delta )
+ *delta = internal->transform_delta;
+ }
+
+
+ static FT_Renderer
+ ft_lookup_glyph_renderer( FT_GlyphSlot slot );
+
+
+#ifdef GRID_FIT_METRICS
+ static void
+ ft_glyphslot_grid_fit_metrics( FT_GlyphSlot slot,
+ FT_Bool vertical )
+ {
+ FT_Glyph_Metrics* metrics = &slot->metrics;
+ FT_Pos right, bottom;
+
+
+ if ( vertical )
+ {
+ metrics->horiBearingX = FT_PIX_FLOOR( metrics->horiBearingX );
+ metrics->horiBearingY = FT_PIX_CEIL_LONG( metrics->horiBearingY );
+
+ right = FT_PIX_CEIL_LONG( ADD_LONG( metrics->vertBearingX,
+ metrics->width ) );
+ bottom = FT_PIX_CEIL_LONG( ADD_LONG( metrics->vertBearingY,
+ metrics->height ) );
+
+ metrics->vertBearingX = FT_PIX_FLOOR( metrics->vertBearingX );
+ metrics->vertBearingY = FT_PIX_FLOOR( metrics->vertBearingY );
+
+ metrics->width = SUB_LONG( right,
+ metrics->vertBearingX );
+ metrics->height = SUB_LONG( bottom,
+ metrics->vertBearingY );
+ }
+ else
+ {
+ metrics->vertBearingX = FT_PIX_FLOOR( metrics->vertBearingX );
+ metrics->vertBearingY = FT_PIX_FLOOR( metrics->vertBearingY );
+
+ right = FT_PIX_CEIL_LONG( ADD_LONG( metrics->horiBearingX,
+ metrics->width ) );
+ bottom = FT_PIX_FLOOR( SUB_LONG( metrics->horiBearingY,
+ metrics->height ) );
+
+ metrics->horiBearingX = FT_PIX_FLOOR( metrics->horiBearingX );
+ metrics->horiBearingY = FT_PIX_CEIL_LONG( metrics->horiBearingY );
+
+ metrics->width = SUB_LONG( right,
+ metrics->horiBearingX );
+ metrics->height = SUB_LONG( metrics->horiBearingY,
+ bottom );
+ }
+
+ metrics->horiAdvance = FT_PIX_ROUND_LONG( metrics->horiAdvance );
+ metrics->vertAdvance = FT_PIX_ROUND_LONG( metrics->vertAdvance );
+ }
+#endif /* GRID_FIT_METRICS */
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Load_Glyph( FT_Face face,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_GlyphSlot slot;
+ FT_Library library;
+ FT_Bool autohint = FALSE;
+ FT_Module hinter;
+ TT_Face ttface = (TT_Face)face;
+
+
+ if ( !face || !face->size || !face->glyph )
+ return FT_THROW( Invalid_Face_Handle );
+
+ /* The validity test for `glyph_index' is performed by the */
+ /* font drivers. */
+
+ slot = face->glyph;
+ ft_glyphslot_clear( slot );
+
+ driver = face->driver;
+ library = driver->root.library;
+ hinter = library->auto_hinter;
+
+ /* undefined scale means no scale */
+ if ( face->size->metrics.x_ppem == 0 ||
+ face->size->metrics.y_ppem == 0 )
+ load_flags |= FT_LOAD_NO_SCALE;
+
+ /* resolve load flags dependencies */
+
+ if ( load_flags & FT_LOAD_NO_RECURSE )
+ load_flags |= FT_LOAD_NO_SCALE |
+ FT_LOAD_IGNORE_TRANSFORM;
+
+ if ( load_flags & FT_LOAD_NO_SCALE )
+ {
+ load_flags |= FT_LOAD_NO_HINTING |
+ FT_LOAD_NO_BITMAP;
+
+ load_flags &= ~FT_LOAD_RENDER;
+ }
+
+ if ( load_flags & FT_LOAD_BITMAP_METRICS_ONLY )
+ load_flags &= ~FT_LOAD_RENDER;
+
+ /*
+ * Determine whether we need to auto-hint or not.
+ * The general rules are:
+ *
+ * - Do only auto-hinting if we have
+ *
+ * - a hinter module,
+ * - a scalable font,
+ * - not a tricky font, and
+ * - no transforms except simple slants and/or rotations by
+ * integer multiples of 90 degrees.
+ *
+ * - Then, auto-hint if FT_LOAD_FORCE_AUTOHINT is set or if we don't
+ * have a native font hinter.
+ *
+ * - Otherwise, auto-hint for LIGHT hinting mode or if there isn't
+ * any hinting bytecode in the TrueType/OpenType font.
+ *
+ * - Exception: The font is `tricky' and requires the native hinter to
+ * load properly.
+ */
+
+ if ( hinter &&
+ !( load_flags & FT_LOAD_NO_HINTING ) &&
+ !( load_flags & FT_LOAD_NO_AUTOHINT ) &&
+ FT_IS_SCALABLE( face ) &&
+ !FT_IS_TRICKY( face ) &&
+ ( ( load_flags & FT_LOAD_IGNORE_TRANSFORM ) ||
+ ( face->internal->transform_matrix.yx == 0 &&
+ face->internal->transform_matrix.xx != 0 ) ||
+ ( face->internal->transform_matrix.xx == 0 &&
+ face->internal->transform_matrix.yx != 0 ) ) )
+ {
+ if ( ( load_flags & FT_LOAD_FORCE_AUTOHINT ) ||
+ !FT_DRIVER_HAS_HINTER( driver ) )
+ autohint = TRUE;
+ else
+ {
+ FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags );
+ FT_Bool is_light_type1;
+
+
+ /* only the new Adobe engine (for both CFF and Type 1) is `light'; */
+ /* we use `strstr' to catch both `Type 1' and `CID Type 1' */
+ is_light_type1 =
+ ft_strstr( FT_Get_Font_Format( face ), "Type 1" ) != NULL &&
+ ((PS_Driver)driver)->hinting_engine == FT_HINTING_ADOBE;
+
+ /* the check for `num_locations' assures that we actually */
+ /* test for instructions in a TTF and not in a CFF-based OTF */
+ /* */
+ /* since `maxSizeOfInstructions' might be unreliable, we */
+ /* check the size of the `fpgm' and `prep' tables, too -- */
+ /* the assumption is that there don't exist real TTFs where */
+ /* both `fpgm' and `prep' tables are missing */
+ if ( ( mode == FT_RENDER_MODE_LIGHT &&
+ ( !FT_DRIVER_HINTS_LIGHTLY( driver ) &&
+ !is_light_type1 ) ) ||
+ ( FT_IS_SFNT( face ) &&
+ ttface->num_locations &&
+ ttface->max_profile.maxSizeOfInstructions == 0 &&
+ ttface->font_program_size == 0 &&
+ ttface->cvt_program_size == 0 ) )
+ autohint = TRUE;
+ }
+ }
+
+ if ( autohint )
+ {
+ FT_AutoHinter_Interface hinting;
+
+
+ /* XXX: The use of the `FT_LOAD_XXX_ONLY` flags is not very */
+ /* elegant. */
+
+ /* try to load SVG documents if available */
+ if ( FT_HAS_SVG( face ) )
+ {
+ error = driver->clazz->load_glyph( slot, face->size,
+ glyph_index,
+ load_flags | FT_LOAD_SVG_ONLY );
+
+ if ( !error && slot->format == FT_GLYPH_FORMAT_SVG )
+ goto Load_Ok;
+ }
+
+ /* try to load embedded bitmaps if available */
+ if ( FT_HAS_FIXED_SIZES( face ) &&
+ ( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
+ {
+ error = driver->clazz->load_glyph( slot, face->size,
+ glyph_index,
+ load_flags | FT_LOAD_SBITS_ONLY );
+
+ if ( !error && slot->format == FT_GLYPH_FORMAT_BITMAP )
+ goto Load_Ok;
+ }
+
+ {
+ FT_Face_Internal internal = face->internal;
+ FT_Int transform_flags = internal->transform_flags;
+
+
+ /* since the auto-hinter calls FT_Load_Glyph by itself, */
+ /* make sure that glyphs aren't transformed */
+ internal->transform_flags = 0;
+
+ /* load auto-hinted outline */
+ hinting = (FT_AutoHinter_Interface)hinter->clazz->module_interface;
+
+ error = hinting->load_glyph( (FT_AutoHinter)hinter,
+ slot, face->size,
+ glyph_index, load_flags );
+
+ internal->transform_flags = transform_flags;
+ }
+ }
+ else
+ {
+ error = driver->clazz->load_glyph( slot,
+ face->size,
+ glyph_index,
+ load_flags );
+ if ( error )
+ goto Exit;
+
+ if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+ {
+ /* check that the loaded outline is correct */
+ error = FT_Outline_Check( &slot->outline );
+ if ( error )
+ goto Exit;
+
+#ifdef GRID_FIT_METRICS
+ if ( !( load_flags & FT_LOAD_NO_HINTING ) )
+ ft_glyphslot_grid_fit_metrics(
+ slot,
+ FT_BOOL( load_flags & FT_LOAD_VERTICAL_LAYOUT ) );
+#endif
+ }
+ }
+
+ Load_Ok:
+ /* compute the advance */
+ if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
+ {
+ slot->advance.x = 0;
+ slot->advance.y = slot->metrics.vertAdvance;
+ }
+ else
+ {
+ slot->advance.x = slot->metrics.horiAdvance;
+ slot->advance.y = 0;
+ }
+
+ /* compute the linear advance in 16.16 pixels */
+ if ( ( load_flags & FT_LOAD_LINEAR_DESIGN ) == 0 &&
+ FT_IS_SCALABLE( face ) )
+ {
+ FT_Size_Metrics* metrics = &face->size->metrics;
+
+
+ /* it's tricky! */
+ slot->linearHoriAdvance = FT_MulDiv( slot->linearHoriAdvance,
+ metrics->x_scale, 64 );
+
+ slot->linearVertAdvance = FT_MulDiv( slot->linearVertAdvance,
+ metrics->y_scale, 64 );
+ }
+
+ if ( ( load_flags & FT_LOAD_IGNORE_TRANSFORM ) == 0 )
+ {
+ FT_Face_Internal internal = face->internal;
+
+
+ /* now, transform the glyph image if needed */
+ if ( internal->transform_flags )
+ {
+ /* get renderer */
+ FT_Renderer renderer = ft_lookup_glyph_renderer( slot );
+
+
+ if ( renderer )
+ error = renderer->clazz->transform_glyph(
+ renderer, slot,
+ &internal->transform_matrix,
+ &internal->transform_delta );
+ else if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+ {
+ /* apply `standard' transformation if no renderer is available */
+ if ( internal->transform_flags & 1 )
+ FT_Outline_Transform( &slot->outline,
+ &internal->transform_matrix );
+
+ if ( internal->transform_flags & 2 )
+ FT_Outline_Translate( &slot->outline,
+ internal->transform_delta.x,
+ internal->transform_delta.y );
+ }
+
+ /* transform advance */
+ FT_Vector_Transform( &slot->advance, &internal->transform_matrix );
+ }
+ }
+
+ slot->glyph_index = glyph_index;
+ slot->internal->load_flags = load_flags;
+
+ /* do we need to render the image or preset the bitmap now? */
+ if ( !error &&
+ ( load_flags & FT_LOAD_NO_SCALE ) == 0 &&
+ slot->format != FT_GLYPH_FORMAT_BITMAP &&
+ slot->format != FT_GLYPH_FORMAT_COMPOSITE )
+ {
+ FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags );
+
+
+ if ( mode == FT_RENDER_MODE_NORMAL &&
+ load_flags & FT_LOAD_MONOCHROME )
+ mode = FT_RENDER_MODE_MONO;
+
+ if ( load_flags & FT_LOAD_RENDER )
+ error = FT_Render_Glyph( slot, mode );
+ else
+ ft_glyphslot_preset_bitmap( slot, mode, NULL );
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_TRACE5(( "FT_Load_Glyph: index %d, flags 0x%x\n",
+ glyph_index, load_flags ));
+ FT_TRACE5(( " bitmap %dx%d %s, %s (mode %d)\n",
+ slot->bitmap.width,
+ slot->bitmap.rows,
+ slot->outline.points ?
+ slot->bitmap.buffer ? "rendered"
+ : "preset"
+ :
+ slot->internal->flags & FT_GLYPH_OWN_BITMAP ? "owned"
+ : "unowned",
+ pixel_modes[slot->bitmap.pixel_mode],
+ slot->bitmap.pixel_mode ));
+ FT_TRACE5(( "\n" ));
+ FT_TRACE5(( " x advance: %f\n", (double)slot->advance.x / 64 ));
+ FT_TRACE5(( " y advance: %f\n", (double)slot->advance.y / 64 ));
+ FT_TRACE5(( " linear x advance: %f\n",
+ (double)slot->linearHoriAdvance / 65536 ));
+ FT_TRACE5(( " linear y advance: %f\n",
+ (double)slot->linearVertAdvance / 65536 ));
+
+ {
+ FT_Glyph_Metrics* metrics = &slot->metrics;
+
+
+ FT_TRACE5(( " metrics:\n" ));
+ FT_TRACE5(( " width: %f\n", (double)metrics->width / 64 ));
+ FT_TRACE5(( " height: %f\n", (double)metrics->height / 64 ));
+ FT_TRACE5(( "\n" ));
+ FT_TRACE5(( " horiBearingX: %f\n",
+ (double)metrics->horiBearingX / 64 ));
+ FT_TRACE5(( " horiBearingY: %f\n",
+ (double)metrics->horiBearingY / 64 ));
+ FT_TRACE5(( " horiAdvance: %f\n",
+ (double)metrics->horiAdvance / 64 ));
+ FT_TRACE5(( "\n" ));
+ FT_TRACE5(( " vertBearingX: %f\n",
+ (double)metrics->vertBearingX / 64 ));
+ FT_TRACE5(( " vertBearingY: %f\n",
+ (double)metrics->vertBearingY / 64 ));
+ FT_TRACE5(( " vertAdvance: %f\n",
+ (double)metrics->vertAdvance / 64 ));
+ }
+#endif
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Load_Char( FT_Face face,
+ FT_ULong char_code,
+ FT_Int32 load_flags )
+ {
+ FT_UInt glyph_index;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ glyph_index = (FT_UInt)char_code;
+ if ( face->charmap )
+ glyph_index = FT_Get_Char_Index( face, char_code );
+
+ return FT_Load_Glyph( face, glyph_index, load_flags );
+ }
+
+
+ /* destructor for sizes list */
+ static void
+ destroy_size( FT_Memory memory,
+ FT_Size size,
+ FT_Driver driver )
+ {
+ /* finalize client-specific data */
+ if ( size->generic.finalizer )
+ size->generic.finalizer( size );
+
+ /* finalize format-specific stuff */
+ if ( driver->clazz->done_size )
+ driver->clazz->done_size( size );
+
+ FT_FREE( size->internal );
+ FT_FREE( size );
+ }
+
+
+ static void
+ ft_cmap_done_internal( FT_CMap cmap );
+
+
+ static void
+ destroy_charmaps( FT_Face face,
+ FT_Memory memory )
+ {
+ FT_Int n;
+
+
+ if ( !face )
+ return;
+
+ for ( n = 0; n < face->num_charmaps; n++ )
+ {
+ FT_CMap cmap = FT_CMAP( face->charmaps[n] );
+
+
+ ft_cmap_done_internal( cmap );
+
+ face->charmaps[n] = NULL;
+ }
+
+ FT_FREE( face->charmaps );
+ face->num_charmaps = 0;
+ }
+
+
+ /* destructor for faces list */
+ static void
+ destroy_face( FT_Memory memory,
+ FT_Face face,
+ FT_Driver driver )
+ {
+ FT_Driver_Class clazz = driver->clazz;
+
+
+ /* discard auto-hinting data */
+ if ( face->autohint.finalizer )
+ face->autohint.finalizer( face->autohint.data );
+
+ /* Discard glyph slots for this face. */
+ /* Beware! FT_Done_GlyphSlot() changes the field `face->glyph' */
+ while ( face->glyph )
+ FT_Done_GlyphSlot( face->glyph );
+
+ /* discard all sizes for this face */
+ FT_List_Finalize( &face->sizes_list,
+ (FT_List_Destructor)destroy_size,
+ memory,
+ driver );
+ face->size = NULL;
+
+ /* now discard client data */
+ if ( face->generic.finalizer )
+ face->generic.finalizer( face );
+
+ /* discard charmaps */
+ destroy_charmaps( face, memory );
+
+ /* finalize format-specific stuff */
+ if ( clazz->done_face )
+ clazz->done_face( face );
+
+ /* close the stream for this face if needed */
+ FT_Stream_Free(
+ face->stream,
+ ( face->face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 );
+
+ face->stream = NULL;
+
+ /* get rid of it */
+ if ( face->internal )
+ {
+ FT_FREE( face->internal );
+ }
+ FT_FREE( face );
+ }
+
+
+ static void
+ Destroy_Driver( FT_Driver driver )
+ {
+ FT_List_Finalize( &driver->faces_list,
+ (FT_List_Destructor)destroy_face,
+ driver->root.memory,
+ driver );
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * find_unicode_charmap
+ *
+ * @Description:
+ * This function finds a Unicode charmap, if there is one.
+ * And if there is more than one, it tries to favour the more
+ * extensive one, i.e., one that supports UCS-4 against those which
+ * are limited to the BMP (said UCS-2 encoding.)
+ *
+ * This function is called from open_face() (just below), and also
+ * from FT_Select_Charmap( ..., FT_ENCODING_UNICODE ).
+ */
+ static FT_Error
+ find_unicode_charmap( FT_Face face )
+ {
+ FT_CharMap* first;
+ FT_CharMap* cur;
+
+
+ /* caller should have already checked that `face' is valid */
+ FT_ASSERT( face );
+
+ first = face->charmaps;
+
+ if ( !first )
+ return FT_THROW( Invalid_CharMap_Handle );
+
+ /*
+ * The original TrueType specification(s) only specified charmap
+ * formats that are capable of mapping 8 or 16 bit character codes to
+ * glyph indices.
+ *
+ * However, recent updates to the Apple and OpenType specifications
+ * introduced new formats that are capable of mapping 32-bit character
+ * codes as well. And these are already used on some fonts, mainly to
+ * map non-BMP Asian ideographs as defined in Unicode.
+ *
+ * For compatibility purposes, these fonts generally come with
+ * *several* Unicode charmaps:
+ *
+ * - One of them in the "old" 16-bit format, that cannot access
+ * all glyphs in the font.
+ *
+ * - Another one in the "new" 32-bit format, that can access all
+ * the glyphs.
+ *
+ * This function has been written to always favor a 32-bit charmap
+ * when found. Otherwise, a 16-bit one is returned when found.
+ */
+
+ /* Since the `interesting' table, with IDs (3,10), is normally the */
+ /* last one, we loop backwards. This loses with type1 fonts with */
+ /* non-BMP characters (<.0001%), this wins with .ttf with non-BMP */
+ /* chars (.01% ?), and this is the same about 99.99% of the time! */
+
+ cur = first + face->num_charmaps; /* points after the last one */
+
+ for ( ; --cur >= first; )
+ {
+ if ( cur[0]->encoding == FT_ENCODING_UNICODE )
+ {
+ /* XXX If some new encodings to represent UCS-4 are added, */
+ /* they should be added here. */
+ if ( ( cur[0]->platform_id == TT_PLATFORM_MICROSOFT &&
+ cur[0]->encoding_id == TT_MS_ID_UCS_4 ) ||
+ ( cur[0]->platform_id == TT_PLATFORM_APPLE_UNICODE &&
+ cur[0]->encoding_id == TT_APPLE_ID_UNICODE_32 ) )
+ {
+ face->charmap = cur[0];
+ return FT_Err_Ok;
+ }
+ }
+ }
+
+ /* We do not have any UCS-4 charmap. */
+ /* Do the loop again and search for UCS-2 charmaps. */
+ cur = first + face->num_charmaps;
+
+ for ( ; --cur >= first; )
+ {
+ if ( cur[0]->encoding == FT_ENCODING_UNICODE )
+ {
+ face->charmap = cur[0];
+ return FT_Err_Ok;
+ }
+ }
+
+ return FT_THROW( Invalid_CharMap_Handle );
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * find_variant_selector_charmap
+ *
+ * @Description:
+ * This function finds the variant selector charmap, if there is one.
+ * There can only be one (platform=0, specific=5, format=14).
+ */
+ static FT_CharMap
+ find_variant_selector_charmap( FT_Face face )
+ {
+ FT_CharMap* first;
+ FT_CharMap* end;
+ FT_CharMap* cur;
+
+
+ /* caller should have already checked that `face' is valid */
+ FT_ASSERT( face );
+
+ first = face->charmaps;
+
+ if ( !first )
+ return NULL;
+
+ end = first + face->num_charmaps; /* points after the last one */
+
+ for ( cur = first; cur < end; cur++ )
+ {
+ if ( cur[0]->platform_id == TT_PLATFORM_APPLE_UNICODE &&
+ cur[0]->encoding_id == TT_APPLE_ID_VARIANT_SELECTOR &&
+ FT_Get_CMap_Format( cur[0] ) == 14 )
+ return cur[0];
+ }
+
+ return NULL;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * open_face
+ *
+ * @Description:
+ * This function does some work for FT_Open_Face().
+ */
+ static FT_Error
+ open_face( FT_Driver driver,
+ FT_Stream *astream,
+ FT_Bool *anexternal_stream,
+ FT_Long face_index,
+ FT_Int num_params,
+ FT_Parameter* params,
+ FT_Face *aface )
+ {
+ FT_Memory memory;
+ FT_Driver_Class clazz;
+ FT_Face face = NULL;
+ FT_Face_Internal internal = NULL;
+
+ FT_Error error, error2;
+
+
+ clazz = driver->clazz;
+ memory = driver->root.memory;
+
+ /* allocate the face object and perform basic initialization */
+ if ( FT_ALLOC( face, clazz->face_object_size ) )
+ goto Fail;
+
+ face->driver = driver;
+ face->memory = memory;
+ face->stream = *astream;
+
+ /* set the FT_FACE_FLAG_EXTERNAL_STREAM bit for FT_Done_Face */
+ if ( *anexternal_stream )
+ face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM;
+
+ if ( FT_NEW( internal ) )
+ goto Fail;
+
+ face->internal = internal;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ {
+ int i;
+
+
+ face->internal->incremental_interface = NULL;
+ for ( i = 0; i < num_params && !face->internal->incremental_interface;
+ i++ )
+ if ( params[i].tag == FT_PARAM_TAG_INCREMENTAL )
+ face->internal->incremental_interface =
+ (FT_Incremental_Interface)params[i].data;
+ }
+#endif
+
+ face->internal->random_seed = -1;
+
+ if ( clazz->init_face )
+ error = clazz->init_face( *astream,
+ face,
+ (FT_Int)face_index,
+ num_params,
+ params );
+ /* Stream may have been changed. */
+ *astream = face->stream;
+ *anexternal_stream =
+ ( face->face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0;
+ if ( error )
+ goto Fail;
+
+ /* select Unicode charmap by default */
+ error2 = find_unicode_charmap( face );
+
+ /* if no Unicode charmap can be found, FT_Err_Invalid_CharMap_Handle */
+ /* is returned. */
+
+ /* no error should happen, but we want to play safe */
+ if ( error2 && FT_ERR_NEQ( error2, Invalid_CharMap_Handle ) )
+ {
+ error = error2;
+ goto Fail;
+ }
+
+ *aface = face;
+
+ Fail:
+ if ( error )
+ {
+ destroy_charmaps( face, memory );
+ if ( clazz->done_face )
+ clazz->done_face( face );
+ FT_FREE( internal );
+ FT_FREE( face );
+ *aface = NULL;
+ }
+
+ return error;
+ }
+
+
+ /* there's a Mac-specific extended implementation of FT_New_Face() */
+ /* in src/base/ftmac.c */
+
+#ifndef FT_MACINTOSH
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face( FT_Library library,
+ const char* pathname,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Open_Args args;
+
+
+ /* test for valid `library' and `aface' delayed to `FT_Open_Face' */
+ if ( !pathname )
+ return FT_THROW( Invalid_Argument );
+
+ args.flags = FT_OPEN_PATHNAME;
+ args.pathname = (char*)pathname;
+ args.stream = NULL;
+
+ return ft_open_face_internal( library, &args, face_index, aface, 1 );
+ }
+
+#endif
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Memory_Face( FT_Library library,
+ const FT_Byte* file_base,
+ FT_Long file_size,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Open_Args args;
+
+
+ /* test for valid `library' and `face' delayed to `FT_Open_Face' */
+ if ( !file_base )
+ return FT_THROW( Invalid_Argument );
+
+ args.flags = FT_OPEN_MEMORY;
+ args.memory_base = file_base;
+ args.memory_size = file_size;
+ args.stream = NULL;
+
+ return ft_open_face_internal( library, &args, face_index, aface, 1 );
+ }
+
+
+#ifdef FT_CONFIG_OPTION_MAC_FONTS
+
+ /* The behavior here is very similar to that in base/ftmac.c, but it */
+ /* is designed to work on non-mac systems, so no mac specific calls. */
+ /* */
+ /* We look at the file and determine if it is a mac dfont file or a mac */
+ /* resource file, or a macbinary file containing a mac resource file. */
+ /* */
+ /* Unlike ftmac I'm not going to look at a `FOND'. I don't really see */
+ /* the point, especially since there may be multiple `FOND' resources. */
+ /* Instead I'll just look for `sfnt' and `POST' resources, ordered as */
+ /* they occur in the file. */
+ /* */
+ /* Note that multiple `POST' resources do not mean multiple postscript */
+ /* fonts; they all get jammed together to make what is essentially a */
+ /* pfb file. */
+ /* */
+ /* We aren't interested in `NFNT' or `FONT' bitmap resources. */
+ /* */
+ /* As soon as we get an `sfnt' load it into memory and pass it off to */
+ /* FT_Open_Face. */
+ /* */
+ /* If we have a (set of) `POST' resources, massage them into a (memory) */
+ /* pfb file and pass that to FT_Open_Face. (As with ftmac.c I'm not */
+ /* going to try to save the kerning info. After all that lives in the */
+ /* `FOND' which isn't in the file containing the `POST' resources so */
+ /* we don't really have access to it. */
+
+
+ /* Finalizer for a memory stream; gets called by FT_Done_Face(). */
+ /* It frees the memory it uses. */
+ /* From `ftmac.c'. */
+ static void
+ memory_stream_close( FT_Stream stream )
+ {
+ FT_Memory memory = (FT_Memory)stream->descriptor.pointer;
+
+
+ FT_FREE( stream->base );
+ stream->size = 0;
+ stream->close = NULL;
+ FT_FREE( stream );
+ }
+
+
+ /* Create a new memory stream from a buffer and a size. */
+ /* From `ftmac.c'. */
+ static FT_Error
+ new_memory_stream( FT_Library library,
+ FT_Byte* base,
+ FT_ULong size,
+ FT_Stream_CloseFunc close,
+ FT_Stream *astream )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Stream stream = NULL;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !base )
+ return FT_THROW( Invalid_Argument );
+
+ *astream = NULL;
+ memory = library->memory;
+ if ( FT_NEW( stream ) )
+ goto Exit;
+
+ FT_Stream_OpenMemory( stream, base, size );
+
+ stream->descriptor.pointer = memory;
+ stream->close = close;
+
+ *astream = stream;
+
+ Exit:
+ return error;
+ }
+
+
+ /* Create a new FT_Face given a buffer and a driver name. */
+ /* From `ftmac.c'. */
+ FT_LOCAL_DEF( FT_Error )
+ open_face_from_buffer( FT_Library library,
+ FT_Byte* base,
+ FT_ULong size,
+ FT_Long face_index,
+ const char* driver_name,
+ FT_Face *aface )
+ {
+ FT_Open_Args args;
+ FT_Error error;
+ FT_Memory memory = library->memory;
+
+
+ args.flags = 0;
+
+ if ( driver_name )
+ {
+ args.driver = FT_Get_Module( library, driver_name );
+ if ( !args.driver )
+ {
+ FT_FREE( base );
+ return FT_THROW( Missing_Module );
+ }
+
+ args.flags = args.flags | FT_OPEN_DRIVER;
+ }
+
+ /* `memory_stream_close` also frees the stream object. */
+ error = new_memory_stream( library,
+ base,
+ size,
+ memory_stream_close,
+ &args.stream );
+ if ( error )
+ {
+ FT_FREE( base );
+ return error;
+ }
+
+ args.flags |= FT_OPEN_STREAM;
+
+#ifdef FT_MACINTOSH
+ /* At this point, the face index has served its purpose; */
+ /* whoever calls this function has already used it to */
+ /* locate the correct font data. We should not propagate */
+ /* this index to FT_Open_Face() (unless it is negative). */
+
+ if ( face_index > 0 )
+ face_index &= 0x7FFF0000L; /* retain GX data */
+#endif
+
+ return ft_open_face_internal( library, &args, face_index, aface, 0 );
+ }
+
+
+ /* Look up `TYP1' or `CID ' table from sfnt table directory. */
+ /* `offset' and `length' must exclude the binary header in tables. */
+
+ /* Type 1 and CID-keyed font drivers should recognize sfnt-wrapped */
+ /* format too. Here, since we can't expect that the TrueType font */
+ /* driver is loaded unconditionally, we must parse the font by */
+ /* ourselves. We are only interested in the name of the table and */
+ /* the offset. */
+
+ static FT_Error
+ ft_lookup_PS_in_sfnt_stream( FT_Stream stream,
+ FT_Long face_index,
+ FT_ULong* offset,
+ FT_ULong* length,
+ FT_Bool* is_sfnt_cid )
+ {
+ FT_Error error;
+ FT_UShort numTables;
+ FT_Long pstable_index;
+ FT_ULong tag;
+ int i;
+
+
+ *offset = 0;
+ *length = 0;
+ *is_sfnt_cid = FALSE;
+
+ /* TODO: support for sfnt-wrapped PS/CID in TTC format */
+
+ /* version check for 'typ1' (should be ignored?) */
+ if ( FT_READ_ULONG( tag ) )
+ return error;
+ if ( tag != TTAG_typ1 )
+ return FT_THROW( Unknown_File_Format );
+
+ if ( FT_READ_USHORT( numTables ) )
+ return error;
+ if ( FT_STREAM_SKIP( 2 * 3 ) ) /* skip binary search header */
+ return error;
+
+ pstable_index = -1;
+ *is_sfnt_cid = FALSE;
+
+ for ( i = 0; i < numTables; i++ )
+ {
+ if ( FT_READ_ULONG( tag ) || FT_STREAM_SKIP( 4 ) ||
+ FT_READ_ULONG( *offset ) || FT_READ_ULONG( *length ) )
+ return error;
+
+ if ( tag == TTAG_CID )
+ {
+ pstable_index++;
+ *offset += 22;
+ *length -= 22;
+ *is_sfnt_cid = TRUE;
+ if ( face_index < 0 )
+ return FT_Err_Ok;
+ }
+ else if ( tag == TTAG_TYP1 )
+ {
+ pstable_index++;
+ *offset += 24;
+ *length -= 24;
+ *is_sfnt_cid = FALSE;
+ if ( face_index < 0 )
+ return FT_Err_Ok;
+ }
+ if ( face_index >= 0 && pstable_index == face_index )
+ return FT_Err_Ok;
+ }
+
+ return FT_THROW( Table_Missing );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ open_face_PS_from_sfnt_stream( FT_Library library,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Int num_params,
+ FT_Parameter *params,
+ FT_Face *aface )
+ {
+ FT_Error error;
+ FT_Memory memory = library->memory;
+ FT_ULong offset, length;
+ FT_ULong pos;
+ FT_Bool is_sfnt_cid;
+ FT_Byte* sfnt_ps = NULL;
+
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+
+
+ /* ignore GX stuff */
+ if ( face_index > 0 )
+ face_index &= 0xFFFFL;
+
+ pos = FT_STREAM_POS();
+
+ error = ft_lookup_PS_in_sfnt_stream( stream,
+ face_index,
+ &offset,
+ &length,
+ &is_sfnt_cid );
+ if ( error )
+ goto Exit;
+
+ if ( offset > stream->size )
+ {
+ FT_TRACE2(( "open_face_PS_from_sfnt_stream: invalid table offset\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+ else if ( length > stream->size - offset )
+ {
+ FT_TRACE2(( "open_face_PS_from_sfnt_stream: invalid table length\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ error = FT_Stream_Seek( stream, pos + offset );
+ if ( error )
+ goto Exit;
+
+ if ( FT_QALLOC( sfnt_ps, (FT_Long)length ) )
+ goto Exit;
+
+ error = FT_Stream_Read( stream, (FT_Byte *)sfnt_ps, length );
+ if ( error )
+ {
+ FT_FREE( sfnt_ps );
+ goto Exit;
+ }
+
+ error = open_face_from_buffer( library,
+ sfnt_ps,
+ length,
+ FT_MIN( face_index, 0 ),
+ is_sfnt_cid ? "t1cid" : "type1",
+ aface );
+ Exit:
+ {
+ FT_Error error1;
+
+
+ if ( FT_ERR_EQ( error, Unknown_File_Format ) )
+ {
+ error1 = FT_Stream_Seek( stream, pos );
+ if ( error1 )
+ return error1;
+ }
+
+ return error;
+ }
+ }
+
+
+#ifndef FT_MACINTOSH
+
+ /* The resource header says we've got resource_cnt `POST' (type1) */
+ /* resources in this file. They all need to be coalesced into */
+ /* one lump which gets passed on to the type1 driver. */
+ /* Here can be only one PostScript font in a file so face_index */
+ /* must be 0 (or -1). */
+ /* */
+ static FT_Error
+ Mac_Read_POST_Resource( FT_Library library,
+ FT_Stream stream,
+ FT_Long *offsets,
+ FT_Long resource_cnt,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Error error = FT_ERR( Cannot_Open_Resource );
+ FT_Memory memory = library->memory;
+
+ FT_Byte* pfb_data = NULL;
+ int i, type, flags;
+ FT_ULong len;
+ FT_ULong pfb_len, pfb_pos, pfb_lenpos;
+ FT_ULong rlen, temp;
+
+
+ if ( face_index == -1 )
+ face_index = 0;
+ if ( face_index != 0 )
+ return error;
+
+ /* Find the length of all the POST resources, concatenated. Assume */
+ /* worst case (each resource in its own section). */
+ pfb_len = 0;
+ for ( i = 0; i < resource_cnt; i++ )
+ {
+ error = FT_Stream_Seek( stream, (FT_ULong)offsets[i] );
+ if ( error )
+ goto Exit;
+ if ( FT_READ_ULONG( temp ) ) /* actually LONG */
+ goto Exit;
+
+ /* FT2 allocator takes signed long buffer length,
+ * too large value causing overflow should be checked
+ */
+ FT_TRACE4(( " POST fragment #%d: length=0x%08lx"
+ " total pfb_len=0x%08lx\n",
+ i, temp, pfb_len + temp + 6 ));
+
+ if ( FT_MAC_RFORK_MAX_LEN < temp ||
+ FT_MAC_RFORK_MAX_LEN - temp < pfb_len + 6 )
+ {
+ FT_TRACE2(( " MacOS resource length cannot exceed"
+ " 0x%08lx\n",
+ FT_MAC_RFORK_MAX_LEN ));
+
+ error = FT_THROW( Invalid_Offset );
+ goto Exit;
+ }
+
+ pfb_len += temp + 6;
+ }
+
+ FT_TRACE2(( " total buffer size to concatenate"
+ " %ld POST fragments: 0x%08lx\n",
+ resource_cnt, pfb_len + 2 ));
+
+ if ( pfb_len + 2 < 6 )
+ {
+ FT_TRACE2(( " too long fragment length makes"
+ " pfb_len confused: pfb_len=0x%08lx\n",
+ pfb_len ));
+
+ error = FT_THROW( Array_Too_Large );
+ goto Exit;
+ }
+
+ if ( FT_QALLOC( pfb_data, (FT_Long)pfb_len + 2 ) )
+ goto Exit;
+
+ pfb_data[0] = 0x80;
+ pfb_data[1] = 1; /* Ascii section */
+ pfb_data[2] = 0; /* 4-byte length, fill in later */
+ pfb_data[3] = 0;
+ pfb_data[4] = 0;
+ pfb_data[5] = 0;
+ pfb_pos = 6;
+ pfb_lenpos = 2;
+
+ len = 0;
+ type = 1;
+
+ for ( i = 0; i < resource_cnt; i++ )
+ {
+ error = FT_Stream_Seek( stream, (FT_ULong)offsets[i] );
+ if ( error )
+ goto Exit2;
+ if ( FT_READ_ULONG( rlen ) )
+ goto Exit2;
+
+ /* FT2 allocator takes signed long buffer length,
+ * too large fragment length causing overflow should be checked
+ */
+ if ( 0x7FFFFFFFUL < rlen )
+ {
+ error = FT_THROW( Invalid_Offset );
+ goto Exit2;
+ }
+
+ if ( FT_READ_USHORT( flags ) )
+ goto Exit2;
+
+ FT_TRACE3(( "POST fragment[%d]:"
+ " offsets=0x%08lx, rlen=0x%08lx, flags=0x%04x\n",
+ i, offsets[i], rlen, flags ));
+
+ error = FT_ERR( Array_Too_Large );
+
+ /* postpone the check of `rlen longer than buffer' */
+ /* until `FT_Stream_Read' */
+
+ if ( ( flags >> 8 ) == 0 ) /* Comment, should not be loaded */
+ {
+ FT_TRACE3(( " Skip POST fragment #%d because it is a comment\n",
+ i ));
+ continue;
+ }
+
+ /* the flags are part of the resource, so rlen >= 2, */
+ /* but some fonts declare rlen = 0 for empty fragment */
+ if ( rlen > 2 )
+ rlen -= 2;
+ else
+ rlen = 0;
+
+ if ( ( flags >> 8 ) == type )
+ len += rlen;
+ else
+ {
+ FT_TRACE3(( " Write POST fragment #%d header (4-byte) to buffer"
+ " %p + 0x%08lx\n",
+ i, (void*)pfb_data, pfb_lenpos ));
+
+ if ( pfb_lenpos + 3 > pfb_len + 2 )
+ goto Exit2;
+
+ pfb_data[pfb_lenpos ] = (FT_Byte)( len );
+ pfb_data[pfb_lenpos + 1] = (FT_Byte)( len >> 8 );
+ pfb_data[pfb_lenpos + 2] = (FT_Byte)( len >> 16 );
+ pfb_data[pfb_lenpos + 3] = (FT_Byte)( len >> 24 );
+
+ if ( ( flags >> 8 ) == 5 ) /* End of font mark */
+ break;
+
+ FT_TRACE3(( " Write POST fragment #%d header (6-byte) to buffer"
+ " %p + 0x%08lx\n",
+ i, (void*)pfb_data, pfb_pos ));
+
+ if ( pfb_pos + 6 > pfb_len + 2 )
+ goto Exit2;
+
+ pfb_data[pfb_pos++] = 0x80;
+
+ type = flags >> 8;
+ len = rlen;
+
+ pfb_data[pfb_pos++] = (FT_Byte)type;
+ pfb_lenpos = pfb_pos;
+ pfb_data[pfb_pos++] = 0; /* 4-byte length, fill in later */
+ pfb_data[pfb_pos++] = 0;
+ pfb_data[pfb_pos++] = 0;
+ pfb_data[pfb_pos++] = 0;
+ }
+
+ if ( pfb_pos > pfb_len || pfb_pos + rlen > pfb_len )
+ goto Exit2;
+
+ FT_TRACE3(( " Load POST fragment #%d (%ld byte) to buffer"
+ " %p + 0x%08lx\n",
+ i, rlen, (void*)pfb_data, pfb_pos ));
+
+ error = FT_Stream_Read( stream, (FT_Byte *)pfb_data + pfb_pos, rlen );
+ if ( error )
+ goto Exit2;
+
+ pfb_pos += rlen;
+ }
+
+ error = FT_ERR( Array_Too_Large );
+
+ if ( pfb_pos + 2 > pfb_len + 2 )
+ goto Exit2;
+ pfb_data[pfb_pos++] = 0x80;
+ pfb_data[pfb_pos++] = 3;
+
+ if ( pfb_lenpos + 3 > pfb_len + 2 )
+ goto Exit2;
+ pfb_data[pfb_lenpos ] = (FT_Byte)( len );
+ pfb_data[pfb_lenpos + 1] = (FT_Byte)( len >> 8 );
+ pfb_data[pfb_lenpos + 2] = (FT_Byte)( len >> 16 );
+ pfb_data[pfb_lenpos + 3] = (FT_Byte)( len >> 24 );
+
+ return open_face_from_buffer( library,
+ pfb_data,
+ pfb_pos,
+ face_index,
+ "type1",
+ aface );
+
+ Exit2:
+ if ( FT_ERR_EQ( error, Array_Too_Large ) )
+ FT_TRACE2(( " Abort due to too-short buffer to store"
+ " all POST fragments\n" ));
+ else if ( FT_ERR_EQ( error, Invalid_Offset ) )
+ FT_TRACE2(( " Abort due to invalid offset in a POST fragment\n" ));
+
+ if ( error )
+ error = FT_ERR( Cannot_Open_Resource );
+ FT_FREE( pfb_data );
+
+ Exit:
+ return error;
+ }
+
+
+ /* The resource header says we've got resource_cnt `sfnt' */
+ /* (TrueType/OpenType) resources in this file. Look through */
+ /* them for the one indicated by face_index, load it into mem, */
+ /* pass it on to the truetype driver, and return it. */
+ /* */
+ static FT_Error
+ Mac_Read_sfnt_Resource( FT_Library library,
+ FT_Stream stream,
+ FT_Long *offsets,
+ FT_Long resource_cnt,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Memory memory = library->memory;
+ FT_Byte* sfnt_data = NULL;
+ FT_Error error;
+ FT_ULong flag_offset;
+ FT_ULong rlen;
+ int is_cff;
+ FT_Long face_index_in_resource = 0;
+
+
+ if ( face_index < 0 )
+ face_index = -face_index - 1;
+ if ( face_index >= resource_cnt )
+ return FT_THROW( Cannot_Open_Resource );
+
+ flag_offset = (FT_ULong)offsets[face_index];
+ error = FT_Stream_Seek( stream, flag_offset );
+ if ( error )
+ goto Exit;
+
+ if ( FT_READ_ULONG( rlen ) )
+ goto Exit;
+ if ( !rlen )
+ return FT_THROW( Cannot_Open_Resource );
+ if ( rlen > FT_MAC_RFORK_MAX_LEN )
+ return FT_THROW( Invalid_Offset );
+
+ error = open_face_PS_from_sfnt_stream( library,
+ stream,
+ face_index,
+ 0, NULL,
+ aface );
+ if ( !error )
+ goto Exit;
+
+ /* rewind sfnt stream before open_face_PS_from_sfnt_stream() */
+ error = FT_Stream_Seek( stream, flag_offset + 4 );
+ if ( error )
+ goto Exit;
+
+ if ( FT_QALLOC( sfnt_data, rlen ) )
+ return error;
+ error = FT_Stream_Read( stream, (FT_Byte *)sfnt_data, rlen );
+ if ( error )
+ {
+ FT_FREE( sfnt_data );
+ goto Exit;
+ }
+
+ is_cff = rlen > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 );
+ error = open_face_from_buffer( library,
+ sfnt_data,
+ rlen,
+ face_index_in_resource,
+ is_cff ? "cff" : "truetype",
+ aface );
+
+ Exit:
+ return error;
+ }
+
+
+ /* Check for a valid resource fork header, or a valid dfont */
+ /* header. In a resource fork the first 16 bytes are repeated */
+ /* at the location specified by bytes 4-7. In a dfont bytes */
+ /* 4-7 point to 16 bytes of zeroes instead. */
+ /* */
+ static FT_Error
+ IsMacResource( FT_Library library,
+ FT_Stream stream,
+ FT_Long resource_offset,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Memory memory = library->memory;
+ FT_Error error;
+ FT_Long map_offset, rdata_pos;
+ FT_Long *data_offsets;
+ FT_Long count;
+
+
+ error = FT_Raccess_Get_HeaderInfo( library, stream, resource_offset,
+ &map_offset, &rdata_pos );
+ if ( error )
+ return error;
+
+ /* POST resources must be sorted to concatenate properly */
+ error = FT_Raccess_Get_DataOffsets( library, stream,
+ map_offset, rdata_pos,
+ TTAG_POST, TRUE,
+ &data_offsets, &count );
+ if ( !error )
+ {
+ error = Mac_Read_POST_Resource( library, stream, data_offsets, count,
+ face_index, aface );
+ FT_FREE( data_offsets );
+ /* POST exists in an LWFN providing a single face */
+ if ( !error )
+ (*aface)->num_faces = 1;
+ return error;
+ }
+
+ /* sfnt resources should not be sorted to preserve the face order by
+ QuickDraw API */
+ error = FT_Raccess_Get_DataOffsets( library, stream,
+ map_offset, rdata_pos,
+ TTAG_sfnt, FALSE,
+ &data_offsets, &count );
+ if ( !error )
+ {
+ FT_Long face_index_internal = face_index % count;
+
+
+ error = Mac_Read_sfnt_Resource( library, stream, data_offsets, count,
+ face_index_internal, aface );
+ FT_FREE( data_offsets );
+ if ( !error )
+ (*aface)->num_faces = count;
+ }
+
+ return error;
+ }
+
+
+ /* Check for a valid macbinary header, and if we find one */
+ /* check that the (flattened) resource fork in it is valid. */
+ /* */
+ static FT_Error
+ IsMacBinary( FT_Library library,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ unsigned char header[128];
+ FT_Error error;
+ FT_Long dlen, offset;
+
+
+ if ( !stream )
+ return FT_THROW( Invalid_Stream_Operation );
+
+ error = FT_Stream_Seek( stream, 0 );
+ if ( error )
+ goto Exit;
+
+ error = FT_Stream_Read( stream, (FT_Byte*)header, 128 );
+ if ( error )
+ goto Exit;
+
+ if ( header[ 0] != 0 ||
+ header[74] != 0 ||
+ header[82] != 0 ||
+ header[ 1] == 0 ||
+ header[ 1] > 33 ||
+ header[63] != 0 ||
+ header[2 + header[1]] != 0 ||
+ header[0x53] > 0x7F )
+ return FT_THROW( Unknown_File_Format );
+
+ dlen = ( header[0x53] << 24 ) |
+ ( header[0x54] << 16 ) |
+ ( header[0x55] << 8 ) |
+ header[0x56];
+#if 0
+ rlen = ( header[0x57] << 24 ) |
+ ( header[0x58] << 16 ) |
+ ( header[0x59] << 8 ) |
+ header[0x5A];
+#endif /* 0 */
+ offset = 128 + ( ( dlen + 127 ) & ~127 );
+
+ return IsMacResource( library, stream, offset, face_index, aface );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ load_face_in_embedded_rfork( FT_Library library,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Face *aface,
+ const FT_Open_Args *args )
+ {
+
+#undef FT_COMPONENT
+#define FT_COMPONENT raccess
+
+ FT_Memory memory = library->memory;
+ FT_Error error = FT_ERR( Unknown_File_Format );
+ FT_UInt i;
+
+ char* file_names[FT_RACCESS_N_RULES];
+ FT_Long offsets[FT_RACCESS_N_RULES];
+ FT_Error errors[FT_RACCESS_N_RULES];
+ FT_Bool is_darwin_vfs, vfs_rfork_has_no_font = FALSE; /* not tested */
+
+ FT_Open_Args args2;
+ FT_Stream stream2 = NULL;
+
+
+ FT_Raccess_Guess( library, stream,
+ args->pathname, file_names, offsets, errors );
+
+ for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
+ {
+ is_darwin_vfs = ft_raccess_rule_by_darwin_vfs( library, i );
+ if ( is_darwin_vfs && vfs_rfork_has_no_font )
+ {
+ FT_TRACE3(( "Skip rule %d: darwin vfs resource fork"
+ " is already checked and"
+ " no font is found\n",
+ i ));
+ continue;
+ }
+
+ if ( errors[i] )
+ {
+ FT_TRACE3(( "Error 0x%x has occurred in rule %d\n",
+ errors[i], i ));
+ continue;
+ }
+
+ args2.flags = FT_OPEN_PATHNAME;
+ args2.pathname = file_names[i] ? file_names[i] : args->pathname;
+
+ FT_TRACE3(( "Try rule %d: %s (offset=%ld) ...",
+ i, args2.pathname, offsets[i] ));
+
+ error = FT_Stream_New( library, &args2, &stream2 );
+ if ( is_darwin_vfs && FT_ERR_EQ( error, Cannot_Open_Stream ) )
+ vfs_rfork_has_no_font = TRUE;
+
+ if ( error )
+ {
+ FT_TRACE3(( "failed\n" ));
+ continue;
+ }
+
+ error = IsMacResource( library, stream2, offsets[i],
+ face_index, aface );
+ FT_Stream_Free( stream2, 0 );
+
+ FT_TRACE3(( "%s\n", error ? "failed": "successful" ));
+
+ if ( !error )
+ break;
+ else if ( is_darwin_vfs )
+ vfs_rfork_has_no_font = TRUE;
+ }
+
+ for (i = 0; i < FT_RACCESS_N_RULES; i++)
+ {
+ if ( file_names[i] )
+ FT_FREE( file_names[i] );
+ }
+
+ /* Caller (load_mac_face) requires FT_Err_Unknown_File_Format. */
+ if ( error )
+ error = FT_ERR( Unknown_File_Format );
+
+ return error;
+
+#undef FT_COMPONENT
+#define FT_COMPONENT objs
+
+ }
+
+
+ /* Check for some macintosh formats without Carbon framework. */
+ /* Is this a macbinary file? If so look at the resource fork. */
+ /* Is this a mac dfont file? */
+ /* Is this an old style resource fork? (in data) */
+ /* Else call load_face_in_embedded_rfork to try extra rules */
+ /* (defined in `ftrfork.c'). */
+ /* */
+ static FT_Error
+ load_mac_face( FT_Library library,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Face *aface,
+ const FT_Open_Args *args )
+ {
+ FT_Error error;
+ FT_UNUSED( args );
+
+
+ error = IsMacBinary( library, stream, face_index, aface );
+ if ( FT_ERR_EQ( error, Unknown_File_Format ) )
+ {
+
+#undef FT_COMPONENT
+#define FT_COMPONENT raccess
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_TRACE3(( "Try as dfont: " ));
+ if ( !( args->flags & FT_OPEN_MEMORY ) )
+ FT_TRACE3(( "%s ...", args->pathname ));
+#endif
+
+ error = IsMacResource( library, stream, 0, face_index, aface );
+
+ FT_TRACE3(( "%s\n", error ? "failed" : "successful" ));
+
+#undef FT_COMPONENT
+#define FT_COMPONENT objs
+
+ }
+
+ if ( ( FT_ERR_EQ( error, Unknown_File_Format ) ||
+ FT_ERR_EQ( error, Invalid_Stream_Operation ) ) &&
+ ( args->flags & FT_OPEN_PATHNAME ) )
+ error = load_face_in_embedded_rfork( library, stream,
+ face_index, aface, args );
+ return error;
+ }
+#endif
+
+#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Open_Face( FT_Library library,
+ const FT_Open_Args* args,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ return ft_open_face_internal( library, args, face_index, aface, 1 );
+ }
+
+
+ static FT_Error
+ ft_open_face_internal( FT_Library library,
+ const FT_Open_Args* args,
+ FT_Long face_index,
+ FT_Face *aface,
+ FT_Bool test_mac_fonts )
+ {
+ FT_Error error;
+ FT_Driver driver = NULL;
+ FT_Memory memory = NULL;
+ FT_Stream stream = NULL;
+ FT_Face face = NULL;
+ FT_ListNode node = NULL;
+ FT_Bool external_stream;
+ FT_Module* cur;
+ FT_Module* limit;
+
+#ifndef FT_CONFIG_OPTION_MAC_FONTS
+ FT_UNUSED( test_mac_fonts );
+#endif
+
+
+ /* only use lower 31 bits together with sign bit */
+ if ( face_index > 0 )
+ face_index &= 0x7FFFFFFFL;
+ else
+ {
+ face_index = -face_index;
+ face_index &= 0x7FFFFFFFL;
+ face_index = -face_index;
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_TRACE3(( "FT_Open_Face: " ));
+ if ( face_index < 0 )
+ FT_TRACE3(( "Requesting number of faces and named instances\n"));
+ else
+ {
+ FT_TRACE3(( "Requesting face %ld", face_index & 0xFFFFL ));
+ if ( face_index & 0x7FFF0000L )
+ FT_TRACE3(( ", named instance %ld", face_index >> 16 ));
+ FT_TRACE3(( "\n" ));
+ }
+#endif
+
+ /* test for valid `library' delayed to `FT_Stream_New' */
+
+ if ( !args )
+ return FT_THROW( Invalid_Argument );
+
+ external_stream = FT_BOOL( ( args->flags & FT_OPEN_STREAM ) &&
+ args->stream );
+
+ /* create input stream */
+ error = FT_Stream_New( library, args, &stream );
+ if ( error )
+ goto Fail3;
+
+ /* Do this error check after `FT_Stream_New` to ensure that the */
+ /* 'close' callback is called. */
+ if ( !aface && face_index >= 0 )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Fail3;
+ }
+
+ memory = library->memory;
+
+ /* If the font driver is specified in the `args' structure, use */
+ /* it. Otherwise, we scan the list of registered drivers. */
+ if ( ( args->flags & FT_OPEN_DRIVER ) && args->driver )
+ {
+ driver = FT_DRIVER( args->driver );
+
+ /* not all modules are drivers, so check... */
+ if ( FT_MODULE_IS_DRIVER( driver ) )
+ {
+ FT_Int num_params = 0;
+ FT_Parameter* params = NULL;
+
+
+ if ( args->flags & FT_OPEN_PARAMS )
+ {
+ num_params = args->num_params;
+ params = args->params;
+ }
+
+ error = open_face( driver, &stream, &external_stream, face_index,
+ num_params, params, &face );
+ if ( !error )
+ goto Success;
+ }
+ else
+ error = FT_THROW( Invalid_Handle );
+
+ FT_Stream_Free( stream, external_stream );
+ goto Fail;
+ }
+ else
+ {
+ error = FT_ERR( Missing_Module );
+
+ /* check each font driver for an appropriate format */
+ cur = library->modules;
+ limit = cur + library->num_modules;
+
+ for ( ; cur < limit; cur++ )
+ {
+ /* not all modules are font drivers, so check... */
+ if ( FT_MODULE_IS_DRIVER( cur[0] ) )
+ {
+ FT_Int num_params = 0;
+ FT_Parameter* params = NULL;
+
+
+ driver = FT_DRIVER( cur[0] );
+
+ if ( args->flags & FT_OPEN_PARAMS )
+ {
+ num_params = args->num_params;
+ params = args->params;
+ }
+
+ error = open_face( driver, &stream, &external_stream, face_index,
+ num_params, params, &face );
+ if ( !error )
+ goto Success;
+
+#ifdef FT_CONFIG_OPTION_MAC_FONTS
+ if ( test_mac_fonts &&
+ ft_strcmp( cur[0]->clazz->module_name, "truetype" ) == 0 &&
+ FT_ERR_EQ( error, Table_Missing ) )
+ {
+ /* TrueType but essential tables are missing */
+ error = FT_Stream_Seek( stream, 0 );
+ if ( error )
+ break;
+
+ error = open_face_PS_from_sfnt_stream( library,
+ stream,
+ face_index,
+ num_params,
+ params,
+ aface );
+ if ( !error )
+ {
+ FT_Stream_Free( stream, external_stream );
+ return error;
+ }
+ }
+#endif
+
+ if ( FT_ERR_NEQ( error, Unknown_File_Format ) )
+ goto Fail3;
+ }
+ }
+
+ Fail3:
+ /* If we are on the mac, and we get an */
+ /* FT_Err_Invalid_Stream_Operation it may be because we have an */
+ /* empty data fork, so we need to check the resource fork. */
+ if ( FT_ERR_NEQ( error, Cannot_Open_Stream ) &&
+ FT_ERR_NEQ( error, Unknown_File_Format ) &&
+ FT_ERR_NEQ( error, Invalid_Stream_Operation ) )
+ goto Fail2;
+
+#if !defined( FT_MACINTOSH ) && defined( FT_CONFIG_OPTION_MAC_FONTS )
+ if ( test_mac_fonts )
+ {
+ error = load_mac_face( library, stream, face_index, aface, args );
+ if ( !error )
+ {
+ /* We don't want to go to Success here. We've already done */
+ /* that. On the other hand, if we succeeded we still need to */
+ /* close this stream (we opened a different stream which */
+ /* extracted the interesting information out of this stream */
+ /* here. That stream will still be open and the face will */
+ /* point to it). */
+ FT_Stream_Free( stream, external_stream );
+ return error;
+ }
+ }
+
+ if ( FT_ERR_NEQ( error, Unknown_File_Format ) )
+ goto Fail2;
+#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */
+
+ /* no driver is able to handle this format */
+ error = FT_THROW( Unknown_File_Format );
+
+ Fail2:
+ FT_Stream_Free( stream, external_stream );
+ goto Fail;
+ }
+
+ Success:
+ FT_TRACE4(( "FT_Open_Face: New face object, adding to list\n" ));
+
+ /* add the face object to its driver's list */
+ if ( FT_QNEW( node ) )
+ goto Fail;
+
+ node->data = face;
+ /* don't assume driver is the same as face->driver, so use */
+ /* face->driver instead. */
+ FT_List_Add( &face->driver->faces_list, node );
+
+ /* now allocate a glyph slot object for the face */
+ FT_TRACE4(( "FT_Open_Face: Creating glyph slot\n" ));
+
+ if ( face_index >= 0 )
+ {
+ error = FT_New_GlyphSlot( face, NULL );
+ if ( error )
+ goto Fail;
+
+ /* finally, allocate a size object for the face */
+ {
+ FT_Size size;
+
+
+ FT_TRACE4(( "FT_Open_Face: Creating size object\n" ));
+
+ error = FT_New_Size( face, &size );
+ if ( error )
+ goto Fail;
+
+ face->size = size;
+ }
+ }
+
+ /* some checks */
+
+ if ( FT_IS_SCALABLE( face ) )
+ {
+ if ( face->height < 0 )
+ face->height = (FT_Short)-face->height;
+
+ if ( !FT_HAS_VERTICAL( face ) )
+ face->max_advance_height = (FT_Short)face->height;
+ }
+
+ if ( FT_HAS_FIXED_SIZES( face ) )
+ {
+ FT_Int i;
+
+
+ for ( i = 0; i < face->num_fixed_sizes; i++ )
+ {
+ FT_Bitmap_Size* bsize = face->available_sizes + i;
+
+
+ if ( bsize->height < 0 )
+ bsize->height = -bsize->height;
+ if ( bsize->x_ppem < 0 )
+ bsize->x_ppem = -bsize->x_ppem;
+ if ( bsize->y_ppem < 0 )
+ bsize->y_ppem = -bsize->y_ppem;
+
+ /* check whether negation actually has worked */
+ if ( bsize->height < 0 || bsize->x_ppem < 0 || bsize->y_ppem < 0 )
+ {
+ FT_TRACE0(( "FT_Open_Face:"
+ " Invalid bitmap dimensions for strike %d,"
+ " now disabled\n", i ));
+ bsize->width = 0;
+ bsize->height = 0;
+ bsize->size = 0;
+ bsize->x_ppem = 0;
+ bsize->y_ppem = 0;
+ }
+ }
+ }
+
+ /* initialize internal face data */
+ {
+ FT_Face_Internal internal = face->internal;
+
+
+ internal->transform_matrix.xx = 0x10000L;
+ internal->transform_matrix.xy = 0;
+ internal->transform_matrix.yx = 0;
+ internal->transform_matrix.yy = 0x10000L;
+
+ internal->transform_delta.x = 0;
+ internal->transform_delta.y = 0;
+
+ internal->refcount = 1;
+
+ internal->no_stem_darkening = -1;
+
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+ /* Per-face filtering can only be set up by FT_Face_Properties */
+ internal->lcd_filter_func = NULL;
+#endif
+ }
+
+ if ( aface )
+ *aface = face;
+ else
+ FT_Done_Face( face );
+
+ goto Exit;
+
+ Fail:
+ if ( node )
+ FT_Done_Face( face ); /* face must be in the driver's list */
+ else if ( face )
+ destroy_face( memory, face, driver );
+
+ Exit:
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( !error && face_index < 0 )
+ {
+ FT_TRACE3(( "FT_Open_Face: The font has %ld face%s\n",
+ face->num_faces,
+ face->num_faces == 1 ? "" : "s" ));
+ FT_TRACE3(( " and %ld named instance%s for face %ld\n",
+ face->style_flags >> 16,
+ ( face->style_flags >> 16 ) == 1 ? "" : "s",
+ -face_index - 1 ));
+ }
+#endif
+
+ FT_TRACE4(( "FT_Open_Face: Return 0x%x\n", error ));
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Attach_File( FT_Face face,
+ const char* filepathname )
+ {
+ FT_Open_Args open;
+
+
+ /* test for valid `face' delayed to `FT_Attach_Stream' */
+
+ if ( !filepathname )
+ return FT_THROW( Invalid_Argument );
+
+ open.stream = NULL;
+ open.flags = FT_OPEN_PATHNAME;
+ open.pathname = (char*)filepathname;
+
+ return FT_Attach_Stream( face, &open );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Attach_Stream( FT_Face face,
+ const FT_Open_Args* parameters )
+ {
+ FT_Stream stream;
+ FT_Error error;
+ FT_Driver driver;
+
+ FT_Driver_Class clazz;
+
+
+ /* test for valid `parameters' delayed to `FT_Stream_New' */
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ driver = face->driver;
+ if ( !driver )
+ return FT_THROW( Invalid_Driver_Handle );
+
+ error = FT_Stream_New( driver->root.library, parameters, &stream );
+ if ( error )
+ goto Exit;
+
+ /* we implement FT_Attach_Stream in each driver through the */
+ /* `attach_file' interface */
+
+ error = FT_ERR( Unimplemented_Feature );
+ clazz = driver->clazz;
+ if ( clazz->attach_file )
+ error = clazz->attach_file( face, stream );
+
+ /* close the attached stream */
+ FT_Stream_Free( stream,
+ FT_BOOL( parameters->stream &&
+ ( parameters->flags & FT_OPEN_STREAM ) ) );
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Reference_Face( FT_Face face )
+ {
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ face->internal->refcount++;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_Face( FT_Face face )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_Memory memory;
+ FT_ListNode node;
+
+
+ error = FT_ERR( Invalid_Face_Handle );
+ if ( face && face->driver )
+ {
+ face->internal->refcount--;
+ if ( face->internal->refcount > 0 )
+ error = FT_Err_Ok;
+ else
+ {
+ driver = face->driver;
+ memory = driver->root.memory;
+
+ /* find face in driver's list */
+ node = FT_List_Find( &driver->faces_list, face );
+ if ( node )
+ {
+ /* remove face object from the driver's list */
+ FT_List_Remove( &driver->faces_list, node );
+ FT_FREE( node );
+
+ /* now destroy the object proper */
+ destroy_face( memory, face, driver );
+ error = FT_Err_Ok;
+ }
+ }
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Size( FT_Face face,
+ FT_Size *asize )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Driver driver;
+ FT_Driver_Class clazz;
+
+ FT_Size size = NULL;
+ FT_ListNode node = NULL;
+
+ FT_Size_Internal internal = NULL;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !asize )
+ return FT_THROW( Invalid_Argument );
+
+ if ( !face->driver )
+ return FT_THROW( Invalid_Driver_Handle );
+
+ *asize = NULL;
+
+ driver = face->driver;
+ clazz = driver->clazz;
+ memory = face->memory;
+
+ /* Allocate new size object and perform basic initialisation */
+ if ( FT_ALLOC( size, clazz->size_object_size ) || FT_QNEW( node ) )
+ goto Exit;
+
+ size->face = face;
+
+ if ( FT_NEW( internal ) )
+ goto Exit;
+
+ size->internal = internal;
+
+ if ( clazz->init_size )
+ error = clazz->init_size( size );
+
+ /* in case of success, add to the face's list */
+ if ( !error )
+ {
+ *asize = size;
+ node->data = size;
+ FT_List_Add( &face->sizes_list, node );
+ }
+
+ Exit:
+ if ( error )
+ {
+ FT_FREE( node );
+ if ( size )
+ FT_FREE( size->internal );
+ FT_FREE( size );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_Size( FT_Size size )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_Memory memory;
+ FT_Face face;
+ FT_ListNode node;
+
+
+ if ( !size )
+ return FT_THROW( Invalid_Size_Handle );
+
+ face = size->face;
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ driver = face->driver;
+ if ( !driver )
+ return FT_THROW( Invalid_Driver_Handle );
+
+ memory = driver->root.memory;
+
+ error = FT_Err_Ok;
+ node = FT_List_Find( &face->sizes_list, size );
+ if ( node )
+ {
+ FT_List_Remove( &face->sizes_list, node );
+ FT_FREE( node );
+
+ if ( face->size == size )
+ {
+ face->size = NULL;
+ if ( face->sizes_list.head )
+ face->size = (FT_Size)(face->sizes_list.head->data);
+ }
+
+ destroy_size( memory, size, driver );
+ }
+ else
+ error = FT_THROW( Invalid_Size_Handle );
+
+ return error;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( FT_Error )
+ FT_Match_Size( FT_Face face,
+ FT_Size_Request req,
+ FT_Bool ignore_width,
+ FT_ULong* size_index )
+ {
+ FT_Int i;
+ FT_Long w, h;
+
+
+ if ( !FT_HAS_FIXED_SIZES( face ) )
+ return FT_THROW( Invalid_Face_Handle );
+
+ /* FT_Bitmap_Size doesn't provide enough info... */
+ if ( req->type != FT_SIZE_REQUEST_TYPE_NOMINAL )
+ return FT_THROW( Unimplemented_Feature );
+
+ w = FT_REQUEST_WIDTH ( req );
+ h = FT_REQUEST_HEIGHT( req );
+
+ if ( req->width && !req->height )
+ h = w;
+ else if ( !req->width && req->height )
+ w = h;
+
+ w = FT_PIX_ROUND( w );
+ h = FT_PIX_ROUND( h );
+
+ if ( !w || !h )
+ return FT_THROW( Invalid_Pixel_Size );
+
+ for ( i = 0; i < face->num_fixed_sizes; i++ )
+ {
+ FT_Bitmap_Size* bsize = face->available_sizes + i;
+
+
+ if ( h != FT_PIX_ROUND( bsize->y_ppem ) )
+ continue;
+
+ if ( w == FT_PIX_ROUND( bsize->x_ppem ) || ignore_width )
+ {
+ FT_TRACE3(( "FT_Match_Size: bitmap strike %d matches\n", i ));
+
+ if ( size_index )
+ *size_index = (FT_ULong)i;
+
+ return FT_Err_Ok;
+ }
+ }
+
+ FT_TRACE3(( "FT_Match_Size: no matching bitmap strike\n" ));
+
+ return FT_THROW( Invalid_Pixel_Size );
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( void )
+ ft_synthesize_vertical_metrics( FT_Glyph_Metrics* metrics,
+ FT_Pos advance )
+ {
+ FT_Pos height = metrics->height;
+
+
+ /* compensate for glyph with bbox above/below the baseline */
+ if ( metrics->horiBearingY < 0 )
+ {
+ if ( height < metrics->horiBearingY )
+ height = metrics->horiBearingY;
+ }
+ else if ( metrics->horiBearingY > 0 )
+ height -= metrics->horiBearingY;
+
+ /* the factor 1.2 is a heuristical value */
+ if ( !advance )
+ advance = height * 12 / 10;
+
+ metrics->vertBearingX = metrics->horiBearingX - metrics->horiAdvance / 2;
+ metrics->vertBearingY = ( advance - height ) / 2;
+ metrics->vertAdvance = advance;
+ }
+
+
+ static void
+ ft_recompute_scaled_metrics( FT_Face face,
+ FT_Size_Metrics* metrics )
+ {
+ /* Compute root ascender, descender, test height, and max_advance */
+
+#ifdef GRID_FIT_METRICS
+ metrics->ascender = FT_PIX_CEIL( FT_MulFix( face->ascender,
+ metrics->y_scale ) );
+
+ metrics->descender = FT_PIX_FLOOR( FT_MulFix( face->descender,
+ metrics->y_scale ) );
+
+ metrics->height = FT_PIX_ROUND( FT_MulFix( face->height,
+ metrics->y_scale ) );
+
+ metrics->max_advance = FT_PIX_ROUND( FT_MulFix( face->max_advance_width,
+ metrics->x_scale ) );
+#else /* !GRID_FIT_METRICS */
+ metrics->ascender = FT_MulFix( face->ascender,
+ metrics->y_scale );
+
+ metrics->descender = FT_MulFix( face->descender,
+ metrics->y_scale );
+
+ metrics->height = FT_MulFix( face->height,
+ metrics->y_scale );
+
+ metrics->max_advance = FT_MulFix( face->max_advance_width,
+ metrics->x_scale );
+#endif /* !GRID_FIT_METRICS */
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Select_Metrics( FT_Face face,
+ FT_ULong strike_index )
+ {
+ FT_Size_Metrics* metrics;
+ FT_Bitmap_Size* bsize;
+
+
+ metrics = &face->size->metrics;
+ bsize = face->available_sizes + strike_index;
+
+ metrics->x_ppem = (FT_UShort)( ( bsize->x_ppem + 32 ) >> 6 );
+ metrics->y_ppem = (FT_UShort)( ( bsize->y_ppem + 32 ) >> 6 );
+
+ if ( FT_IS_SCALABLE( face ) )
+ {
+ metrics->x_scale = FT_DivFix( bsize->x_ppem,
+ face->units_per_EM );
+ metrics->y_scale = FT_DivFix( bsize->y_ppem,
+ face->units_per_EM );
+
+ ft_recompute_scaled_metrics( face, metrics );
+ }
+ else
+ {
+ metrics->x_scale = 1L << 16;
+ metrics->y_scale = 1L << 16;
+ metrics->ascender = bsize->y_ppem;
+ metrics->descender = 0;
+ metrics->height = bsize->height << 6;
+ metrics->max_advance = bsize->x_ppem;
+ }
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Request_Metrics( FT_Face face,
+ FT_Size_Request req )
+ {
+ FT_Error error = FT_Err_Ok;
+
+ FT_Size_Metrics* metrics;
+
+
+ metrics = &face->size->metrics;
+
+ if ( FT_IS_SCALABLE( face ) )
+ {
+ FT_Long w = 0, h = 0, scaled_w = 0, scaled_h = 0;
+
+
+ switch ( req->type )
+ {
+ case FT_SIZE_REQUEST_TYPE_NOMINAL:
+ w = h = face->units_per_EM;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_REAL_DIM:
+ w = h = face->ascender - face->descender;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_BBOX:
+ w = face->bbox.xMax - face->bbox.xMin;
+ h = face->bbox.yMax - face->bbox.yMin;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_CELL:
+ w = face->max_advance_width;
+ h = face->ascender - face->descender;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_SCALES:
+ metrics->x_scale = (FT_Fixed)req->width;
+ metrics->y_scale = (FT_Fixed)req->height;
+ if ( !metrics->x_scale )
+ metrics->x_scale = metrics->y_scale;
+ else if ( !metrics->y_scale )
+ metrics->y_scale = metrics->x_scale;
+ goto Calculate_Ppem;
+
+ case FT_SIZE_REQUEST_TYPE_MAX:
+ break;
+ }
+
+ /* to be on the safe side */
+ if ( w < 0 )
+ w = -w;
+
+ if ( h < 0 )
+ h = -h;
+
+ scaled_w = FT_REQUEST_WIDTH ( req );
+ scaled_h = FT_REQUEST_HEIGHT( req );
+
+ /* determine scales */
+ if ( req->height || !req->width )
+ {
+ if ( h == 0 )
+ {
+ FT_ERROR(( "FT_Request_Metrics: Divide by zero\n" ));
+ error = FT_ERR( Divide_By_Zero );
+ goto Exit;
+ }
+
+ metrics->y_scale = FT_DivFix( scaled_h, h );
+ }
+
+ if ( req->width )
+ {
+ if ( w == 0 )
+ {
+ FT_ERROR(( "FT_Request_Metrics: Divide by zero\n" ));
+ error = FT_ERR( Divide_By_Zero );
+ goto Exit;
+ }
+
+ metrics->x_scale = FT_DivFix( scaled_w, w );
+ }
+ else
+ {
+ metrics->x_scale = metrics->y_scale;
+ scaled_w = FT_MulDiv( scaled_h, w, h );
+ }
+
+ if ( !req->height )
+ {
+ metrics->y_scale = metrics->x_scale;
+ scaled_h = FT_MulDiv( scaled_w, h, w );
+ }
+
+ if ( req->type == FT_SIZE_REQUEST_TYPE_CELL )
+ {
+ if ( metrics->y_scale > metrics->x_scale )
+ metrics->y_scale = metrics->x_scale;
+ else
+ metrics->x_scale = metrics->y_scale;
+ }
+
+ Calculate_Ppem:
+ /* calculate the ppems */
+ if ( req->type != FT_SIZE_REQUEST_TYPE_NOMINAL )
+ {
+ scaled_w = FT_MulFix( face->units_per_EM, metrics->x_scale );
+ scaled_h = FT_MulFix( face->units_per_EM, metrics->y_scale );
+ }
+
+ scaled_w = ( scaled_w + 32 ) >> 6;
+ scaled_h = ( scaled_h + 32 ) >> 6;
+ if ( scaled_w > (FT_Long)FT_USHORT_MAX ||
+ scaled_h > (FT_Long)FT_USHORT_MAX )
+ {
+ FT_ERROR(( "FT_Request_Metrics: Resulting ppem size too large\n" ));
+ error = FT_ERR( Invalid_Pixel_Size );
+ goto Exit;
+ }
+
+ metrics->x_ppem = (FT_UShort)scaled_w;
+ metrics->y_ppem = (FT_UShort)scaled_h;
+
+ ft_recompute_scaled_metrics( face, metrics );
+ }
+ else
+ {
+ FT_ZERO( metrics );
+ metrics->x_scale = 1L << 16;
+ metrics->y_scale = 1L << 16;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Select_Size( FT_Face face,
+ FT_Int strike_index )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Driver_Class clazz;
+
+
+ if ( !face || !FT_HAS_FIXED_SIZES( face ) )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( strike_index < 0 || strike_index >= face->num_fixed_sizes )
+ return FT_THROW( Invalid_Argument );
+
+ clazz = face->driver->clazz;
+
+ if ( clazz->select_size )
+ {
+ error = clazz->select_size( face->size, (FT_ULong)strike_index );
+
+ FT_TRACE5(( "FT_Select_Size (%s driver):\n",
+ face->driver->root.clazz->module_name ));
+ }
+ else
+ {
+ FT_Select_Metrics( face, (FT_ULong)strike_index );
+
+ FT_TRACE5(( "FT_Select_Size:\n" ));
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ FT_Size_Metrics* metrics = &face->size->metrics;
+
+
+ FT_TRACE5(( " x scale: %ld (%f)\n",
+ metrics->x_scale, (double)metrics->x_scale / 65536 ));
+ FT_TRACE5(( " y scale: %ld (%f)\n",
+ metrics->y_scale, (double)metrics->y_scale / 65536 ));
+ FT_TRACE5(( " ascender: %f\n",
+ (double)metrics->ascender / 64 ));
+ FT_TRACE5(( " descender: %f\n",
+ (double)metrics->descender / 64 ));
+ FT_TRACE5(( " height: %f\n",
+ (double)metrics->height / 64 ));
+ FT_TRACE5(( " max advance: %f\n",
+ (double)metrics->max_advance / 64 ));
+ FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
+ FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
+ }
+#endif
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Request_Size( FT_Face face,
+ FT_Size_Request req )
+ {
+ FT_Error error;
+ FT_Driver_Class clazz;
+ FT_ULong strike_index;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !face->size )
+ return FT_THROW( Invalid_Size_Handle );
+
+ if ( !req || req->width < 0 || req->height < 0 ||
+ req->type >= FT_SIZE_REQUEST_TYPE_MAX )
+ return FT_THROW( Invalid_Argument );
+
+ /* signal the auto-hinter to recompute its size metrics */
+ /* (if requested) */
+ face->size->internal->autohint_metrics.x_scale = 0;
+
+ clazz = face->driver->clazz;
+
+ if ( clazz->request_size )
+ {
+ error = clazz->request_size( face->size, req );
+
+ FT_TRACE5(( "FT_Request_Size (%s driver):\n",
+ face->driver->root.clazz->module_name ));
+ }
+ else if ( !FT_IS_SCALABLE( face ) && FT_HAS_FIXED_SIZES( face ) )
+ {
+ /*
+ * The reason that a driver doesn't have `request_size' defined is
+ * either that the scaling here suffices or that the supported formats
+ * are bitmap-only and size matching is not implemented.
+ *
+ * In the latter case, a simple size matching is done.
+ */
+ error = FT_Match_Size( face, req, 0, &strike_index );
+ if ( error )
+ goto Exit;
+
+ return FT_Select_Size( face, (FT_Int)strike_index );
+ }
+ else
+ {
+ error = FT_Request_Metrics( face, req );
+ if ( error )
+ goto Exit;
+
+ FT_TRACE5(( "FT_Request_Size:\n" ));
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ FT_Size_Metrics* metrics = &face->size->metrics;
+
+
+ FT_TRACE5(( " x scale: %ld (%f)\n",
+ metrics->x_scale, (double)metrics->x_scale / 65536 ));
+ FT_TRACE5(( " y scale: %ld (%f)\n",
+ metrics->y_scale, (double)metrics->y_scale / 65536 ));
+ FT_TRACE5(( " ascender: %f\n",
+ (double)metrics->ascender / 64 ));
+ FT_TRACE5(( " descender: %f\n",
+ (double)metrics->descender / 64 ));
+ FT_TRACE5(( " height: %f\n",
+ (double)metrics->height / 64 ));
+ FT_TRACE5(( " max advance: %f\n",
+ (double)metrics->max_advance / 64 ));
+ FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
+ FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
+ }
+#endif
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Char_Size( FT_Face face,
+ FT_F26Dot6 char_width,
+ FT_F26Dot6 char_height,
+ FT_UInt horz_resolution,
+ FT_UInt vert_resolution )
+ {
+ FT_Size_RequestRec req;
+
+
+ /* check of `face' delayed to `FT_Request_Size' */
+
+ if ( !char_width )
+ char_width = char_height;
+ else if ( !char_height )
+ char_height = char_width;
+
+ if ( !horz_resolution )
+ horz_resolution = vert_resolution;
+ else if ( !vert_resolution )
+ vert_resolution = horz_resolution;
+
+ if ( char_width < 1 * 64 )
+ char_width = 1 * 64;
+ if ( char_height < 1 * 64 )
+ char_height = 1 * 64;
+
+ if ( !horz_resolution )
+ horz_resolution = vert_resolution = 72;
+
+ req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
+ req.width = char_width;
+ req.height = char_height;
+ req.horiResolution = horz_resolution;
+ req.vertResolution = vert_resolution;
+
+ return FT_Request_Size( face, &req );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Pixel_Sizes( FT_Face face,
+ FT_UInt pixel_width,
+ FT_UInt pixel_height )
+ {
+ FT_Size_RequestRec req;
+
+
+ /* check of `face' delayed to `FT_Request_Size' */
+
+ if ( pixel_width == 0 )
+ pixel_width = pixel_height;
+ else if ( pixel_height == 0 )
+ pixel_height = pixel_width;
+
+ if ( pixel_width < 1 )
+ pixel_width = 1;
+ if ( pixel_height < 1 )
+ pixel_height = 1;
+
+ /* use `>=' to avoid potential compiler warning on 16bit platforms */
+ if ( pixel_width >= 0xFFFFU )
+ pixel_width = 0xFFFFU;
+ if ( pixel_height >= 0xFFFFU )
+ pixel_height = 0xFFFFU;
+
+ req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
+ req.width = (FT_Long)( pixel_width << 6 );
+ req.height = (FT_Long)( pixel_height << 6 );
+ req.horiResolution = 0;
+ req.vertResolution = 0;
+
+ return FT_Request_Size( face, &req );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Kerning( FT_Face face,
+ FT_UInt left_glyph,
+ FT_UInt right_glyph,
+ FT_UInt kern_mode,
+ FT_Vector *akerning )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Driver driver;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !akerning )
+ return FT_THROW( Invalid_Argument );
+
+ driver = face->driver;
+
+ akerning->x = 0;
+ akerning->y = 0;
+
+ if ( driver->clazz->get_kerning )
+ {
+ error = driver->clazz->get_kerning( face,
+ left_glyph,
+ right_glyph,
+ akerning );
+ if ( !error )
+ {
+ if ( kern_mode != FT_KERNING_UNSCALED )
+ {
+ akerning->x = FT_MulFix( akerning->x, face->size->metrics.x_scale );
+ akerning->y = FT_MulFix( akerning->y, face->size->metrics.y_scale );
+
+ if ( kern_mode != FT_KERNING_UNFITTED )
+ {
+ FT_Pos orig_x = akerning->x;
+ FT_Pos orig_y = akerning->y;
+
+
+ /* we scale down kerning values for small ppem values */
+ /* to avoid that rounding makes them too big. */
+ /* `25' has been determined heuristically. */
+ if ( face->size->metrics.x_ppem < 25 )
+ akerning->x = FT_MulDiv( orig_x,
+ face->size->metrics.x_ppem, 25 );
+ if ( face->size->metrics.y_ppem < 25 )
+ akerning->y = FT_MulDiv( orig_y,
+ face->size->metrics.y_ppem, 25 );
+
+ akerning->x = FT_PIX_ROUND( akerning->x );
+ akerning->y = FT_PIX_ROUND( akerning->y );
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ FT_Pos orig_x_rounded = FT_PIX_ROUND( orig_x );
+ FT_Pos orig_y_rounded = FT_PIX_ROUND( orig_y );
+
+
+ if ( akerning->x != orig_x_rounded ||
+ akerning->y != orig_y_rounded )
+ FT_TRACE5(( "FT_Get_Kerning: horizontal kerning"
+ " (%ld, %ld) scaled down to (%ld, %ld) pixels\n",
+ orig_x_rounded / 64, orig_y_rounded / 64,
+ akerning->x / 64, akerning->y / 64 ));
+ }
+#endif
+ }
+ }
+ }
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Track_Kerning( FT_Face face,
+ FT_Fixed point_size,
+ FT_Int degree,
+ FT_Fixed* akerning )
+ {
+ FT_Service_Kerning service;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !akerning )
+ return FT_THROW( Invalid_Argument );
+
+ FT_FACE_FIND_SERVICE( face, service, KERNING );
+ if ( !service )
+ return FT_THROW( Unimplemented_Feature );
+
+ error = service->get_track( face,
+ point_size,
+ degree,
+ akerning );
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Select_Charmap( FT_Face face,
+ FT_Encoding encoding )
+ {
+ FT_CharMap* cur;
+ FT_CharMap* limit;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ /* FT_ENCODING_NONE is a valid encoding for BDF, PCF, and Windows FNT */
+ if ( encoding == FT_ENCODING_NONE && !face->num_charmaps )
+ return FT_THROW( Invalid_Argument );
+
+ /* FT_ENCODING_UNICODE is special. We try to find the `best' Unicode */
+ /* charmap available, i.e., one with UCS-4 characters, if possible. */
+ /* */
+ /* This is done by find_unicode_charmap() above, to share code. */
+ if ( encoding == FT_ENCODING_UNICODE )
+ return find_unicode_charmap( face );
+
+ cur = face->charmaps;
+ if ( !cur )
+ return FT_THROW( Invalid_CharMap_Handle );
+
+ limit = cur + face->num_charmaps;
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur[0]->encoding == encoding )
+ {
+ face->charmap = cur[0];
+ return FT_Err_Ok;
+ }
+ }
+
+ return FT_THROW( Invalid_Argument );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Charmap( FT_Face face,
+ FT_CharMap charmap )
+ {
+ FT_CharMap* cur;
+ FT_CharMap* limit;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ cur = face->charmaps;
+ if ( !cur || !charmap )
+ return FT_THROW( Invalid_CharMap_Handle );
+
+ limit = cur + face->num_charmaps;
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur[0] == charmap &&
+ FT_Get_CMap_Format ( charmap ) != 14 )
+ {
+ face->charmap = cur[0];
+ return FT_Err_Ok;
+ }
+ }
+
+ return FT_THROW( Invalid_Argument );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Int )
+ FT_Get_Charmap_Index( FT_CharMap charmap )
+ {
+ FT_Int i;
+
+
+ if ( !charmap || !charmap->face )
+ return -1;
+
+ for ( i = 0; i < charmap->face->num_charmaps; i++ )
+ if ( charmap->face->charmaps[i] == charmap )
+ break;
+
+ FT_ASSERT( i < charmap->face->num_charmaps );
+
+ return i;
+ }
+
+
+ static void
+ ft_cmap_done_internal( FT_CMap cmap )
+ {
+ FT_CMap_Class clazz = cmap->clazz;
+ FT_Face face = cmap->charmap.face;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ if ( clazz->done )
+ clazz->done( cmap );
+
+ FT_FREE( cmap );
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_CMap_Done( FT_CMap cmap )
+ {
+ if ( cmap )
+ {
+ FT_Face face = cmap->charmap.face;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Error error;
+ FT_Int i, j;
+
+
+ for ( i = 0; i < face->num_charmaps; i++ )
+ {
+ if ( (FT_CMap)face->charmaps[i] == cmap )
+ {
+ FT_CharMap last_charmap = face->charmaps[face->num_charmaps - 1];
+
+
+ if ( FT_QRENEW_ARRAY( face->charmaps,
+ face->num_charmaps,
+ face->num_charmaps - 1 ) )
+ return;
+
+ /* remove it from our list of charmaps */
+ for ( j = i + 1; j < face->num_charmaps; j++ )
+ {
+ if ( j == face->num_charmaps - 1 )
+ face->charmaps[j - 1] = last_charmap;
+ else
+ face->charmaps[j - 1] = face->charmaps[j];
+ }
+
+ face->num_charmaps--;
+
+ if ( (FT_CMap)face->charmap == cmap )
+ face->charmap = NULL;
+
+ ft_cmap_done_internal( cmap );
+
+ break;
+ }
+ }
+ }
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_CMap_New( FT_CMap_Class clazz,
+ FT_Pointer init_data,
+ FT_CharMap charmap,
+ FT_CMap *acmap )
+ {
+ FT_Error error;
+ FT_Face face;
+ FT_Memory memory;
+ FT_CMap cmap = NULL;
+
+
+ if ( !clazz || !charmap || !charmap->face )
+ return FT_THROW( Invalid_Argument );
+
+ face = charmap->face;
+ memory = FT_FACE_MEMORY( face );
+
+ if ( !FT_ALLOC( cmap, clazz->size ) )
+ {
+ cmap->charmap = *charmap;
+ cmap->clazz = clazz;
+
+ if ( clazz->init )
+ {
+ error = clazz->init( cmap, init_data );
+ if ( error )
+ goto Fail;
+ }
+
+ /* add it to our list of charmaps */
+ if ( FT_QRENEW_ARRAY( face->charmaps,
+ face->num_charmaps,
+ face->num_charmaps + 1 ) )
+ goto Fail;
+
+ face->charmaps[face->num_charmaps++] = (FT_CharMap)cmap;
+ }
+
+ Exit:
+ if ( acmap )
+ *acmap = cmap;
+
+ return error;
+
+ Fail:
+ ft_cmap_done_internal( cmap );
+ cmap = NULL;
+ goto Exit;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UInt )
+ FT_Get_Char_Index( FT_Face face,
+ FT_ULong charcode )
+ {
+ FT_UInt result = 0;
+
+
+ if ( face && face->charmap )
+ {
+ FT_CMap cmap = FT_CMAP( face->charmap );
+
+
+ if ( charcode > 0xFFFFFFFFUL )
+ {
+ FT_TRACE1(( "FT_Get_Char_Index: too large charcode" ));
+ FT_TRACE1(( " 0x%lx is truncated\n", charcode ));
+ }
+
+ result = cmap->clazz->char_index( cmap, (FT_UInt32)charcode );
+ if ( result >= (FT_UInt)face->num_glyphs )
+ result = 0;
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_ULong )
+ FT_Get_First_Char( FT_Face face,
+ FT_UInt *agindex )
+ {
+ FT_ULong result = 0;
+ FT_UInt gindex = 0;
+
+
+ /* only do something if we have a charmap, and we have glyphs at all */
+ if ( face && face->charmap && face->num_glyphs )
+ {
+ gindex = FT_Get_Char_Index( face, 0 );
+ if ( gindex == 0 )
+ result = FT_Get_Next_Char( face, 0, &gindex );
+ }
+
+ if ( agindex )
+ *agindex = gindex;
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_ULong )
+ FT_Get_Next_Char( FT_Face face,
+ FT_ULong charcode,
+ FT_UInt *agindex )
+ {
+ FT_ULong result = 0;
+ FT_UInt gindex = 0;
+
+
+ if ( face && face->charmap && face->num_glyphs )
+ {
+ FT_UInt32 code = (FT_UInt32)charcode;
+ FT_CMap cmap = FT_CMAP( face->charmap );
+
+
+ do
+ {
+ gindex = cmap->clazz->char_next( cmap, &code );
+
+ } while ( gindex >= (FT_UInt)face->num_glyphs );
+
+ result = ( gindex == 0 ) ? 0 : code;
+ }
+
+ if ( agindex )
+ *agindex = gindex;
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Face_Properties( FT_Face face,
+ FT_UInt num_properties,
+ FT_Parameter* properties )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( num_properties > 0 && !properties )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ for ( ; num_properties > 0; num_properties-- )
+ {
+ if ( properties->tag == FT_PARAM_TAG_STEM_DARKENING )
+ {
+ if ( properties->data )
+ {
+ if ( *( (FT_Bool*)properties->data ) == TRUE )
+ face->internal->no_stem_darkening = FALSE;
+ else
+ face->internal->no_stem_darkening = TRUE;
+ }
+ else
+ {
+ /* use module default */
+ face->internal->no_stem_darkening = -1;
+ }
+ }
+ else if ( properties->tag == FT_PARAM_TAG_LCD_FILTER_WEIGHTS )
+ {
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+ if ( properties->data )
+ {
+ ft_memcpy( face->internal->lcd_weights,
+ properties->data,
+ FT_LCD_FILTER_FIVE_TAPS );
+ face->internal->lcd_filter_func = ft_lcd_filter_fir;
+ }
+#else
+ error = FT_THROW( Unimplemented_Feature );
+ goto Exit;
+#endif
+ }
+ else if ( properties->tag == FT_PARAM_TAG_RANDOM_SEED )
+ {
+ if ( properties->data )
+ {
+ face->internal->random_seed = *( (FT_Int32*)properties->data );
+ if ( face->internal->random_seed < 0 )
+ face->internal->random_seed = 0;
+ }
+ else
+ {
+ /* use module default */
+ face->internal->random_seed = -1;
+ }
+ }
+ else
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ if ( error )
+ break;
+
+ properties++;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UInt )
+ FT_Face_GetCharVariantIndex( FT_Face face,
+ FT_ULong charcode,
+ FT_ULong variantSelector )
+ {
+ FT_UInt result = 0;
+
+
+ if ( face &&
+ face->charmap &&
+ face->charmap->encoding == FT_ENCODING_UNICODE )
+ {
+ FT_CharMap charmap = find_variant_selector_charmap( face );
+ FT_CMap ucmap = FT_CMAP( face->charmap );
+
+
+ if ( charmap )
+ {
+ FT_CMap vcmap = FT_CMAP( charmap );
+
+
+ if ( charcode > 0xFFFFFFFFUL )
+ {
+ FT_TRACE1(( "FT_Face_GetCharVariantIndex:"
+ " too large charcode" ));
+ FT_TRACE1(( " 0x%lx is truncated\n", charcode ));
+ }
+ if ( variantSelector > 0xFFFFFFFFUL )
+ {
+ FT_TRACE1(( "FT_Face_GetCharVariantIndex:"
+ " too large variantSelector" ));
+ FT_TRACE1(( " 0x%lx is truncated\n", variantSelector ));
+ }
+
+ result = vcmap->clazz->char_var_index( vcmap, ucmap,
+ (FT_UInt32)charcode,
+ (FT_UInt32)variantSelector );
+ }
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Int )
+ FT_Face_GetCharVariantIsDefault( FT_Face face,
+ FT_ULong charcode,
+ FT_ULong variantSelector )
+ {
+ FT_Int result = -1;
+
+
+ if ( face )
+ {
+ FT_CharMap charmap = find_variant_selector_charmap( face );
+
+
+ if ( charmap )
+ {
+ FT_CMap vcmap = FT_CMAP( charmap );
+
+
+ if ( charcode > 0xFFFFFFFFUL )
+ {
+ FT_TRACE1(( "FT_Face_GetCharVariantIsDefault:"
+ " too large charcode" ));
+ FT_TRACE1(( " 0x%lx is truncated\n", charcode ));
+ }
+ if ( variantSelector > 0xFFFFFFFFUL )
+ {
+ FT_TRACE1(( "FT_Face_GetCharVariantIsDefault:"
+ " too large variantSelector" ));
+ FT_TRACE1(( " 0x%lx is truncated\n", variantSelector ));
+ }
+
+ result = vcmap->clazz->char_var_default( vcmap,
+ (FT_UInt32)charcode,
+ (FT_UInt32)variantSelector );
+ }
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UInt32* )
+ FT_Face_GetVariantSelectors( FT_Face face )
+ {
+ FT_UInt32 *result = NULL;
+
+
+ if ( face )
+ {
+ FT_CharMap charmap = find_variant_selector_charmap( face );
+
+
+ if ( charmap )
+ {
+ FT_CMap vcmap = FT_CMAP( charmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ result = vcmap->clazz->variant_list( vcmap, memory );
+ }
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UInt32* )
+ FT_Face_GetVariantsOfChar( FT_Face face,
+ FT_ULong charcode )
+ {
+ FT_UInt32 *result = NULL;
+
+
+ if ( face )
+ {
+ FT_CharMap charmap = find_variant_selector_charmap( face );
+
+
+ if ( charmap )
+ {
+ FT_CMap vcmap = FT_CMAP( charmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ if ( charcode > 0xFFFFFFFFUL )
+ {
+ FT_TRACE1(( "FT_Face_GetVariantsOfChar: too large charcode" ));
+ FT_TRACE1(( " 0x%lx is truncated\n", charcode ));
+ }
+
+ result = vcmap->clazz->charvariant_list( vcmap, memory,
+ (FT_UInt32)charcode );
+ }
+ }
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UInt32* )
+ FT_Face_GetCharsOfVariant( FT_Face face,
+ FT_ULong variantSelector )
+ {
+ FT_UInt32 *result = NULL;
+
+
+ if ( face )
+ {
+ FT_CharMap charmap = find_variant_selector_charmap( face );
+
+
+ if ( charmap )
+ {
+ FT_CMap vcmap = FT_CMAP( charmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ if ( variantSelector > 0xFFFFFFFFUL )
+ {
+ FT_TRACE1(( "FT_Get_Char_Index: too large variantSelector" ));
+ FT_TRACE1(( " 0x%lx is truncated\n", variantSelector ));
+ }
+
+ result = vcmap->clazz->variantchar_list( vcmap, memory,
+ (FT_UInt32)variantSelector );
+ }
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UInt )
+ FT_Get_Name_Index( FT_Face face,
+ const FT_String* glyph_name )
+ {
+ FT_UInt result = 0;
+
+
+ if ( face &&
+ FT_HAS_GLYPH_NAMES( face ) &&
+ glyph_name )
+ {
+ FT_Service_GlyphDict service;
+
+
+ FT_FACE_LOOKUP_SERVICE( face,
+ service,
+ GLYPH_DICT );
+
+ if ( service && service->name_index )
+ result = service->name_index( face, glyph_name );
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Glyph_Name( FT_Face face,
+ FT_UInt glyph_index,
+ FT_Pointer buffer,
+ FT_UInt buffer_max )
+ {
+ FT_Error error;
+ FT_Service_GlyphDict service;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !buffer || buffer_max == 0 )
+ return FT_THROW( Invalid_Argument );
+
+ /* clean up buffer */
+ ((FT_Byte*)buffer)[0] = '\0';
+
+ if ( (FT_Long)glyph_index >= face->num_glyphs )
+ return FT_THROW( Invalid_Glyph_Index );
+
+ if ( !FT_HAS_GLYPH_NAMES( face ) )
+ return FT_THROW( Invalid_Argument );
+
+ FT_FACE_LOOKUP_SERVICE( face, service, GLYPH_DICT );
+ if ( service && service->get_name )
+ error = service->get_name( face, glyph_index, buffer, buffer_max );
+ else
+ error = FT_THROW( Invalid_Argument );
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( const char* )
+ FT_Get_Postscript_Name( FT_Face face )
+ {
+ const char* result = NULL;
+
+
+ if ( !face )
+ goto Exit;
+
+ if ( !result )
+ {
+ FT_Service_PsFontName service;
+
+
+ FT_FACE_LOOKUP_SERVICE( face,
+ service,
+ POSTSCRIPT_FONT_NAME );
+
+ if ( service && service->get_ps_font_name )
+ result = service->get_ps_font_name( face );
+ }
+
+ Exit:
+ return result;
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( void* )
+ FT_Get_Sfnt_Table( FT_Face face,
+ FT_Sfnt_Tag tag )
+ {
+ void* table = NULL;
+ FT_Service_SFNT_Table service;
+
+
+ if ( face && FT_IS_SFNT( face ) )
+ {
+ FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
+ if ( service )
+ table = service->get_table( face, tag );
+ }
+
+ return table;
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Load_Sfnt_Table( FT_Face face,
+ FT_ULong tag,
+ FT_Long offset,
+ FT_Byte* buffer,
+ FT_ULong* length )
+ {
+ FT_Service_SFNT_Table service;
+
+
+ if ( !face || !FT_IS_SFNT( face ) )
+ return FT_THROW( Invalid_Face_Handle );
+
+ FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
+ if ( !service )
+ return FT_THROW( Unimplemented_Feature );
+
+ return service->load_table( face, tag, offset, buffer, length );
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Sfnt_Table_Info( FT_Face face,
+ FT_UInt table_index,
+ FT_ULong *tag,
+ FT_ULong *length )
+ {
+ FT_Service_SFNT_Table service;
+ FT_ULong offset;
+
+
+ /* test for valid `length' delayed to `service->table_info' */
+
+ if ( !face || !FT_IS_SFNT( face ) )
+ return FT_THROW( Invalid_Face_Handle );
+
+ FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
+ if ( !service )
+ return FT_THROW( Unimplemented_Feature );
+
+ return service->table_info( face, table_index, tag, &offset, length );
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( FT_ULong )
+ FT_Get_CMap_Language_ID( FT_CharMap charmap )
+ {
+ FT_Service_TTCMaps service;
+ FT_Face face;
+ TT_CMapInfo cmap_info;
+
+
+ if ( !charmap || !charmap->face )
+ return 0;
+
+ face = charmap->face;
+ FT_FACE_FIND_SERVICE( face, service, TT_CMAP );
+ if ( !service )
+ return 0;
+ if ( service->get_cmap_info( charmap, &cmap_info ))
+ return 0;
+
+ return cmap_info.language;
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_Get_CMap_Format( FT_CharMap charmap )
+ {
+ FT_Service_TTCMaps service;
+ FT_Face face;
+ TT_CMapInfo cmap_info;
+
+
+ if ( !charmap || !charmap->face )
+ return -1;
+
+ face = charmap->face;
+ FT_FACE_FIND_SERVICE( face, service, TT_CMAP );
+ if ( !service )
+ return -1;
+ if ( service->get_cmap_info( charmap, &cmap_info ))
+ return -1;
+
+ return cmap_info.format;
+ }
+
+
+ /* documentation is in ftsizes.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Activate_Size( FT_Size size )
+ {
+ FT_Face face;
+
+
+ if ( !size )
+ return FT_THROW( Invalid_Size_Handle );
+
+ face = size->face;
+ if ( !face || !face->driver )
+ return FT_THROW( Invalid_Face_Handle );
+
+ /* we don't need anything more complex than that; all size objects */
+ /* are already listed by the face */
+ face->size = size;
+
+ return FT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** R E N D E R E R S ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* lookup a renderer by glyph format in the library's list */
+ FT_BASE_DEF( FT_Renderer )
+ FT_Lookup_Renderer( FT_Library library,
+ FT_Glyph_Format format,
+ FT_ListNode* node )
+ {
+ FT_ListNode cur;
+ FT_Renderer result = NULL;
+
+
+ if ( !library )
+ goto Exit;
+
+ cur = library->renderers.head;
+
+ if ( node )
+ {
+ if ( *node )
+ cur = (*node)->next;
+ *node = NULL;
+ }
+
+ while ( cur )
+ {
+ FT_Renderer renderer = FT_RENDERER( cur->data );
+
+
+ if ( renderer->glyph_format == format )
+ {
+ if ( node )
+ *node = cur;
+
+ result = renderer;
+ break;
+ }
+ cur = cur->next;
+ }
+
+ Exit:
+ return result;
+ }
+
+
+ static FT_Renderer
+ ft_lookup_glyph_renderer( FT_GlyphSlot slot )
+ {
+ FT_Face face = slot->face;
+ FT_Library library = FT_FACE_LIBRARY( face );
+ FT_Renderer result = library->cur_renderer;
+
+
+ if ( !result || result->glyph_format != slot->format )
+ result = FT_Lookup_Renderer( library, slot->format, 0 );
+
+ return result;
+ }
+
+
+ static void
+ ft_set_current_renderer( FT_Library library )
+ {
+ FT_Renderer renderer;
+
+
+ renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE, 0 );
+ library->cur_renderer = renderer;
+ }
+
+
+ static FT_Error
+ ft_add_renderer( FT_Module module )
+ {
+ FT_Library library = module->library;
+ FT_Memory memory = library->memory;
+ FT_Error error;
+ FT_ListNode node = NULL;
+
+
+ if ( FT_QNEW( node ) )
+ goto Exit;
+
+ {
+ FT_Renderer render = FT_RENDERER( module );
+ FT_Renderer_Class* clazz = (FT_Renderer_Class*)module->clazz;
+
+
+ render->clazz = clazz;
+ render->glyph_format = clazz->glyph_format;
+
+ /* allocate raster object if needed */
+ if ( clazz->raster_class && clazz->raster_class->raster_new )
+ {
+ error = clazz->raster_class->raster_new( memory, &render->raster );
+ if ( error )
+ goto Fail;
+
+ render->raster_render = clazz->raster_class->raster_render;
+ render->render = clazz->render_glyph;
+ }
+
+#ifdef FT_CONFIG_OPTION_SVG
+ if ( clazz->glyph_format == FT_GLYPH_FORMAT_SVG )
+ render->render = clazz->render_glyph;
+#endif
+
+ /* add to list */
+ node->data = module;
+ FT_List_Add( &library->renderers, node );
+
+ ft_set_current_renderer( library );
+ }
+
+ Fail:
+ if ( error )
+ FT_FREE( node );
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ ft_remove_renderer( FT_Module module )
+ {
+ FT_Library library;
+ FT_Memory memory;
+ FT_ListNode node;
+
+
+ library = module->library;
+ if ( !library )
+ return;
+
+ memory = library->memory;
+
+ node = FT_List_Find( &library->renderers, module );
+ if ( node )
+ {
+ FT_Renderer render = FT_RENDERER( module );
+
+
+ /* release raster object, if any */
+ if ( render->raster )
+ render->clazz->raster_class->raster_done( render->raster );
+
+ /* remove from list */
+ FT_List_Remove( &library->renderers, node );
+ FT_FREE( node );
+
+ ft_set_current_renderer( library );
+ }
+ }
+
+
+ /* documentation is in ftrender.h */
+
+ FT_EXPORT_DEF( FT_Renderer )
+ FT_Get_Renderer( FT_Library library,
+ FT_Glyph_Format format )
+ {
+ /* test for valid `library' delayed to `FT_Lookup_Renderer' */
+
+ return FT_Lookup_Renderer( library, format, 0 );
+ }
+
+
+ /* documentation is in ftrender.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Renderer( FT_Library library,
+ FT_Renderer renderer,
+ FT_UInt num_params,
+ FT_Parameter* parameters )
+ {
+ FT_ListNode node;
+ FT_Error error = FT_Err_Ok;
+
+ FT_Renderer_SetModeFunc set_mode;
+
+
+ if ( !library )
+ {
+ error = FT_THROW( Invalid_Library_Handle );
+ goto Exit;
+ }
+
+ if ( !renderer )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ if ( num_params > 0 && !parameters )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ node = FT_List_Find( &library->renderers, renderer );
+ if ( !node )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ FT_List_Up( &library->renderers, node );
+
+ if ( renderer->glyph_format == FT_GLYPH_FORMAT_OUTLINE )
+ library->cur_renderer = renderer;
+
+ set_mode = renderer->clazz->set_mode;
+
+ for ( ; num_params > 0; num_params-- )
+ {
+ error = set_mode( renderer, parameters->tag, parameters->data );
+ if ( error )
+ break;
+ parameters++;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Render_Glyph_Internal( FT_Library library,
+ FT_GlyphSlot slot,
+ FT_Render_Mode render_mode )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Face face = slot->face;
+ FT_Renderer renderer;
+
+
+ switch ( slot->format )
+ {
+ default:
+ if ( slot->internal->load_flags & FT_LOAD_COLOR )
+ {
+ FT_LayerIterator iterator;
+
+ FT_UInt base_glyph = slot->glyph_index;
+
+ FT_Bool have_layers;
+ FT_UInt glyph_index;
+ FT_UInt color_index;
+
+
+ /* check whether we have colored glyph layers */
+ iterator.p = NULL;
+ have_layers = FT_Get_Color_Glyph_Layer( face,
+ base_glyph,
+ &glyph_index,
+ &color_index,
+ &iterator );
+ if ( have_layers )
+ {
+ error = FT_New_GlyphSlot( face, NULL );
+ if ( !error )
+ {
+ TT_Face ttface = (TT_Face)face;
+ SFNT_Service sfnt = (SFNT_Service)ttface->sfnt;
+
+
+ do
+ {
+ FT_Int32 load_flags = slot->internal->load_flags;
+
+
+ /* disable the `FT_LOAD_COLOR' flag to avoid recursion */
+ /* right here in this function */
+ load_flags &= ~FT_LOAD_COLOR;
+
+ /* render into the new `face->glyph' glyph slot */
+ load_flags |= FT_LOAD_RENDER;
+
+ error = FT_Load_Glyph( face, glyph_index, load_flags );
+ if ( error )
+ break;
+
+ /* blend new `face->glyph' into old `slot'; */
+ /* at the first call, `slot' is still empty */
+ error = sfnt->colr_blend( ttface,
+ color_index,
+ slot,
+ face->glyph );
+ if ( error )
+ break;
+
+ } while ( FT_Get_Color_Glyph_Layer( face,
+ base_glyph,
+ &glyph_index,
+ &color_index,
+ &iterator ) );
+
+ if ( !error )
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
+
+ /* this call also restores `slot' as the glyph slot */
+ FT_Done_GlyphSlot( face->glyph );
+ }
+
+ if ( !error )
+ return error;
+
+ /* Failed to do the colored layer. Draw outline instead. */
+ slot->format = FT_GLYPH_FORMAT_OUTLINE;
+ }
+ }
+
+ {
+ FT_ListNode node = NULL;
+
+
+ /* small shortcut for the very common case */
+ if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+ {
+ renderer = library->cur_renderer;
+ node = library->renderers.head;
+ }
+ else
+ renderer = FT_Lookup_Renderer( library, slot->format, &node );
+
+ error = FT_ERR( Cannot_Render_Glyph );
+ while ( renderer )
+ {
+ error = renderer->render( renderer, slot, render_mode, NULL );
+ if ( !error ||
+ FT_ERR_NEQ( error, Cannot_Render_Glyph ) )
+ break;
+
+ /* FT_Err_Cannot_Render_Glyph is returned if the render mode */
+ /* is unsupported by the current renderer for this glyph image */
+ /* format. */
+
+ /* now, look for another renderer that supports the same */
+ /* format. */
+ renderer = FT_Lookup_Renderer( library, slot->format, &node );
+ }
+
+ /* it is not an error if we cannot render a bitmap glyph */
+ if ( FT_ERR_EQ( error, Cannot_Render_Glyph ) &&
+ slot->format == FT_GLYPH_FORMAT_BITMAP )
+ error = FT_Err_Ok;
+ }
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+#undef FT_COMPONENT
+#define FT_COMPONENT checksum
+
+ /*
+ * Computing the MD5 checksum is expensive, unnecessarily distorting a
+ * possible profiling of FreeType if compiled with tracing support. For
+ * this reason, we execute the following code only if explicitly
+ * requested.
+ */
+
+ /* we use FT_TRACE3 in this block */
+ if ( !error &&
+ ft_trace_levels[trace_checksum] >= 3 &&
+ slot->bitmap.buffer )
+ {
+ FT_Bitmap bitmap;
+ FT_Error err;
+
+
+ FT_Bitmap_Init( &bitmap );
+
+ /* we convert to a single bitmap format for computing the checksum */
+ /* this also converts the bitmap flow to `down' (i.e., pitch > 0) */
+ err = FT_Bitmap_Convert( library, &slot->bitmap, &bitmap, 1 );
+ if ( !err )
+ {
+ MD5_CTX ctx;
+ unsigned char md5[16];
+ unsigned long coverage = 0;
+ int i, j;
+ int rows = (int)bitmap.rows;
+ int pitch = bitmap.pitch;
+
+
+ FT_TRACE3(( "FT_Render_Glyph: bitmap %dx%d, %s (mode %d)\n",
+ pitch,
+ rows,
+ pixel_modes[slot->bitmap.pixel_mode],
+ slot->bitmap.pixel_mode ));
+
+ for ( i = 0; i < rows; i++ )
+ for ( j = 0; j < pitch; j++ )
+ coverage += bitmap.buffer[i * pitch + j];
+
+ FT_TRACE3(( " Total coverage: %lu\n", coverage ));
+
+ MD5_Init( &ctx );
+ if ( bitmap.buffer )
+ MD5_Update( &ctx, bitmap.buffer,
+ (unsigned long)rows * (unsigned long)pitch );
+ MD5_Final( md5, &ctx );
+
+ FT_TRACE3(( " MD5 checksum: " ));
+ for ( i = 0; i < 16; i++ )
+ FT_TRACE3(( "%02X", md5[i] ));
+ FT_TRACE3(( "\n" ));
+ }
+
+ FT_Bitmap_Done( library, &bitmap );
+ }
+
+ /*
+ * Dump bitmap in Netpbm format (PBM or PGM).
+ */
+
+ /* we use FT_TRACE7 in this block */
+ if ( !error &&
+ ft_trace_levels[trace_checksum] >= 7 &&
+ slot->bitmap.buffer )
+ {
+ if ( slot->bitmap.rows < 128U &&
+ slot->bitmap.width < 128U )
+ {
+ int rows = (int)slot->bitmap.rows;
+ int width = (int)slot->bitmap.width;
+ int pitch = slot->bitmap.pitch;
+ int i, j, m;
+
+ unsigned char* topleft = slot->bitmap.buffer;
+
+
+ if ( pitch < 0 )
+ topleft -= pitch * ( rows - 1 );
+
+ FT_TRACE7(( "Netpbm image: start\n" ));
+ switch ( slot->bitmap.pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ FT_TRACE7(( "P1 %d %d\n", width, rows ));
+ for ( i = 0; i < rows; i++ )
+ {
+ for ( j = 0; j < width; )
+ for ( m = 128; m > 0 && j < width; m >>= 1, j++ )
+ FT_TRACE7(( " %d",
+ ( topleft[i * pitch + j / 8] & m ) != 0 ));
+ FT_TRACE7(( "\n" ));
+ }
+ break;
+
+ default:
+ FT_TRACE7(( "P2 %d %d 255\n", width, rows ));
+ for ( i = 0; i < rows; i++ )
+ {
+ for ( j = 0; j < width; j += 1 )
+ FT_TRACE7(( " %3u", topleft[i * pitch + j] ));
+ FT_TRACE7(( "\n" ));
+ }
+ }
+ FT_TRACE7(( "Netpbm image: end\n" ));
+ }
+ else
+ FT_TRACE7(( "Netpbm image: too large, omitted\n" ));
+ }
+
+#undef FT_COMPONENT
+#define FT_COMPONENT objs
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Render_Glyph( FT_GlyphSlot slot,
+ FT_Render_Mode render_mode )
+ {
+ FT_Library library;
+
+
+ if ( !slot || !slot->face )
+ return FT_THROW( Invalid_Argument );
+
+ library = FT_FACE_LIBRARY( slot->face );
+
+ return FT_Render_Glyph_Internal( library, slot, render_mode );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** M O D U L E S ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * Destroy_Module
+ *
+ * @Description:
+ * Destroys a given module object. For drivers, this also destroys
+ * all child faces.
+ *
+ * @InOut:
+ * module ::
+ * A handle to the target driver object.
+ *
+ * @Note:
+ * The driver _must_ be LOCKED!
+ */
+ static void
+ Destroy_Module( FT_Module module )
+ {
+ FT_Memory memory = module->memory;
+ FT_Module_Class* clazz = module->clazz;
+ FT_Library library = module->library;
+
+
+ if ( library && library->auto_hinter == module )
+ library->auto_hinter = NULL;
+
+ /* if the module is a renderer */
+ if ( FT_MODULE_IS_RENDERER( module ) )
+ ft_remove_renderer( module );
+
+ /* if the module is a font driver, add some steps */
+ if ( FT_MODULE_IS_DRIVER( module ) )
+ Destroy_Driver( FT_DRIVER( module ) );
+
+ /* finalize the module object */
+ if ( clazz->module_done )
+ clazz->module_done( module );
+
+ /* discard it */
+ FT_FREE( module );
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Add_Module( FT_Library library,
+ const FT_Module_Class* clazz )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Module module = NULL;
+ FT_UInt nn;
+
+
+#define FREETYPE_VER_FIXED ( ( (FT_Long)FREETYPE_MAJOR << 16 ) | \
+ FREETYPE_MINOR )
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !clazz )
+ return FT_THROW( Invalid_Argument );
+
+ /* check FreeType version */
+ if ( clazz->module_requires > FREETYPE_VER_FIXED )
+ return FT_THROW( Invalid_Version );
+
+ /* look for a module with the same name in the library's table */
+ for ( nn = 0; nn < library->num_modules; nn++ )
+ {
+ module = library->modules[nn];
+ if ( ft_strcmp( module->clazz->module_name, clazz->module_name ) == 0 )
+ {
+ /* this installed module has the same name, compare their versions */
+ if ( clazz->module_version <= module->clazz->module_version )
+ return FT_THROW( Lower_Module_Version );
+
+ /* remove the module from our list, then exit the loop to replace */
+ /* it by our new version.. */
+ FT_Remove_Module( library, module );
+ break;
+ }
+ }
+
+ memory = library->memory;
+ error = FT_Err_Ok;
+
+ if ( library->num_modules >= FT_MAX_MODULES )
+ {
+ error = FT_THROW( Too_Many_Drivers );
+ goto Exit;
+ }
+
+ /* allocate module object */
+ if ( FT_ALLOC( module, clazz->module_size ) )
+ goto Exit;
+
+ /* base initialization */
+ module->library = library;
+ module->memory = memory;
+ module->clazz = (FT_Module_Class*)clazz;
+
+ /* check whether the module is a renderer - this must be performed */
+ /* before the normal module initialization */
+ if ( FT_MODULE_IS_RENDERER( module ) )
+ {
+ /* add to the renderers list */
+ error = ft_add_renderer( module );
+ if ( error )
+ goto Fail;
+ }
+
+ /* is the module a auto-hinter? */
+ if ( FT_MODULE_IS_HINTER( module ) )
+ library->auto_hinter = module;
+
+ /* if the module is a font driver */
+ if ( FT_MODULE_IS_DRIVER( module ) )
+ {
+ FT_Driver driver = FT_DRIVER( module );
+
+
+ driver->clazz = (FT_Driver_Class)module->clazz;
+ }
+
+ if ( clazz->module_init )
+ {
+ error = clazz->module_init( module );
+ if ( error )
+ goto Fail;
+ }
+
+ /* add module to the library's table */
+ library->modules[library->num_modules++] = module;
+
+ Exit:
+ return error;
+
+ Fail:
+ if ( FT_MODULE_IS_RENDERER( module ) )
+ {
+ FT_Renderer renderer = FT_RENDERER( module );
+
+
+ if ( renderer->clazz &&
+ renderer->clazz->glyph_format == FT_GLYPH_FORMAT_OUTLINE &&
+ renderer->raster )
+ renderer->clazz->raster_class->raster_done( renderer->raster );
+ }
+
+ FT_FREE( module );
+ goto Exit;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Module )
+ FT_Get_Module( FT_Library library,
+ const char* module_name )
+ {
+ FT_Module result = NULL;
+ FT_Module* cur;
+ FT_Module* limit;
+
+
+ if ( !library || !module_name )
+ return result;
+
+ cur = library->modules;
+ limit = cur + library->num_modules;
+
+ for ( ; cur < limit; cur++ )
+ if ( ft_strcmp( cur[0]->clazz->module_name, module_name ) == 0 )
+ {
+ result = cur[0];
+ break;
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( const void* )
+ FT_Get_Module_Interface( FT_Library library,
+ const char* mod_name )
+ {
+ FT_Module module;
+
+
+ /* test for valid `library' delayed to FT_Get_Module() */
+
+ module = FT_Get_Module( library, mod_name );
+
+ return module ? module->clazz->module_interface : 0;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_module_get_service( FT_Module module,
+ const char* service_id,
+ FT_Bool global )
+ {
+ FT_Pointer result = NULL;
+
+
+ if ( module )
+ {
+ FT_ASSERT( module->clazz && module->clazz->get_interface );
+
+ /* first, look for the service in the module */
+ if ( module->clazz->get_interface )
+ result = module->clazz->get_interface( module, service_id );
+
+ if ( global && !result )
+ {
+ /* we didn't find it, look in all other modules then */
+ FT_Library library = module->library;
+ FT_Module* cur = library->modules;
+ FT_Module* limit = cur + library->num_modules;
+
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur[0] != module )
+ {
+ FT_ASSERT( cur[0]->clazz );
+
+ if ( cur[0]->clazz->get_interface )
+ {
+ result = cur[0]->clazz->get_interface( cur[0], service_id );
+ if ( result )
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Remove_Module( FT_Library library,
+ FT_Module module )
+ {
+ /* try to find the module from the table, then remove it from there */
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( module )
+ {
+ FT_Module* cur = library->modules;
+ FT_Module* limit = cur + library->num_modules;
+
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur[0] == module )
+ {
+ /* remove it from the table */
+ library->num_modules--;
+ limit--;
+ while ( cur < limit )
+ {
+ cur[0] = cur[1];
+ cur++;
+ }
+ limit[0] = NULL;
+
+ /* destroy the module */
+ Destroy_Module( module );
+
+ return FT_Err_Ok;
+ }
+ }
+ }
+ return FT_THROW( Invalid_Driver_Handle );
+ }
+
+
+ static FT_Error
+ ft_property_do( FT_Library library,
+ const FT_String* module_name,
+ const FT_String* property_name,
+ void* value,
+ FT_Bool set,
+ FT_Bool value_is_string )
+ {
+ FT_Module* cur;
+ FT_Module* limit;
+ FT_Module_Interface interface;
+
+ FT_Service_Properties service;
+
+#ifdef FT_DEBUG_LEVEL_ERROR
+ const FT_String* set_name = "FT_Property_Set";
+ const FT_String* get_name = "FT_Property_Get";
+ const FT_String* func_name = set ? set_name : get_name;
+#endif
+
+ FT_Bool missing_func;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !module_name || !property_name || !value )
+ return FT_THROW( Invalid_Argument );
+
+ cur = library->modules;
+ limit = cur + library->num_modules;
+
+ /* search module */
+ for ( ; cur < limit; cur++ )
+ if ( !ft_strcmp( cur[0]->clazz->module_name, module_name ) )
+ break;
+
+ if ( cur == limit )
+ {
+ FT_TRACE2(( "%s: can't find module `%s'\n",
+ func_name, module_name ));
+ return FT_THROW( Missing_Module );
+ }
+
+ /* check whether we have a service interface */
+ if ( !cur[0]->clazz->get_interface )
+ {
+ FT_TRACE2(( "%s: module `%s' doesn't support properties\n",
+ func_name, module_name ));
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+ /* search property service */
+ interface = cur[0]->clazz->get_interface( cur[0],
+ FT_SERVICE_ID_PROPERTIES );
+ if ( !interface )
+ {
+ FT_TRACE2(( "%s: module `%s' doesn't support properties\n",
+ func_name, module_name ));
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+ service = (FT_Service_Properties)interface;
+
+ if ( set )
+ missing_func = FT_BOOL( !service->set_property );
+ else
+ missing_func = FT_BOOL( !service->get_property );
+
+ if ( missing_func )
+ {
+ FT_TRACE2(( "%s: property service of module `%s' is broken\n",
+ func_name, module_name ));
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+ return set ? service->set_property( cur[0],
+ property_name,
+ value,
+ value_is_string )
+ : service->get_property( cur[0],
+ property_name,
+ value );
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Property_Set( FT_Library library,
+ const FT_String* module_name,
+ const FT_String* property_name,
+ const void* value )
+ {
+ return ft_property_do( library,
+ module_name,
+ property_name,
+ (void*)value,
+ TRUE,
+ FALSE );
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Property_Get( FT_Library library,
+ const FT_String* module_name,
+ const FT_String* property_name,
+ void* value )
+ {
+ return ft_property_do( library,
+ module_name,
+ property_name,
+ value,
+ FALSE,
+ FALSE );
+ }
+
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+
+ /* this variant is used for handling the FREETYPE_PROPERTIES */
+ /* environment variable */
+
+ FT_BASE_DEF( FT_Error )
+ ft_property_string_set( FT_Library library,
+ const FT_String* module_name,
+ const FT_String* property_name,
+ FT_String* value )
+ {
+ return ft_property_do( library,
+ module_name,
+ property_name,
+ (void*)value,
+ TRUE,
+ TRUE );
+ }
+
+#endif
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** L I B R A R Y ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Reference_Library( FT_Library library )
+ {
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ library->refcount++;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Library( FT_Memory memory,
+ FT_Library *alibrary )
+ {
+ FT_Library library = NULL;
+ FT_Error error;
+
+
+ if ( !memory || !alibrary )
+ return FT_THROW( Invalid_Argument );
+
+#ifndef FT_DEBUG_LOGGING
+#ifdef FT_DEBUG_LEVEL_ERROR
+ /* init debugging support */
+ ft_debug_init();
+#endif /* FT_DEBUG_LEVEL_ERROR */
+#endif /* !FT_DEBUG_LOGGING */
+
+ /* first of all, allocate the library object */
+ if ( FT_NEW( library ) )
+ return error;
+
+ library->memory = memory;
+
+ library->version_major = FREETYPE_MAJOR;
+ library->version_minor = FREETYPE_MINOR;
+ library->version_patch = FREETYPE_PATCH;
+
+ library->refcount = 1;
+
+ /* That's ok now */
+ *alibrary = library;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Library_Version( FT_Library library,
+ FT_Int *amajor,
+ FT_Int *aminor,
+ FT_Int *apatch )
+ {
+ FT_Int major = 0;
+ FT_Int minor = 0;
+ FT_Int patch = 0;
+
+
+ if ( library )
+ {
+ major = library->version_major;
+ minor = library->version_minor;
+ patch = library->version_patch;
+ }
+
+ if ( amajor )
+ *amajor = major;
+
+ if ( aminor )
+ *aminor = minor;
+
+ if ( apatch )
+ *apatch = patch;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_Library( FT_Library library )
+ {
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ library->refcount--;
+ if ( library->refcount > 0 )
+ goto Exit;
+
+ memory = library->memory;
+
+ /*
+ * Close all faces in the library. If we don't do this, we can have
+ * some subtle memory leaks.
+ *
+ * Example:
+ *
+ * - the cff font driver uses the pshinter module in cff_size_done
+ * - if the pshinter module is destroyed before the cff font driver,
+ * opened FT_Face objects managed by the driver are not properly
+ * destroyed, resulting in a memory leak
+ *
+ * Some faces are dependent on other faces, like Type42 faces that
+ * depend on TrueType faces synthesized internally.
+ *
+ * The order of drivers should be specified in driver_name[].
+ */
+ {
+ FT_UInt m, n;
+ const char* driver_name[] = { "type42", NULL };
+
+
+ for ( m = 0;
+ m < sizeof ( driver_name ) / sizeof ( driver_name[0] );
+ m++ )
+ {
+ for ( n = 0; n < library->num_modules; n++ )
+ {
+ FT_Module module = library->modules[n];
+ const char* module_name = module->clazz->module_name;
+ FT_List faces;
+
+
+ if ( driver_name[m] &&
+ ft_strcmp( module_name, driver_name[m] ) != 0 )
+ continue;
+
+ if ( ( module->clazz->module_flags & FT_MODULE_FONT_DRIVER ) == 0 )
+ continue;
+
+ FT_TRACE7(( "FT_Done_Library: close faces for %s\n", module_name ));
+
+ faces = &FT_DRIVER( module )->faces_list;
+ while ( faces->head )
+ {
+ FT_Done_Face( FT_FACE( faces->head->data ) );
+ if ( faces->head )
+ FT_TRACE0(( "FT_Done_Library: failed to free some faces\n" ));
+ }
+ }
+ }
+ }
+
+ /* Close all other modules in the library */
+#if 1
+ /* XXX Modules are removed in the reversed order so that */
+ /* type42 module is removed before truetype module. This */
+ /* avoids double free in some occasions. It is a hack. */
+ while ( library->num_modules > 0 )
+ FT_Remove_Module( library,
+ library->modules[library->num_modules - 1] );
+#else
+ {
+ FT_UInt n;
+
+
+ for ( n = 0; n < library->num_modules; n++ )
+ {
+ FT_Module module = library->modules[n];
+
+
+ if ( module )
+ {
+ Destroy_Module( module );
+ library->modules[n] = NULL;
+ }
+ }
+ }
+#endif
+
+ FT_FREE( library );
+
+ Exit:
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Debug_Hook( FT_Library library,
+ FT_UInt hook_index,
+ FT_DebugHook_Func debug_hook )
+ {
+ if ( library && debug_hook &&
+ hook_index <
+ ( sizeof ( library->debug_hooks ) / sizeof ( void* ) ) )
+ library->debug_hooks[hook_index] = debug_hook;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_TrueTypeEngineType )
+ FT_Get_TrueType_Engine_Type( FT_Library library )
+ {
+ FT_TrueTypeEngineType result = FT_TRUETYPE_ENGINE_TYPE_NONE;
+
+
+ if ( library )
+ {
+ FT_Module module = FT_Get_Module( library, "truetype" );
+
+
+ if ( module )
+ {
+ FT_Service_TrueTypeEngine service;
+
+
+ service = (FT_Service_TrueTypeEngine)
+ ft_module_get_service( module,
+ FT_SERVICE_ID_TRUETYPE_ENGINE,
+ 0 );
+ if ( service )
+ result = service->engine_type;
+ }
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_SubGlyph_Info( FT_GlyphSlot glyph,
+ FT_UInt sub_index,
+ FT_Int *p_index,
+ FT_UInt *p_flags,
+ FT_Int *p_arg1,
+ FT_Int *p_arg2,
+ FT_Matrix *p_transform )
+ {
+ FT_Error error = FT_ERR( Invalid_Argument );
+
+
+ if ( glyph &&
+ glyph->subglyphs &&
+ glyph->format == FT_GLYPH_FORMAT_COMPOSITE &&
+ sub_index < glyph->num_subglyphs )
+ {
+ FT_SubGlyph subg = glyph->subglyphs + sub_index;
+
+
+ *p_index = subg->index;
+ *p_flags = subg->flags;
+ *p_arg1 = subg->arg1;
+ *p_arg2 = subg->arg2;
+ *p_transform = subg->transform;
+
+ error = FT_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Get_Color_Glyph_Layer( FT_Face face,
+ FT_UInt base_glyph,
+ FT_UInt *aglyph_index,
+ FT_UInt *acolor_index,
+ FT_LayerIterator* iterator )
+ {
+ TT_Face ttface;
+ SFNT_Service sfnt;
+
+
+ if ( !face ||
+ !aglyph_index ||
+ !acolor_index ||
+ !iterator ||
+ base_glyph >= (FT_UInt)face->num_glyphs )
+ return 0;
+
+ if ( !FT_IS_SFNT( face ) )
+ return 0;
+
+ ttface = (TT_Face)face;
+ sfnt = (SFNT_Service)ttface->sfnt;
+
+ if ( sfnt->get_colr_layer )
+ return sfnt->get_colr_layer( ttface,
+ base_glyph,
+ aglyph_index,
+ acolor_index,
+ iterator );
+ else
+ return 0;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Get_Color_Glyph_Paint( FT_Face face,
+ FT_UInt base_glyph,
+ FT_Color_Root_Transform root_transform,
+ FT_OpaquePaint* paint )
+ {
+ TT_Face ttface;
+ SFNT_Service sfnt;
+
+
+ if ( !face || !paint )
+ return 0;
+
+ if ( !FT_IS_SFNT( face ) )
+ return 0;
+
+ ttface = (TT_Face)face;
+ sfnt = (SFNT_Service)ttface->sfnt;
+
+ if ( sfnt->get_colr_layer )
+ return sfnt->get_colr_glyph_paint( ttface,
+ base_glyph,
+ root_transform,
+ paint );
+ else
+ return 0;
+ }
+
+
+ /* documentation is in ftcolor.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Get_Color_Glyph_ClipBox( FT_Face face,
+ FT_UInt base_glyph,
+ FT_ClipBox* clip_box )
+ {
+ TT_Face ttface;
+ SFNT_Service sfnt;
+
+
+ if ( !face || !clip_box )
+ return 0;
+
+ if ( !FT_IS_SFNT( face ) )
+ return 0;
+
+ ttface = (TT_Face)face;
+ sfnt = (SFNT_Service)ttface->sfnt;
+
+ if ( sfnt->get_color_glyph_clipbox )
+ return sfnt->get_color_glyph_clipbox( ttface,
+ base_glyph,
+ clip_box );
+ else
+ return 0;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Get_Paint_Layers( FT_Face face,
+ FT_LayerIterator* layer_iterator,
+ FT_OpaquePaint* paint )
+ {
+ TT_Face ttface;
+ SFNT_Service sfnt;
+
+
+ if ( !face || !paint || !layer_iterator )
+ return 0;
+
+ if ( !FT_IS_SFNT( face ) )
+ return 0;
+
+ ttface = (TT_Face)face;
+ sfnt = (SFNT_Service)ttface->sfnt;
+
+ if ( sfnt->get_paint_layers )
+ return sfnt->get_paint_layers( ttface, layer_iterator, paint );
+ else
+ return 0;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Get_Paint( FT_Face face,
+ FT_OpaquePaint opaque_paint,
+ FT_COLR_Paint* paint )
+ {
+ TT_Face ttface;
+ SFNT_Service sfnt;
+
+
+ if ( !face || !paint )
+ return 0;
+
+ if ( !FT_IS_SFNT( face ) )
+ return 0;
+
+ ttface = (TT_Face)face;
+ sfnt = (SFNT_Service)ttface->sfnt;
+
+ if ( sfnt->get_paint )
+ return sfnt->get_paint( ttface, opaque_paint, paint );
+ else
+ return 0;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Get_Colorline_Stops ( FT_Face face,
+ FT_ColorStop * color_stop,
+ FT_ColorStopIterator *iterator )
+ {
+ TT_Face ttface;
+ SFNT_Service sfnt;
+
+
+ if ( !face || !color_stop || !iterator )
+ return 0;
+
+ if ( !FT_IS_SFNT( face ) )
+ return 0;
+
+ ttface = (TT_Face)face;
+ sfnt = (SFNT_Service)ttface->sfnt;
+
+ if ( sfnt->get_colorline_stops )
+ return sfnt->get_colorline_stops ( ttface, color_stop, iterator );
+ else
+ return 0;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftotval.c b/modules/freetype2/src/base/ftotval.c
new file mode 100644
index 0000000000..192e12a71f
--- /dev/null
+++ b/modules/freetype2/src/base/ftotval.c
@@ -0,0 +1,90 @@
+/****************************************************************************
+ *
+ * ftotval.c
+ *
+ * FreeType API for validating OpenType tables (body).
+ *
+ * Copyright (C) 2004-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/ftobjs.h>
+#include <freetype/internal/services/svotval.h>
+#include <freetype/ftotval.h>
+
+
+ /* documentation is in ftotval.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_OpenType_Validate( FT_Face face,
+ FT_UInt validation_flags,
+ FT_Bytes *BASE_table,
+ FT_Bytes *GDEF_table,
+ FT_Bytes *GPOS_table,
+ FT_Bytes *GSUB_table,
+ FT_Bytes *JSTF_table )
+ {
+ FT_Service_OTvalidate service;
+ FT_Error error;
+
+
+ if ( !face )
+ {
+ error = FT_THROW( Invalid_Face_Handle );
+ goto Exit;
+ }
+
+ if ( !( BASE_table &&
+ GDEF_table &&
+ GPOS_table &&
+ GSUB_table &&
+ JSTF_table ) )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, service, OPENTYPE_VALIDATE );
+
+ if ( service )
+ error = service->validate( face,
+ validation_flags,
+ BASE_table,
+ GDEF_table,
+ GPOS_table,
+ GSUB_table,
+ JSTF_table );
+ else
+ error = FT_THROW( Unimplemented_Feature );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_OpenType_Free( FT_Face face,
+ FT_Bytes table )
+ {
+ FT_Memory memory;
+
+
+ if ( !face )
+ return;
+
+ memory = FT_FACE_MEMORY( face );
+
+ FT_FREE( table );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftoutln.c b/modules/freetype2/src/base/ftoutln.c
new file mode 100644
index 0000000000..30ff21ff39
--- /dev/null
+++ b/modules/freetype2/src/base/ftoutln.c
@@ -0,0 +1,1122 @@
+/****************************************************************************
+ *
+ * ftoutln.c
+ *
+ * FreeType outline management (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/ftoutln.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftcalc.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/fttrigon.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 outline
+
+
+ static
+ const FT_Outline null_outline = { 0, 0, NULL, NULL, NULL, 0 };
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Decompose( FT_Outline* outline,
+ const FT_Outline_Funcs* func_interface,
+ void* user )
+ {
+#undef SCALED
+#define SCALED( x ) ( (x) * ( 1L << shift ) - delta )
+
+ FT_Vector v_last;
+ FT_Vector v_control;
+ FT_Vector v_start;
+
+ FT_Vector* point;
+ FT_Vector* limit;
+ char* tags;
+
+ FT_Error error;
+
+ FT_Int n; /* index of contour in outline */
+ FT_UInt first; /* index of first point in contour */
+ FT_Int tag; /* current point's state */
+
+ FT_Int shift;
+ FT_Pos delta;
+
+
+ if ( !outline )
+ return FT_THROW( Invalid_Outline );
+
+ if ( !func_interface )
+ return FT_THROW( Invalid_Argument );
+
+ shift = func_interface->shift;
+ delta = func_interface->delta;
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ FT_Int last; /* index of last point in contour */
+
+
+ FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n ));
+
+ last = outline->contours[n];
+ if ( last < 0 )
+ goto Invalid_Outline;
+ limit = outline->points + last;
+
+ v_start = outline->points[first];
+ v_start.x = SCALED( v_start.x );
+ v_start.y = SCALED( v_start.y );
+
+ v_last = outline->points[last];
+ v_last.x = SCALED( v_last.x );
+ v_last.y = SCALED( v_last.y );
+
+ v_control = v_start;
+
+ point = outline->points + first;
+ tags = outline->tags + first;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == FT_CURVE_TAG_CONIC )
+ {
+ /* first point is conic control. Yes, this happens. */
+ if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle and record its position */
+ /* for closure */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+
+ /* v_last = v_start; */
+ }
+ point--;
+ tags--;
+ }
+
+ FT_TRACE5(( " move to (%.2f, %.2f)\n",
+ (double)v_start.x / 64, (double)v_start.y / 64 ));
+ error = func_interface->move_to( &v_start, user );
+ if ( error )
+ goto Exit;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = FT_CURVE_TAG( tags[0] );
+ switch ( tag )
+ {
+ case FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ FT_TRACE5(( " line to (%.2f, %.2f)\n",
+ (double)vec.x / 64, (double)vec.y / 64 ));
+ error = func_interface->line_to( &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ case FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ v_control.x = SCALED( point->x );
+ v_control.y = SCALED( point->y );
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ FT_Vector vec;
+ FT_Vector v_middle;
+
+
+ point++;
+ tags++;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ if ( tag == FT_CURVE_TAG_ON )
+ {
+ FT_TRACE5(( " conic to (%.2f, %.2f)"
+ " with control (%.2f, %.2f)\n",
+ (double)vec.x / 64,
+ (double)vec.y / 64,
+ (double)v_control.x / 64,
+ (double)v_control.y / 64 ));
+ error = func_interface->conic_to( &v_control, &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ if ( tag != FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + vec.x ) / 2;
+ v_middle.y = ( v_control.y + vec.y ) / 2;
+
+ FT_TRACE5(( " conic to (%.2f, %.2f)"
+ " with control (%.2f, %.2f)\n",
+ (double)v_middle.x / 64,
+ (double)v_middle.y / 64,
+ (double)v_control.x / 64,
+ (double)v_control.y / 64 ));
+ error = func_interface->conic_to( &v_control, &v_middle, user );
+ if ( error )
+ goto Exit;
+
+ v_control = vec;
+ goto Do_Conic;
+ }
+
+ FT_TRACE5(( " conic to (%.2f, %.2f)"
+ " with control (%.2f, %.2f)\n",
+ (double)v_start.x / 64,
+ (double)v_start.y / 64,
+ (double)v_control.x / 64,
+ (double)v_control.y / 64 ));
+ error = func_interface->conic_to( &v_control, &v_start, user );
+ goto Close;
+
+ default: /* FT_CURVE_TAG_CUBIC */
+ {
+ FT_Vector vec1, vec2;
+
+
+ if ( point + 1 > limit ||
+ FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ vec1.x = SCALED( point[-2].x );
+ vec1.y = SCALED( point[-2].y );
+
+ vec2.x = SCALED( point[-1].x );
+ vec2.y = SCALED( point[-1].y );
+
+ if ( point <= limit )
+ {
+ FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ FT_TRACE5(( " cubic to (%.2f, %.2f)"
+ " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
+ (double)vec.x / 64,
+ (double)vec.y / 64,
+ (double)vec1.x / 64,
+ (double)vec1.y / 64,
+ (double)vec2.x / 64,
+ (double)vec2.y / 64 ));
+ error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ FT_TRACE5(( " cubic to (%.2f, %.2f)"
+ " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
+ (double)v_start.x / 64,
+ (double)v_start.y / 64,
+ (double)vec1.x / 64,
+ (double)vec1.y / 64,
+ (double)vec2.x / 64,
+ (double)vec2.y / 64 ));
+ error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
+ goto Close;
+ }
+ }
+ }
+
+ /* close the contour with a line segment */
+ FT_TRACE5(( " line to (%.2f, %.2f)\n",
+ (double)v_start.x / 64, (double)v_start.y / 64 ));
+ error = func_interface->line_to( &v_start, user );
+
+ Close:
+ if ( error )
+ goto Exit;
+
+ first = (FT_UInt)last + 1;
+ }
+
+ FT_TRACE5(( "FT_Outline_Decompose: Done\n" ));
+ return FT_Err_Ok;
+
+ Invalid_Outline:
+ error = FT_THROW( Invalid_Outline );
+ /* fall through */
+
+ Exit:
+ FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error ));
+ return error;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_New( FT_Library library,
+ FT_UInt numPoints,
+ FT_Int numContours,
+ FT_Outline *anoutline )
+ {
+ FT_Error error;
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ memory = library->memory;
+
+ if ( !anoutline || !memory )
+ return FT_THROW( Invalid_Argument );
+
+ *anoutline = null_outline;
+
+ if ( numContours < 0 ||
+ (FT_UInt)numContours > numPoints )
+ return FT_THROW( Invalid_Argument );
+
+ if ( numPoints > FT_OUTLINE_POINTS_MAX )
+ return FT_THROW( Array_Too_Large );
+
+ if ( FT_NEW_ARRAY( anoutline->points, numPoints ) ||
+ FT_NEW_ARRAY( anoutline->tags, numPoints ) ||
+ FT_NEW_ARRAY( anoutline->contours, numContours ) )
+ goto Fail;
+
+ anoutline->n_points = (FT_Short)numPoints;
+ anoutline->n_contours = (FT_Short)numContours;
+ anoutline->flags |= FT_OUTLINE_OWNER;
+
+ return FT_Err_Ok;
+
+ Fail:
+ anoutline->flags |= FT_OUTLINE_OWNER;
+ FT_Outline_Done( library, anoutline );
+
+ return error;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Check( FT_Outline* outline )
+ {
+ if ( outline )
+ {
+ FT_Int n_points = outline->n_points;
+ FT_Int n_contours = outline->n_contours;
+ FT_Int end0, end;
+ FT_Int n;
+
+
+ /* empty glyph? */
+ if ( n_points == 0 && n_contours == 0 )
+ return FT_Err_Ok;
+
+ /* check point and contour counts */
+ if ( n_points <= 0 || n_contours <= 0 )
+ goto Bad;
+
+ end0 = end = -1;
+ for ( n = 0; n < n_contours; n++ )
+ {
+ end = outline->contours[n];
+
+ /* note that we don't accept empty contours */
+ if ( end <= end0 || end >= n_points )
+ goto Bad;
+
+ end0 = end;
+ }
+
+ if ( end != n_points - 1 )
+ goto Bad;
+
+ /* XXX: check the tags array */
+ return FT_Err_Ok;
+ }
+
+ Bad:
+ return FT_THROW( Invalid_Argument );
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Copy( const FT_Outline* source,
+ FT_Outline *target )
+ {
+ FT_Int is_owner;
+
+
+ if ( !source || !target )
+ return FT_THROW( Invalid_Outline );
+
+ if ( source->n_points != target->n_points ||
+ source->n_contours != target->n_contours )
+ return FT_THROW( Invalid_Argument );
+
+ if ( source == target )
+ return FT_Err_Ok;
+
+ if ( source->n_points )
+ {
+ FT_ARRAY_COPY( target->points, source->points, source->n_points );
+ FT_ARRAY_COPY( target->tags, source->tags, source->n_points );
+ }
+
+ if ( source->n_contours )
+ FT_ARRAY_COPY( target->contours, source->contours, source->n_contours );
+
+ /* copy all flags, except the `FT_OUTLINE_OWNER' one */
+ is_owner = target->flags & FT_OUTLINE_OWNER;
+ target->flags = source->flags;
+
+ target->flags &= ~FT_OUTLINE_OWNER;
+ target->flags |= is_owner;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Done( FT_Library library,
+ FT_Outline* outline )
+ {
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !outline )
+ return FT_THROW( Invalid_Outline );
+
+ memory = library->memory;
+
+ if ( !memory )
+ return FT_THROW( Invalid_Argument );
+
+ if ( outline->flags & FT_OUTLINE_OWNER )
+ {
+ FT_FREE( outline->points );
+ FT_FREE( outline->tags );
+ FT_FREE( outline->contours );
+ }
+ *outline = null_outline;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Outline_Get_CBox( const FT_Outline* outline,
+ FT_BBox *acbox )
+ {
+ FT_Pos xMin, yMin, xMax, yMax;
+
+
+ if ( outline && acbox )
+ {
+ if ( outline->n_points == 0 )
+ {
+ xMin = 0;
+ yMin = 0;
+ xMax = 0;
+ yMax = 0;
+ }
+ else
+ {
+ FT_Vector* vec = outline->points;
+ FT_Vector* limit = vec + outline->n_points;
+
+
+ xMin = xMax = vec->x;
+ yMin = yMax = vec->y;
+ vec++;
+
+ for ( ; vec < limit; vec++ )
+ {
+ FT_Pos x, y;
+
+
+ x = vec->x;
+ if ( x < xMin ) xMin = x;
+ if ( x > xMax ) xMax = x;
+
+ y = vec->y;
+ if ( y < yMin ) yMin = y;
+ if ( y > yMax ) yMax = y;
+ }
+ }
+ acbox->xMin = xMin;
+ acbox->xMax = xMax;
+ acbox->yMin = yMin;
+ acbox->yMax = yMax;
+ }
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Outline_Translate( const FT_Outline* outline,
+ FT_Pos xOffset,
+ FT_Pos yOffset )
+ {
+ FT_UShort n;
+ FT_Vector* vec;
+
+
+ if ( !outline )
+ return;
+
+ vec = outline->points;
+
+ for ( n = 0; n < outline->n_points; n++ )
+ {
+ vec->x = ADD_LONG( vec->x, xOffset );
+ vec->y = ADD_LONG( vec->y, yOffset );
+ vec++;
+ }
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Outline_Reverse( FT_Outline* outline )
+ {
+ FT_UShort n;
+ FT_Int first, last;
+
+
+ if ( !outline )
+ return;
+
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ last = outline->contours[n];
+
+ /* reverse point table */
+ {
+ FT_Vector* p = outline->points + first;
+ FT_Vector* q = outline->points + last;
+ FT_Vector swap;
+
+
+ while ( p < q )
+ {
+ swap = *p;
+ *p = *q;
+ *q = swap;
+ p++;
+ q--;
+ }
+ }
+
+ /* reverse tags table */
+ {
+ char* p = outline->tags + first;
+ char* q = outline->tags + last;
+
+
+ while ( p < q )
+ {
+ char swap;
+
+
+ swap = *p;
+ *p = *q;
+ *q = swap;
+ p++;
+ q--;
+ }
+ }
+
+ first = last + 1;
+ }
+
+ outline->flags ^= FT_OUTLINE_REVERSE_FILL;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Render( FT_Library library,
+ FT_Outline* outline,
+ FT_Raster_Params* params )
+ {
+ FT_Error error;
+ FT_Renderer renderer;
+ FT_ListNode node;
+ FT_BBox cbox;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !outline )
+ return FT_THROW( Invalid_Outline );
+
+ if ( !params )
+ return FT_THROW( Invalid_Argument );
+
+ FT_Outline_Get_CBox( outline, &cbox );
+ if ( cbox.xMin < -0x1000000L || cbox.yMin < -0x1000000L ||
+ cbox.xMax > 0x1000000L || cbox.yMax > 0x1000000L )
+ return FT_THROW( Invalid_Outline );
+
+ renderer = library->cur_renderer;
+ node = library->renderers.head;
+
+ params->source = (void*)outline;
+
+ /* preset clip_box for direct mode */
+ if ( params->flags & FT_RASTER_FLAG_DIRECT &&
+ !( params->flags & FT_RASTER_FLAG_CLIP ) )
+ {
+ params->clip_box.xMin = cbox.xMin >> 6;
+ params->clip_box.yMin = cbox.yMin >> 6;
+ params->clip_box.xMax = ( cbox.xMax + 63 ) >> 6;
+ params->clip_box.yMax = ( cbox.yMax + 63 ) >> 6;
+ }
+
+ error = FT_ERR( Cannot_Render_Glyph );
+ while ( renderer )
+ {
+ error = renderer->raster_render( renderer->raster, params );
+ if ( !error || FT_ERR_NEQ( error, Cannot_Render_Glyph ) )
+ break;
+
+ /* FT_Err_Cannot_Render_Glyph is returned if the render mode */
+ /* is unsupported by the current renderer for this glyph image */
+ /* format */
+
+ /* now, look for another renderer that supports the same */
+ /* format */
+ renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE,
+ &node );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Get_Bitmap( FT_Library library,
+ FT_Outline* outline,
+ const FT_Bitmap *abitmap )
+ {
+ FT_Raster_Params params;
+
+
+ if ( !abitmap )
+ return FT_THROW( Invalid_Argument );
+
+ /* other checks are delayed to `FT_Outline_Render' */
+
+ params.target = abitmap;
+ params.flags = 0;
+
+ if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY ||
+ abitmap->pixel_mode == FT_PIXEL_MODE_LCD ||
+ abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
+ params.flags |= FT_RASTER_FLAG_AA;
+
+ return FT_Outline_Render( library, outline, &params );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_Transform( FT_Vector* vector,
+ const FT_Matrix* matrix )
+ {
+ FT_Pos xz, yz;
+
+
+ if ( !vector || !matrix )
+ return;
+
+ xz = FT_MulFix( vector->x, matrix->xx ) +
+ FT_MulFix( vector->y, matrix->xy );
+
+ yz = FT_MulFix( vector->x, matrix->yx ) +
+ FT_MulFix( vector->y, matrix->yy );
+
+ vector->x = xz;
+ vector->y = yz;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Outline_Transform( const FT_Outline* outline,
+ const FT_Matrix* matrix )
+ {
+ FT_Vector* vec;
+ FT_Vector* limit;
+
+
+ if ( !outline || !matrix || !outline->points )
+ return;
+
+ vec = outline->points;
+ limit = vec + outline->n_points;
+
+ for ( ; vec < limit; vec++ )
+ FT_Vector_Transform( vec, matrix );
+ }
+
+
+#if 0
+
+#define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \
+ do \
+ { \
+ (first) = ( c > 0 ) ? (outline)->points + \
+ (outline)->contours[c - 1] + 1 \
+ : (outline)->points; \
+ (last) = (outline)->points + (outline)->contours[c]; \
+ } while ( 0 )
+
+
+ /* Is a point in some contour? */
+ /* */
+ /* We treat every point of the contour as if it */
+ /* it were ON. That is, we allow false positives, */
+ /* but disallow false negatives. (XXX really?) */
+ static FT_Bool
+ ft_contour_has( FT_Outline* outline,
+ FT_Short c,
+ FT_Vector* point )
+ {
+ FT_Vector* first;
+ FT_Vector* last;
+ FT_Vector* a;
+ FT_Vector* b;
+ FT_UInt n = 0;
+
+
+ FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
+
+ for ( a = first; a <= last; a++ )
+ {
+ FT_Pos x;
+ FT_Int intersect;
+
+
+ b = ( a == last ) ? first : a + 1;
+
+ intersect = ( a->y - point->y ) ^ ( b->y - point->y );
+
+ /* a and b are on the same side */
+ if ( intersect >= 0 )
+ {
+ if ( intersect == 0 && a->y == point->y )
+ {
+ if ( ( a->x <= point->x && b->x >= point->x ) ||
+ ( a->x >= point->x && b->x <= point->x ) )
+ return 1;
+ }
+
+ continue;
+ }
+
+ x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y );
+
+ if ( x < point->x )
+ n++;
+ else if ( x == point->x )
+ return 1;
+ }
+
+ return n & 1;
+ }
+
+
+ static FT_Bool
+ ft_contour_enclosed( FT_Outline* outline,
+ FT_UShort c )
+ {
+ FT_Vector* first;
+ FT_Vector* last;
+ FT_Short i;
+
+
+ FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
+
+ for ( i = 0; i < outline->n_contours; i++ )
+ {
+ if ( i != c && ft_contour_has( outline, i, first ) )
+ {
+ FT_Vector* pt;
+
+
+ for ( pt = first + 1; pt <= last; pt++ )
+ if ( !ft_contour_has( outline, i, pt ) )
+ return 0;
+
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+
+ /* This version differs from the public one in that each */
+ /* part (contour not enclosed in another contour) of the */
+ /* outline is checked for orientation. This is */
+ /* necessary for some buggy CJK fonts. */
+ static FT_Orientation
+ ft_outline_get_orientation( FT_Outline* outline )
+ {
+ FT_Short i;
+ FT_Vector* first;
+ FT_Vector* last;
+ FT_Orientation orient = FT_ORIENTATION_NONE;
+
+
+ first = outline->points;
+ for ( i = 0; i < outline->n_contours; i++, first = last + 1 )
+ {
+ FT_Vector* point;
+ FT_Vector* xmin_point;
+ FT_Pos xmin;
+
+
+ last = outline->points + outline->contours[i];
+
+ /* skip degenerate contours */
+ if ( last < first + 2 )
+ continue;
+
+ if ( ft_contour_enclosed( outline, i ) )
+ continue;
+
+ xmin = first->x;
+ xmin_point = first;
+
+ for ( point = first + 1; point <= last; point++ )
+ {
+ if ( point->x < xmin )
+ {
+ xmin = point->x;
+ xmin_point = point;
+ }
+ }
+
+ /* check the orientation of the contour */
+ {
+ FT_Vector* prev;
+ FT_Vector* next;
+ FT_Orientation o;
+
+
+ prev = ( xmin_point == first ) ? last : xmin_point - 1;
+ next = ( xmin_point == last ) ? first : xmin_point + 1;
+
+ if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) >
+ FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) )
+ o = FT_ORIENTATION_POSTSCRIPT;
+ else
+ o = FT_ORIENTATION_TRUETYPE;
+
+ if ( orient == FT_ORIENTATION_NONE )
+ orient = o;
+ else if ( orient != o )
+ return FT_ORIENTATION_NONE;
+ }
+ }
+
+ return orient;
+ }
+
+#endif /* 0 */
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Embolden( FT_Outline* outline,
+ FT_Pos strength )
+ {
+ return FT_Outline_EmboldenXY( outline, strength, strength );
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_EmboldenXY( FT_Outline* outline,
+ FT_Pos xstrength,
+ FT_Pos ystrength )
+ {
+ FT_Vector* points;
+ FT_Int c, first, last;
+ FT_Orientation orientation;
+
+
+ if ( !outline )
+ return FT_THROW( Invalid_Outline );
+
+ xstrength /= 2;
+ ystrength /= 2;
+ if ( xstrength == 0 && ystrength == 0 )
+ return FT_Err_Ok;
+
+ orientation = FT_Outline_Get_Orientation( outline );
+ if ( orientation == FT_ORIENTATION_NONE )
+ {
+ if ( outline->n_contours )
+ return FT_THROW( Invalid_Argument );
+ else
+ return FT_Err_Ok;
+ }
+
+ points = outline->points;
+
+ first = 0;
+ for ( c = 0; c < outline->n_contours; c++ )
+ {
+ FT_Vector in, out, anchor, shift;
+ FT_Fixed l_in, l_out, l_anchor = 0, l, q, d;
+ FT_Int i, j, k;
+
+
+ l_in = 0;
+ last = outline->contours[c];
+
+ /* pacify compiler */
+ in.x = in.y = anchor.x = anchor.y = 0;
+
+ /* Counter j cycles though the points; counter i advances only */
+ /* when points are moved; anchor k marks the first moved point. */
+ for ( i = last, j = first, k = -1;
+ j != i && i != k;
+ j = j < last ? j + 1 : first )
+ {
+ if ( j != k )
+ {
+ out.x = points[j].x - points[i].x;
+ out.y = points[j].y - points[i].y;
+ l_out = (FT_Fixed)FT_Vector_NormLen( &out );
+
+ if ( l_out == 0 )
+ continue;
+ }
+ else
+ {
+ out = anchor;
+ l_out = l_anchor;
+ }
+
+ if ( l_in != 0 )
+ {
+ if ( k < 0 )
+ {
+ k = i;
+ anchor = in;
+ l_anchor = l_in;
+ }
+
+ d = FT_MulFix( in.x, out.x ) + FT_MulFix( in.y, out.y );
+
+ /* shift only if turn is less than ~160 degrees */
+ if ( d > -0xF000L )
+ {
+ d = d + 0x10000L;
+
+ /* shift components along lateral bisector in proper orientation */
+ shift.x = in.y + out.y;
+ shift.y = in.x + out.x;
+
+ if ( orientation == FT_ORIENTATION_TRUETYPE )
+ shift.x = -shift.x;
+ else
+ shift.y = -shift.y;
+
+ /* restrict shift magnitude to better handle collapsing segments */
+ q = FT_MulFix( out.x, in.y ) - FT_MulFix( out.y, in.x );
+ if ( orientation == FT_ORIENTATION_TRUETYPE )
+ q = -q;
+
+ l = FT_MIN( l_in, l_out );
+
+ /* non-strict inequalities avoid divide-by-zero when q == l == 0 */
+ if ( FT_MulFix( xstrength, q ) <= FT_MulFix( l, d ) )
+ shift.x = FT_MulDiv( shift.x, xstrength, d );
+ else
+ shift.x = FT_MulDiv( shift.x, l, q );
+
+
+ if ( FT_MulFix( ystrength, q ) <= FT_MulFix( l, d ) )
+ shift.y = FT_MulDiv( shift.y, ystrength, d );
+ else
+ shift.y = FT_MulDiv( shift.y, l, q );
+ }
+ else
+ shift.x = shift.y = 0;
+
+ for ( ;
+ i != j;
+ i = i < last ? i + 1 : first )
+ {
+ points[i].x += xstrength + shift.x;
+ points[i].y += ystrength + shift.y;
+ }
+ }
+ else
+ i = j;
+
+ in = out;
+ l_in = l_out;
+ }
+
+ first = last + 1;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Orientation )
+ FT_Outline_Get_Orientation( FT_Outline* outline )
+ {
+ FT_BBox cbox = { 0, 0, 0, 0 };
+ FT_Int xshift, yshift;
+ FT_Vector* points;
+ FT_Vector v_prev, v_cur;
+ FT_Int c, n, first;
+ FT_Pos area = 0;
+
+
+ if ( !outline || outline->n_points <= 0 )
+ return FT_ORIENTATION_TRUETYPE;
+
+ /* We use the nonzero winding rule to find the orientation. */
+ /* Since glyph outlines behave much more `regular' than arbitrary */
+ /* cubic or quadratic curves, this test deals with the polygon */
+ /* only that is spanned up by the control points. */
+
+ FT_Outline_Get_CBox( outline, &cbox );
+
+ /* Handle collapsed outlines to avoid undefined FT_MSB. */
+ if ( cbox.xMin == cbox.xMax || cbox.yMin == cbox.yMax )
+ return FT_ORIENTATION_NONE;
+
+ /* Reject values large outlines. */
+ if ( cbox.xMin < -0x1000000L || cbox.yMin < -0x1000000L ||
+ cbox.xMax > 0x1000000L || cbox.yMax > 0x1000000L )
+ return FT_ORIENTATION_NONE;
+
+ xshift = FT_MSB( (FT_UInt32)( FT_ABS( cbox.xMax ) |
+ FT_ABS( cbox.xMin ) ) ) - 14;
+ xshift = FT_MAX( xshift, 0 );
+
+ yshift = FT_MSB( (FT_UInt32)( cbox.yMax - cbox.yMin ) ) - 14;
+ yshift = FT_MAX( yshift, 0 );
+
+ points = outline->points;
+
+ first = 0;
+ for ( c = 0; c < outline->n_contours; c++ )
+ {
+ FT_Int last = outline->contours[c];
+
+
+ v_prev.x = points[last].x >> xshift;
+ v_prev.y = points[last].y >> yshift;
+
+ for ( n = first; n <= last; n++ )
+ {
+ v_cur.x = points[n].x >> xshift;
+ v_cur.y = points[n].y >> yshift;
+
+ area = ADD_LONG( area,
+ MUL_LONG( v_cur.y - v_prev.y,
+ v_cur.x + v_prev.x ) );
+
+ v_prev = v_cur;
+ }
+
+ first = last + 1;
+ }
+
+ if ( area > 0 )
+ return FT_ORIENTATION_POSTSCRIPT;
+ else if ( area < 0 )
+ return FT_ORIENTATION_TRUETYPE;
+ else
+ return FT_ORIENTATION_NONE;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftpatent.c b/modules/freetype2/src/base/ftpatent.c
new file mode 100644
index 0000000000..cb5efadffb
--- /dev/null
+++ b/modules/freetype2/src/base/ftpatent.c
@@ -0,0 +1,50 @@
+/****************************************************************************
+ *
+ * ftpatent.c
+ *
+ * FreeType API for checking patented TrueType bytecode instructions
+ * (body). Obsolete, retained for backward compatibility.
+ *
+ * Copyright (C) 2007-2023 by
+ * David Turner.
+ *
+ * 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/freetype.h>
+#include <freetype/tttags.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/services/svsfnt.h>
+#include <freetype/internal/services/svttglyf.h>
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Face_CheckTrueTypePatents( FT_Face face )
+ {
+ FT_UNUSED( face );
+
+ return FALSE;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Face_SetUnpatentedHinting( FT_Face face,
+ FT_Bool value )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( value );
+
+ return FALSE;
+ }
+
+/* END */
diff --git a/modules/freetype2/src/base/ftpfr.c b/modules/freetype2/src/base/ftpfr.c
new file mode 100644
index 0000000000..378385a591
--- /dev/null
+++ b/modules/freetype2/src/base/ftpfr.c
@@ -0,0 +1,152 @@
+/****************************************************************************
+ *
+ * ftpfr.c
+ *
+ * FreeType API for accessing PFR-specific data (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 <freetype/internal/ftobjs.h>
+#include <freetype/internal/services/svpfr.h>
+
+
+ /* check the format */
+ static FT_Service_PfrMetrics
+ ft_pfr_check( FT_Face face )
+ {
+ FT_Service_PfrMetrics service = NULL;
+
+
+ if ( face )
+ FT_FACE_LOOKUP_SERVICE( face, service, PFR_METRICS );
+
+ return service;
+ }
+
+
+ /* documentation is in ftpfr.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PFR_Metrics( FT_Face face,
+ FT_UInt *aoutline_resolution,
+ FT_UInt *ametrics_resolution,
+ FT_Fixed *ametrics_x_scale,
+ FT_Fixed *ametrics_y_scale )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Service_PfrMetrics service;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ service = ft_pfr_check( face );
+ if ( service )
+ {
+ error = service->get_metrics( face,
+ aoutline_resolution,
+ ametrics_resolution,
+ ametrics_x_scale,
+ ametrics_y_scale );
+ }
+ else
+ {
+ FT_Fixed x_scale, y_scale;
+
+
+ /* this is not a PFR font */
+ if ( aoutline_resolution )
+ *aoutline_resolution = face->units_per_EM;
+
+ if ( ametrics_resolution )
+ *ametrics_resolution = face->units_per_EM;
+
+ x_scale = y_scale = 0x10000L;
+ if ( face->size )
+ {
+ x_scale = face->size->metrics.x_scale;
+ y_scale = face->size->metrics.y_scale;
+ }
+
+ if ( ametrics_x_scale )
+ *ametrics_x_scale = x_scale;
+
+ if ( ametrics_y_scale )
+ *ametrics_y_scale = y_scale;
+
+ error = FT_THROW( Unknown_File_Format );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftpfr.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PFR_Kerning( FT_Face face,
+ FT_UInt left,
+ FT_UInt right,
+ FT_Vector *avector )
+ {
+ FT_Error error;
+ FT_Service_PfrMetrics service;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !avector )
+ return FT_THROW( Invalid_Argument );
+
+ service = ft_pfr_check( face );
+ if ( service )
+ error = service->get_kerning( face, left, right, avector );
+ else
+ error = FT_Get_Kerning( face, left, right,
+ FT_KERNING_UNSCALED, avector );
+
+ return error;
+ }
+
+
+ /* documentation is in ftpfr.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PFR_Advance( FT_Face face,
+ FT_UInt gindex,
+ FT_Pos *aadvance )
+ {
+ FT_Error error;
+ FT_Service_PfrMetrics service;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !aadvance )
+ return FT_THROW( Invalid_Argument );
+
+ service = ft_pfr_check( face );
+ if ( service )
+ error = service->get_advance( face, gindex, aadvance );
+ else
+ /* XXX: TODO: PROVIDE ADVANCE-LOADING METHOD TO ALL FONT DRIVERS */
+ error = FT_THROW( Invalid_Argument );
+
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftpsprop.c b/modules/freetype2/src/base/ftpsprop.c
new file mode 100644
index 0000000000..cefdf489d7
--- /dev/null
+++ b/modules/freetype2/src/base/ftpsprop.c
@@ -0,0 +1,284 @@
+/****************************************************************************
+ *
+ * ftpsprop.c
+ *
+ * Get and set properties of PostScript drivers (body).
+ * See `ftdriver.h' for available properties.
+ *
+ * Copyright (C) 2017-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/ftdriver.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/psaux.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftpsprop.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 psprops
+
+
+ FT_BASE_CALLBACK_DEF( FT_Error )
+ ps_property_set( FT_Module module, /* PS_Driver */
+ const char* property_name,
+ const void* value,
+ FT_Bool value_is_string )
+ {
+ FT_Error error = FT_Err_Ok;
+ PS_Driver driver = (PS_Driver)module;
+
+#ifndef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ FT_UNUSED( value_is_string );
+#endif
+
+
+ if ( !ft_strcmp( property_name, "darkening-parameters" ) )
+ {
+ FT_Int* darken_params;
+ FT_Int x1, y1, x2, y2, x3, y3, x4, y4;
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ FT_Int dp[8];
+
+
+ if ( value_is_string )
+ {
+ const char* s = (const char*)value;
+ char* ep;
+ int i;
+
+
+ /* eight comma-separated numbers */
+ for ( i = 0; i < 7; i++ )
+ {
+ dp[i] = (FT_Int)ft_strtol( s, &ep, 10 );
+ if ( *ep != ',' || s == ep )
+ return FT_THROW( Invalid_Argument );
+
+ s = ep + 1;
+ }
+
+ dp[7] = (FT_Int)ft_strtol( s, &ep, 10 );
+ if ( !( *ep == '\0' || *ep == ' ' ) || s == ep )
+ return FT_THROW( Invalid_Argument );
+
+ darken_params = dp;
+ }
+ else
+#endif
+ darken_params = (FT_Int*)value;
+
+ x1 = darken_params[0];
+ y1 = darken_params[1];
+ x2 = darken_params[2];
+ y2 = darken_params[3];
+ x3 = darken_params[4];
+ y3 = darken_params[5];
+ x4 = darken_params[6];
+ y4 = darken_params[7];
+
+ if ( x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 ||
+ y1 < 0 || y2 < 0 || y3 < 0 || y4 < 0 ||
+ x1 > x2 || x2 > x3 || x3 > x4 ||
+ y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 )
+ return FT_THROW( Invalid_Argument );
+
+ driver->darken_params[0] = x1;
+ driver->darken_params[1] = y1;
+ driver->darken_params[2] = x2;
+ driver->darken_params[3] = y2;
+ driver->darken_params[4] = x3;
+ driver->darken_params[5] = y3;
+ driver->darken_params[6] = x4;
+ driver->darken_params[7] = y4;
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "hinting-engine" ) )
+ {
+#if defined( CFF_CONFIG_OPTION_OLD_ENGINE ) || \
+ defined( T1_CONFIG_OPTION_OLD_ENGINE )
+ const char* module_name = module->clazz->module_name;
+#endif
+
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ if ( value_is_string )
+ {
+ const char* s = (const char*)value;
+
+
+ if ( !ft_strcmp( s, "adobe" ) )
+ driver->hinting_engine = FT_HINTING_ADOBE;
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ else if ( !ft_strcmp( module_name, "cff" ) &&
+ !ft_strcmp( s, "freetype" ) )
+ driver->hinting_engine = FT_HINTING_FREETYPE;
+#endif
+
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ else if ( ( !ft_strcmp( module_name, "type1" ) ||
+ !ft_strcmp( module_name, "t1cid" ) ) &&
+ !ft_strcmp( s, "freetype" ) )
+ driver->hinting_engine = FT_HINTING_FREETYPE;
+#endif
+
+ else
+ return FT_THROW( Invalid_Argument );
+ }
+ else
+#endif /* FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES */
+ {
+ FT_UInt* hinting_engine = (FT_UInt*)value;
+
+
+ if ( *hinting_engine == FT_HINTING_ADOBE
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ || ( *hinting_engine == FT_HINTING_FREETYPE &&
+ !ft_strcmp( module_name, "cff" ) )
+#endif
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ || ( *hinting_engine == FT_HINTING_FREETYPE &&
+ ( !ft_strcmp( module_name, "type1" ) ||
+ !ft_strcmp( module_name, "t1cid" ) ) )
+#endif
+ )
+ driver->hinting_engine = *hinting_engine;
+ else
+ error = FT_ERR( Unimplemented_Feature );
+ }
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
+ {
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ if ( value_is_string )
+ {
+ const char* s = (const char*)value;
+ long nsd = ft_strtol( s, NULL, 10 );
+
+
+ if ( !nsd )
+ driver->no_stem_darkening = FALSE;
+ else
+ driver->no_stem_darkening = TRUE;
+ }
+ else
+#endif
+ {
+ FT_Bool* no_stem_darkening = (FT_Bool*)value;
+
+
+ driver->no_stem_darkening = *no_stem_darkening;
+ }
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "random-seed" ) )
+ {
+ FT_Int32 random_seed;
+
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+ if ( value_is_string )
+ {
+ const char* s = (const char*)value;
+
+
+ random_seed = (FT_Int32)ft_strtol( s, NULL, 10 );
+ }
+ else
+#endif
+ random_seed = *(FT_Int32*)value;
+
+ if ( random_seed < 0 )
+ random_seed = 0;
+
+ driver->random_seed = random_seed;
+
+ return error;
+ }
+
+ FT_TRACE2(( "ps_property_set: missing property `%s'\n",
+ property_name ));
+ return FT_THROW( Missing_Property );
+ }
+
+
+ FT_BASE_CALLBACK_DEF( FT_Error )
+ ps_property_get( FT_Module module, /* PS_Driver */
+ const char* property_name,
+ void* value )
+ {
+ FT_Error error = FT_Err_Ok;
+ PS_Driver driver = (PS_Driver)module;
+
+
+ if ( !ft_strcmp( property_name, "darkening-parameters" ) )
+ {
+ FT_Int* darken_params = driver->darken_params;
+ FT_Int* val = (FT_Int*)value;
+
+
+ val[0] = darken_params[0];
+ val[1] = darken_params[1];
+ val[2] = darken_params[2];
+ val[3] = darken_params[3];
+ val[4] = darken_params[4];
+ val[5] = darken_params[5];
+ val[6] = darken_params[6];
+ val[7] = darken_params[7];
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "hinting-engine" ) )
+ {
+ FT_UInt hinting_engine = driver->hinting_engine;
+ FT_UInt* val = (FT_UInt*)value;
+
+
+ *val = hinting_engine;
+
+ return error;
+ }
+
+ else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
+ {
+ FT_Bool no_stem_darkening = driver->no_stem_darkening;
+ FT_Bool* val = (FT_Bool*)value;
+
+
+ *val = no_stem_darkening;
+
+ return error;
+ }
+
+ FT_TRACE2(( "ps_property_get: missing property `%s'\n",
+ property_name ));
+ return FT_THROW( Missing_Property );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftrfork.c b/modules/freetype2/src/base/ftrfork.c
new file mode 100644
index 0000000000..2ab430195f
--- /dev/null
+++ b/modules/freetype2/src/base/ftrfork.c
@@ -0,0 +1,934 @@
+/****************************************************************************
+ *
+ * ftrfork.c
+ *
+ * Embedded resource forks accessor (body).
+ *
+ * Copyright (C) 2004-2023 by
+ * Masatake YAMATO and Redhat K.K.
+ *
+ * FT_Raccess_Get_HeaderInfo() and raccess_guess_darwin_hfsplus() are
+ * derived from ftobjs.c.
+ *
+ * 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.
+ *
+ */
+
+/****************************************************************************
+ * Development of the code in this file is support of
+ * Information-technology Promotion Agency, Japan.
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/ftrfork.h>
+
+#include "ftbase.h"
+
+#undef FT_COMPONENT
+#define FT_COMPONENT raccess
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** Resource fork directory access ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_BASE_DEF( FT_Error )
+ FT_Raccess_Get_HeaderInfo( FT_Library library,
+ FT_Stream stream,
+ FT_Long rfork_offset,
+ FT_Long *map_offset,
+ FT_Long *rdata_pos )
+ {
+ FT_Error error;
+ unsigned char head[16], head2[16];
+ FT_Long map_pos, map_len, rdata_len;
+ int allzeros, allmatch, i;
+ FT_Long type_list;
+
+ FT_UNUSED( library );
+
+
+ error = FT_Stream_Seek( stream, (FT_ULong)rfork_offset );
+ if ( error )
+ return error;
+
+ error = FT_Stream_Read( stream, (FT_Byte*)head, 16 );
+ if ( error )
+ return error;
+
+ /* ensure positive values */
+ if ( head[0] >= 0x80 ||
+ head[4] >= 0x80 ||
+ head[8] >= 0x80 ||
+ head[12] >= 0x80 )
+ return FT_THROW( Unknown_File_Format );
+
+ *rdata_pos = ( head[ 0] << 24 ) |
+ ( head[ 1] << 16 ) |
+ ( head[ 2] << 8 ) |
+ head[ 3];
+ map_pos = ( head[ 4] << 24 ) |
+ ( head[ 5] << 16 ) |
+ ( head[ 6] << 8 ) |
+ head[ 7];
+ rdata_len = ( head[ 8] << 24 ) |
+ ( head[ 9] << 16 ) |
+ ( head[10] << 8 ) |
+ head[11];
+ map_len = ( head[12] << 24 ) |
+ ( head[13] << 16 ) |
+ ( head[14] << 8 ) |
+ head[15];
+
+ /* the map must not be empty */
+ if ( !map_pos )
+ return FT_THROW( Unknown_File_Format );
+
+ /* check whether rdata and map overlap */
+ if ( *rdata_pos < map_pos )
+ {
+ if ( *rdata_pos > map_pos - rdata_len )
+ return FT_THROW( Unknown_File_Format );
+ }
+ else
+ {
+ if ( map_pos > *rdata_pos - map_len )
+ return FT_THROW( Unknown_File_Format );
+ }
+
+ /* check whether end of rdata or map exceeds stream size */
+ if ( FT_LONG_MAX - rdata_len < *rdata_pos ||
+ FT_LONG_MAX - map_len < map_pos ||
+
+ FT_LONG_MAX - ( *rdata_pos + rdata_len ) < rfork_offset ||
+ FT_LONG_MAX - ( map_pos + map_len ) < rfork_offset ||
+
+ (FT_ULong)( rfork_offset + *rdata_pos + rdata_len ) > stream->size ||
+ (FT_ULong)( rfork_offset + map_pos + map_len ) > stream->size )
+ return FT_THROW( Unknown_File_Format );
+
+ *rdata_pos += rfork_offset;
+ map_pos += rfork_offset;
+
+ error = FT_Stream_Seek( stream, (FT_ULong)map_pos );
+ if ( error )
+ return error;
+
+ head2[15] = (FT_Byte)( head[15] + 1 ); /* make it be different */
+
+ error = FT_Stream_Read( stream, (FT_Byte*)head2, 16 );
+ if ( error )
+ return error;
+
+ allzeros = 1;
+ allmatch = 1;
+ for ( i = 0; i < 16; i++ )
+ {
+ if ( head2[i] != 0 )
+ allzeros = 0;
+ if ( head2[i] != head[i] )
+ allmatch = 0;
+ }
+ if ( !allzeros && !allmatch )
+ return FT_THROW( Unknown_File_Format );
+
+ /* If we have reached this point then it is probably a mac resource */
+ /* file. Now, does it contain any interesting resources? */
+
+ (void)FT_STREAM_SKIP( 4 /* skip handle to next resource map */
+ + 2 /* skip file resource number */
+ + 2 ); /* skip attributes */
+
+ if ( FT_READ_SHORT( type_list ) )
+ return error;
+ if ( type_list < 0 )
+ return FT_THROW( Unknown_File_Format );
+
+ error = FT_Stream_Seek( stream, (FT_ULong)( map_pos + type_list ) );
+ if ( error )
+ return error;
+
+ *map_offset = map_pos + type_list;
+ return FT_Err_Ok;
+ }
+
+
+ FT_COMPARE_DEF( int )
+ ft_raccess_sort_ref_by_id( const void* a,
+ const void* b )
+ {
+ return ( (FT_RFork_Ref*)a )->res_id - ( (FT_RFork_Ref*)b )->res_id;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Raccess_Get_DataOffsets( FT_Library library,
+ FT_Stream stream,
+ FT_Long map_offset,
+ FT_Long rdata_pos,
+ FT_Long tag,
+ FT_Bool sort_by_res_id,
+ FT_Long **offsets,
+ FT_Long *count )
+ {
+ FT_Error error;
+ int i, j, cnt, subcnt;
+ FT_Long tag_internal, rpos;
+ FT_Memory memory = library->memory;
+ FT_Long temp;
+ FT_Long *offsets_internal = NULL;
+ FT_RFork_Ref *ref = NULL;
+
+
+ FT_TRACE3(( "\n" ));
+ error = FT_Stream_Seek( stream, (FT_ULong)map_offset );
+ if ( error )
+ return error;
+
+ if ( FT_READ_SHORT( cnt ) )
+ return error;
+ cnt++;
+
+ /* `rpos' is a signed 16bit integer offset to resource records; the */
+ /* size of a resource record is 12 bytes. The map header is 28 bytes, */
+ /* and a type list needs 10 bytes or more. If we assume that the name */
+ /* list is empty and we have only a single entry in the type list, */
+ /* there can be at most */
+ /* */
+ /* (32768 - 28 - 10) / 12 = 2727 */
+ /* */
+ /* resources. */
+ /* */
+ /* A type list starts with a two-byte counter, followed by 10-byte */
+ /* type records. Assuming that there are no resources, the number of */
+ /* type records can be at most */
+ /* */
+ /* (32768 - 28 - 2) / 8 = 4079 */
+ /* */
+ if ( cnt > 4079 )
+ return FT_THROW( Invalid_Table );
+
+ for ( i = 0; i < cnt; i++ )
+ {
+ if ( FT_READ_LONG( tag_internal ) ||
+ FT_READ_SHORT( subcnt ) ||
+ FT_READ_SHORT( rpos ) )
+ return error;
+
+ FT_TRACE2(( "Resource tags: %c%c%c%c\n",
+ (char)( 0xFF & ( tag_internal >> 24 ) ),
+ (char)( 0xFF & ( tag_internal >> 16 ) ),
+ (char)( 0xFF & ( tag_internal >> 8 ) ),
+ (char)( 0xFF & ( tag_internal >> 0 ) ) ));
+ FT_TRACE3(( " : subcount=%d, suboffset=0x%04lx\n",
+ subcnt, rpos ));
+
+ if ( tag_internal == tag )
+ {
+ *count = subcnt + 1;
+ rpos += map_offset;
+
+ /* a zero count might be valid in the resource specification, */
+ /* however, it is completely useless to us */
+ if ( *count < 1 || *count > 2727 )
+ return FT_THROW( Invalid_Table );
+
+ error = FT_Stream_Seek( stream, (FT_ULong)rpos );
+ if ( error )
+ return error;
+
+ if ( FT_QNEW_ARRAY( ref, *count ) )
+ return error;
+
+ for ( j = 0; j < *count; j++ )
+ {
+ if ( FT_READ_SHORT( ref[j].res_id ) )
+ goto Exit;
+ if ( FT_STREAM_SKIP( 2 ) ) /* resource name offset */
+ goto Exit;
+ if ( FT_READ_LONG( temp ) ) /* attributes (8bit), offset (24bit) */
+ goto Exit;
+ if ( FT_STREAM_SKIP( 4 ) ) /* mbz */
+ goto Exit;
+
+ /*
+ * According to Inside Macintosh: More Macintosh Toolbox,
+ * "Resource IDs" (1-46), there are some reserved IDs.
+ * However, FreeType2 is not a font synthesizer, no need
+ * to check the acceptable resource ID.
+ */
+ if ( temp < 0 )
+ {
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ ref[j].offset = temp & 0xFFFFFFL;
+
+ FT_TRACE3(( " [%d]:"
+ " resource_id=0x%04x, offset=0x%08lx\n",
+ j, (FT_UShort)ref[j].res_id, ref[j].offset ));
+ }
+
+ if ( sort_by_res_id )
+ {
+ ft_qsort( ref,
+ (size_t)*count,
+ sizeof ( FT_RFork_Ref ),
+ ft_raccess_sort_ref_by_id );
+
+ FT_TRACE3(( " -- sort resources by their ids --\n" ));
+
+ for ( j = 0; j < *count; j++ )
+ FT_TRACE3(( " [%d]:"
+ " resource_id=0x%04x, offset=0x%08lx\n",
+ j, ref[j].res_id, ref[j].offset ));
+ }
+
+ if ( FT_QNEW_ARRAY( offsets_internal, *count ) )
+ goto Exit;
+
+ /* XXX: duplicated reference ID,
+ * gap between reference IDs are acceptable?
+ * further investigation on Apple implementation is needed.
+ */
+ for ( j = 0; j < *count; j++ )
+ offsets_internal[j] = rdata_pos + ref[j].offset;
+
+ *offsets = offsets_internal;
+ error = FT_Err_Ok;
+
+ Exit:
+ FT_FREE( ref );
+ return error;
+ }
+ }
+
+ return FT_THROW( Cannot_Open_Resource );
+ }
+
+
+#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** Guessing functions ****/
+ /**** ****/
+ /**** When you add a new guessing function, ****/
+ /**** update FT_RACCESS_N_RULES in ftrfork.h. ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_Error
+ raccess_guess_apple_double( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_apple_single( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_darwin_ufs_export( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_darwin_newvfs( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_darwin_hfsplus( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_vfat( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_linux_cap( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_linux_double( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_linux_netatalk( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+
+ CONST_FT_RFORK_RULE_ARRAY_BEGIN( ft_raccess_guess_table,
+ ft_raccess_guess_rec )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( apple_double, apple_double )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( apple_single, apple_single )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( darwin_ufs_export, darwin_ufs_export )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( darwin_newvfs, darwin_newvfs )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( darwin_hfsplus, darwin_hfsplus )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( vfat, vfat )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( linux_cap, linux_cap )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( linux_double, linux_double )
+ CONST_FT_RFORK_RULE_ARRAY_ENTRY( linux_netatalk, linux_netatalk )
+ CONST_FT_RFORK_RULE_ARRAY_END
+
+
+ /*************************************************************************/
+ /**** ****/
+ /**** Helper functions ****/
+ /**** ****/
+ /*************************************************************************/
+
+ static FT_Error
+ raccess_guess_apple_generic( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ FT_Int32 magic,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_linux_double_from_file_name( FT_Library library,
+ char* file_name,
+ FT_Long *result_offset );
+
+ static char *
+ raccess_make_file_name( FT_Memory memory,
+ const char *original_name,
+ const char *insertion );
+
+ FT_BASE_DEF( void )
+ FT_Raccess_Guess( FT_Library library,
+ FT_Stream stream,
+ char* base_name,
+ char **new_names,
+ FT_Long *offsets,
+ FT_Error *errors )
+ {
+ FT_Int i;
+
+
+ for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
+ {
+ new_names[i] = NULL;
+ if ( NULL != stream )
+ errors[i] = FT_Stream_Seek( stream, 0 );
+ else
+ errors[i] = FT_Err_Ok;
+
+ if ( errors[i] )
+ continue;
+
+ errors[i] = ft_raccess_guess_table[i].func( library,
+ stream, base_name,
+ &(new_names[i]),
+ &(offsets[i]) );
+ }
+
+ return;
+ }
+
+
+#if defined( FT_CONFIG_OPTION_MAC_FONTS ) && !defined( FT_MACINTOSH )
+ static FT_RFork_Rule
+ raccess_get_rule_type_from_rule_index( FT_Library library,
+ FT_UInt rule_index )
+ {
+ FT_UNUSED( library );
+
+ if ( rule_index >= FT_RACCESS_N_RULES )
+ return FT_RFork_Rule_invalid;
+
+ return ft_raccess_guess_table[rule_index].type;
+ }
+
+
+ /*
+ * For this function, refer ftbase.h.
+ */
+ FT_LOCAL_DEF( FT_Bool )
+ ft_raccess_rule_by_darwin_vfs( FT_Library library,
+ FT_UInt rule_index )
+ {
+ switch( raccess_get_rule_type_from_rule_index( library, rule_index ) )
+ {
+ case FT_RFork_Rule_darwin_newvfs:
+ case FT_RFork_Rule_darwin_hfsplus:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+#endif
+
+
+ static FT_Error
+ raccess_guess_apple_double( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ FT_Int32 magic = ( 0x00 << 24 ) |
+ ( 0x05 << 16 ) |
+ ( 0x16 << 8 ) |
+ 0x07;
+
+
+ *result_file_name = NULL;
+ if ( NULL == stream )
+ return FT_THROW( Cannot_Open_Stream );
+
+ return raccess_guess_apple_generic( library, stream, base_file_name,
+ magic, result_offset );
+ }
+
+
+ static FT_Error
+ raccess_guess_apple_single( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ FT_Int32 magic = ( 0x00 << 24 ) |
+ ( 0x05 << 16 ) |
+ ( 0x16 << 8 ) |
+ 0x00;
+
+
+ *result_file_name = NULL;
+ if ( NULL == stream )
+ return FT_THROW( Cannot_Open_Stream );
+
+ return raccess_guess_apple_generic( library, stream, base_file_name,
+ magic, result_offset );
+ }
+
+
+ static FT_Error
+ raccess_guess_darwin_ufs_export( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Error error;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+ newpath = raccess_make_file_name( memory, base_file_name, "._" );
+ if ( !newpath )
+ return FT_THROW( Out_Of_Memory );
+
+ error = raccess_guess_linux_double_from_file_name( library, newpath,
+ result_offset );
+ if ( !error )
+ *result_file_name = newpath;
+ else
+ FT_FREE( newpath );
+
+ return error;
+ }
+
+
+ static FT_Error
+ raccess_guess_darwin_hfsplus( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ /*
+ Only meaningful on systems with hfs+ drivers (or Macs).
+ */
+ FT_Error error;
+ char* newpath = NULL;
+ FT_Memory memory;
+ FT_Long base_file_len = (FT_Long)ft_strlen( base_file_name );
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ if ( base_file_len + 6 > FT_INT_MAX )
+ return FT_THROW( Array_Too_Large );
+
+ if ( FT_QALLOC( newpath, base_file_len + 6 ) )
+ return error;
+
+ FT_MEM_COPY( newpath, base_file_name, base_file_len );
+ FT_MEM_COPY( newpath + base_file_len, "/rsrc", 6 );
+
+ *result_file_name = newpath;
+ *result_offset = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Error
+ raccess_guess_darwin_newvfs( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ /*
+ Only meaningful on systems with Mac OS X (> 10.1).
+ */
+ FT_Error error;
+ char* newpath = NULL;
+ FT_Memory memory;
+ FT_Long base_file_len = (FT_Long)ft_strlen( base_file_name );
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ if ( base_file_len + 18 > FT_INT_MAX )
+ return FT_THROW( Array_Too_Large );
+
+ if ( FT_QALLOC( newpath, base_file_len + 18 ) )
+ return error;
+
+ FT_MEM_COPY( newpath, base_file_name, base_file_len );
+ FT_MEM_COPY( newpath + base_file_len, "/..namedfork/rsrc", 18 );
+
+ *result_file_name = newpath;
+ *result_offset = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Error
+ raccess_guess_vfat( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ newpath = raccess_make_file_name( memory, base_file_name,
+ "resource.frk/" );
+ if ( !newpath )
+ return FT_THROW( Out_Of_Memory );
+
+ *result_file_name = newpath;
+ *result_offset = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Error
+ raccess_guess_linux_cap( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ newpath = raccess_make_file_name( memory, base_file_name, ".resource/" );
+ if ( !newpath )
+ return FT_THROW( Out_Of_Memory );
+
+ *result_file_name = newpath;
+ *result_offset = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Error
+ raccess_guess_linux_double( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Error error;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ newpath = raccess_make_file_name( memory, base_file_name, "%" );
+ if ( !newpath )
+ return FT_THROW( Out_Of_Memory );
+
+ error = raccess_guess_linux_double_from_file_name( library, newpath,
+ result_offset );
+ if ( !error )
+ *result_file_name = newpath;
+ else
+ FT_FREE( newpath );
+
+ return error;
+ }
+
+
+ static FT_Error
+ raccess_guess_linux_netatalk( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Error error;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ newpath = raccess_make_file_name( memory, base_file_name,
+ ".AppleDouble/" );
+ if ( !newpath )
+ return FT_THROW( Out_Of_Memory );
+
+ error = raccess_guess_linux_double_from_file_name( library, newpath,
+ result_offset );
+ if ( !error )
+ *result_file_name = newpath;
+ else
+ FT_FREE( newpath );
+
+ return error;
+ }
+
+
+ static FT_Error
+ raccess_guess_apple_generic( FT_Library library,
+ FT_Stream stream,
+ char *base_file_name,
+ FT_Int32 magic,
+ FT_Long *result_offset )
+ {
+ FT_Int32 magic_from_stream;
+ FT_Error error;
+ FT_Int32 version_number = 0;
+ FT_UShort n_of_entries;
+
+ int i;
+ FT_Int32 entry_id, entry_offset, entry_length = 0;
+
+ const FT_Int32 resource_fork_entry_id = 0x2;
+
+ FT_UNUSED( library );
+ FT_UNUSED( base_file_name );
+ FT_UNUSED( version_number );
+ FT_UNUSED( entry_length );
+
+
+ if ( FT_READ_LONG( magic_from_stream ) )
+ return error;
+ if ( magic_from_stream != magic )
+ return FT_THROW( Unknown_File_Format );
+
+ if ( FT_READ_LONG( version_number ) )
+ return error;
+
+ /* filler */
+ error = FT_Stream_Skip( stream, 16 );
+ if ( error )
+ return error;
+
+ if ( FT_READ_USHORT( n_of_entries ) )
+ return error;
+ if ( n_of_entries == 0 )
+ return FT_THROW( Unknown_File_Format );
+
+ for ( i = 0; i < n_of_entries; i++ )
+ {
+ if ( FT_READ_LONG( entry_id ) )
+ return error;
+ if ( entry_id == resource_fork_entry_id )
+ {
+ if ( FT_READ_LONG( entry_offset ) ||
+ FT_READ_LONG( entry_length ) )
+ continue;
+ *result_offset = entry_offset;
+
+ return FT_Err_Ok;
+ }
+ else
+ {
+ error = FT_Stream_Skip( stream, 4 + 4 ); /* offset + length */
+ if ( error )
+ return error;
+ }
+ }
+
+ return FT_THROW( Unknown_File_Format );
+ }
+
+
+ static FT_Error
+ raccess_guess_linux_double_from_file_name( FT_Library library,
+ char *file_name,
+ FT_Long *result_offset )
+ {
+ FT_Open_Args args2;
+ FT_Stream stream2;
+ char* nouse = NULL;
+ FT_Error error;
+
+
+ args2.flags = FT_OPEN_PATHNAME;
+ args2.pathname = file_name;
+ error = FT_Stream_New( library, &args2, &stream2 );
+ if ( error )
+ return error;
+
+ error = raccess_guess_apple_double( library, stream2, file_name,
+ &nouse, result_offset );
+
+ FT_Stream_Free( stream2, 0 );
+
+ return error;
+ }
+
+
+ static char*
+ raccess_make_file_name( FT_Memory memory,
+ const char *original_name,
+ const char *insertion )
+ {
+ char* new_name = NULL;
+ const char* tmp;
+ const char* slash;
+ size_t new_length;
+ FT_Error error;
+
+
+ new_length = ft_strlen( original_name ) + ft_strlen( insertion );
+ if ( FT_QALLOC( new_name, new_length + 1 ) )
+ return NULL;
+
+ tmp = ft_strrchr( original_name, '/' );
+ if ( tmp )
+ {
+ ft_strncpy( new_name,
+ original_name,
+ (size_t)( tmp - original_name + 1 ) );
+ new_name[tmp - original_name + 1] = '\0';
+ slash = tmp + 1;
+ }
+ else
+ {
+ slash = original_name;
+ new_name[0] = '\0';
+ }
+
+ ft_strcat( new_name, insertion );
+ ft_strcat( new_name, slash );
+
+ return new_name;
+ }
+
+
+#else /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */
+
+
+ /**************************************************************************
+ * Dummy function; just sets errors
+ */
+
+ FT_BASE_DEF( void )
+ FT_Raccess_Guess( FT_Library library,
+ FT_Stream stream,
+ char *base_name,
+ char **new_names,
+ FT_Long *offsets,
+ FT_Error *errors )
+ {
+ FT_Int i;
+
+ FT_UNUSED( library );
+ FT_UNUSED( stream );
+ FT_UNUSED( base_name );
+
+
+ for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
+ {
+ new_names[i] = NULL;
+ offsets[i] = 0;
+ errors[i] = FT_ERR( Unimplemented_Feature );
+ }
+ }
+
+
+#endif /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftsnames.c b/modules/freetype2/src/base/ftsnames.c
new file mode 100644
index 0000000000..1917a3f1df
--- /dev/null
+++ b/modules/freetype2/src/base/ftsnames.c
@@ -0,0 +1,185 @@
+/****************************************************************************
+ *
+ * ftsnames.c
+ *
+ * Simple interface to access SFNT name tables (which are used
+ * to hold font names, copyright info, notices, etc.) (body).
+ *
+ * This is _not_ used to retrieve glyph names!
+ *
+ * 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/ftsnames.h>
+#include <freetype/internal/tttypes.h>
+#include <freetype/internal/ftstream.h>
+
+
+#ifdef TT_CONFIG_OPTION_SFNT_NAMES
+
+
+ /* documentation is in ftsnames.h */
+
+ FT_EXPORT_DEF( FT_UInt )
+ FT_Get_Sfnt_Name_Count( FT_Face face )
+ {
+ return ( face && FT_IS_SFNT( face ) ) ? ((TT_Face)face)->num_names : 0;
+ }
+
+
+ /* documentation is in ftsnames.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Sfnt_Name( FT_Face face,
+ FT_UInt idx,
+ FT_SfntName *aname )
+ {
+ FT_Error error = FT_ERR( Invalid_Argument );
+
+
+ if ( aname && face && FT_IS_SFNT( face ) )
+ {
+ TT_Face ttface = (TT_Face)face;
+
+
+ if ( idx < (FT_UInt)ttface->num_names )
+ {
+ TT_Name entry = ttface->name_table.names + idx;
+
+
+ /* load name on demand */
+ if ( entry->stringLength > 0 && !entry->string )
+ {
+ FT_Memory memory = face->memory;
+ FT_Stream stream = face->stream;
+
+
+ if ( FT_QNEW_ARRAY ( entry->string, entry->stringLength ) ||
+ FT_STREAM_SEEK( entry->stringOffset ) ||
+ FT_STREAM_READ( entry->string, entry->stringLength ) )
+ {
+ FT_FREE( entry->string );
+ entry->stringLength = 0;
+ }
+ }
+
+ aname->platform_id = entry->platformID;
+ aname->encoding_id = entry->encodingID;
+ aname->language_id = entry->languageID;
+ aname->name_id = entry->nameID;
+ aname->string = (FT_Byte*)entry->string;
+ aname->string_len = entry->stringLength;
+
+ error = FT_Err_Ok;
+ }
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftsnames.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Sfnt_LangTag( FT_Face face,
+ FT_UInt langID,
+ FT_SfntLangTag *alangTag )
+ {
+ FT_Error error = FT_ERR( Invalid_Argument );
+
+
+ if ( alangTag && face && FT_IS_SFNT( face ) )
+ {
+ TT_Face ttface = (TT_Face)face;
+
+
+ if ( ttface->name_table.format != 1 )
+ return FT_THROW( Invalid_Table );
+
+ if ( langID > 0x8000U &&
+ langID - 0x8000U < ttface->name_table.numLangTagRecords )
+ {
+ TT_LangTag entry = ttface->name_table.langTags +
+ ( langID - 0x8000U );
+
+
+ /* load name on demand */
+ if ( entry->stringLength > 0 && !entry->string )
+ {
+ FT_Memory memory = face->memory;
+ FT_Stream stream = face->stream;
+
+
+ if ( FT_QNEW_ARRAY ( entry->string, entry->stringLength ) ||
+ FT_STREAM_SEEK( entry->stringOffset ) ||
+ FT_STREAM_READ( entry->string, entry->stringLength ) )
+ {
+ FT_FREE( entry->string );
+ entry->stringLength = 0;
+ }
+ }
+
+ alangTag->string = (FT_Byte*)entry->string;
+ alangTag->string_len = entry->stringLength;
+
+ error = FT_Err_Ok;
+ }
+ }
+
+ return error;
+ }
+
+
+#else /* !TT_CONFIG_OPTION_SFNT_NAMES */
+
+
+ FT_EXPORT_DEF( FT_UInt )
+ FT_Get_Sfnt_Name_Count( FT_Face face )
+ {
+ FT_UNUSED( face );
+
+ return 0;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Sfnt_Name( FT_Face face,
+ FT_UInt idx,
+ FT_SfntName *aname )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( idx );
+ FT_UNUSED( aname );
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Sfnt_LangTag( FT_Face face,
+ FT_UInt langID,
+ FT_SfntLangTag *alangTag )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( langID );
+ FT_UNUSED( alangTag );
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+
+#endif /* !TT_CONFIG_OPTION_SFNT_NAMES */
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftstream.c b/modules/freetype2/src/base/ftstream.c
new file mode 100644
index 0000000000..05c5637578
--- /dev/null
+++ b/modules/freetype2/src/base/ftstream.c
@@ -0,0 +1,868 @@
+/****************************************************************************
+ *
+ * ftstream.c
+ *
+ * I/O stream support (body).
+ *
+ * Copyright (C) 2000-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/ftstream.h>
+#include <freetype/internal/ftdebug.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 stream
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_OpenMemory( FT_Stream stream,
+ const FT_Byte* base,
+ FT_ULong size )
+ {
+ stream->base = (FT_Byte*) base;
+ stream->size = size;
+ stream->pos = 0;
+ stream->cursor = NULL;
+ stream->read = NULL;
+ stream->close = NULL;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_Close( FT_Stream stream )
+ {
+ if ( stream && stream->close )
+ stream->close( stream );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_Seek( FT_Stream stream,
+ FT_ULong pos )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( stream->read )
+ {
+ if ( stream->read( stream, pos, NULL, 0 ) )
+ {
+ FT_ERROR(( "FT_Stream_Seek:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ pos, stream->size ));
+
+ error = FT_THROW( Invalid_Stream_Operation );
+ }
+ }
+ /* note that seeking to the first position after the file is valid */
+ else if ( pos > stream->size )
+ {
+ FT_ERROR(( "FT_Stream_Seek:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ pos, stream->size ));
+
+ error = FT_THROW( Invalid_Stream_Operation );
+ }
+
+ if ( !error )
+ stream->pos = pos;
+
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_Skip( FT_Stream stream,
+ FT_Long distance )
+ {
+ if ( distance < 0 )
+ return FT_THROW( Invalid_Stream_Operation );
+
+ return FT_Stream_Seek( stream, stream->pos + (FT_ULong)distance );
+ }
+
+
+ FT_BASE_DEF( FT_ULong )
+ FT_Stream_Pos( FT_Stream stream )
+ {
+ return stream->pos;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_Read( FT_Stream stream,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ return FT_Stream_ReadAt( stream, stream->pos, buffer, count );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_ReadAt( FT_Stream stream,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_ULong read_bytes;
+
+
+ if ( pos >= stream->size )
+ {
+ FT_ERROR(( "FT_Stream_ReadAt:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ pos, stream->size ));
+
+ return FT_THROW( Invalid_Stream_Operation );
+ }
+
+ if ( stream->read )
+ read_bytes = stream->read( stream, pos, buffer, count );
+ else
+ {
+ read_bytes = stream->size - pos;
+ if ( read_bytes > count )
+ read_bytes = count;
+
+ FT_MEM_COPY( buffer, stream->base + pos, read_bytes );
+ }
+
+ stream->pos = pos + read_bytes;
+
+ if ( read_bytes < count )
+ {
+ FT_ERROR(( "FT_Stream_ReadAt:"
+ " invalid read; expected %lu bytes, got %lu\n",
+ count, read_bytes ));
+
+ error = FT_THROW( Invalid_Stream_Operation );
+ }
+
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_ULong )
+ FT_Stream_TryRead( FT_Stream stream,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_ULong read_bytes = 0;
+
+
+ if ( stream->pos >= stream->size )
+ goto Exit;
+
+ if ( stream->read )
+ read_bytes = stream->read( stream, stream->pos, buffer, count );
+ else
+ {
+ read_bytes = stream->size - stream->pos;
+ if ( read_bytes > count )
+ read_bytes = count;
+
+ FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes );
+ }
+
+ stream->pos += read_bytes;
+
+ Exit:
+ return read_bytes;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_ExtractFrame( FT_Stream stream,
+ FT_ULong count,
+ FT_Byte** pbytes )
+ {
+ FT_Error error;
+
+
+ error = FT_Stream_EnterFrame( stream, count );
+ if ( !error )
+ {
+ *pbytes = (FT_Byte*)stream->cursor;
+
+ /* equivalent to FT_Stream_ExitFrame(), with no memory block release */
+ stream->cursor = NULL;
+ stream->limit = NULL;
+ }
+
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_ReleaseFrame( FT_Stream stream,
+ FT_Byte** pbytes )
+ {
+ if ( stream && stream->read )
+ {
+ FT_Memory memory = stream->memory;
+
+
+#ifdef FT_DEBUG_MEMORY
+ ft_mem_free( memory, *pbytes );
+#else
+ FT_FREE( *pbytes );
+#endif
+ }
+
+ *pbytes = NULL;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_EnterFrame( FT_Stream stream,
+ FT_ULong count )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_ULong read_bytes;
+
+
+ FT_TRACE7(( "FT_Stream_EnterFrame: %ld bytes\n", count ));
+
+ /* check for nested frame access */
+ FT_ASSERT( stream && stream->cursor == 0 );
+
+ if ( stream->read )
+ {
+ /* allocate the frame in memory */
+ FT_Memory memory = stream->memory;
+
+
+ /* simple sanity check */
+ if ( count > stream->size )
+ {
+ FT_ERROR(( "FT_Stream_EnterFrame:"
+ " frame size (%lu) larger than stream size (%lu)\n",
+ count, stream->size ));
+
+ error = FT_THROW( Invalid_Stream_Operation );
+ goto Exit;
+ }
+
+#ifdef FT_DEBUG_MEMORY
+ /* assume `ft_debug_file_` and `ft_debug_lineno_` are already set */
+ stream->base = (unsigned char*)ft_mem_qalloc( memory,
+ (FT_Long)count,
+ &error );
+ if ( error )
+ goto Exit;
+#else
+ if ( FT_QALLOC( stream->base, count ) )
+ goto Exit;
+#endif
+ /* read it */
+ read_bytes = stream->read( stream, stream->pos,
+ stream->base, count );
+ if ( read_bytes < count )
+ {
+ FT_ERROR(( "FT_Stream_EnterFrame:"
+ " invalid read; expected %lu bytes, got %lu\n",
+ count, read_bytes ));
+
+ FT_FREE( stream->base );
+ error = FT_THROW( Invalid_Stream_Operation );
+ }
+
+ stream->cursor = stream->base;
+ stream->limit = FT_OFFSET( stream->cursor, count );
+ stream->pos += read_bytes;
+ }
+ else
+ {
+ /* check current and new position */
+ if ( stream->pos >= stream->size ||
+ stream->size - stream->pos < count )
+ {
+ FT_ERROR(( "FT_Stream_EnterFrame:"
+ " invalid i/o; pos = 0x%lx, count = %lu, size = 0x%lx\n",
+ stream->pos, count, stream->size ));
+
+ error = FT_THROW( Invalid_Stream_Operation );
+ goto Exit;
+ }
+
+ /* set cursor */
+ stream->cursor = stream->base + stream->pos;
+ stream->limit = stream->cursor + count;
+ stream->pos += count;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_ExitFrame( FT_Stream stream )
+ {
+ /* IMPORTANT: The assertion stream->cursor != 0 was removed, given */
+ /* that it is possible to access a frame of length 0 in */
+ /* some weird fonts (usually, when accessing an array of */
+ /* 0 records, like in some strange kern tables). */
+ /* */
+ /* In this case, the loader code handles the 0-length table */
+ /* gracefully; however, stream.cursor is really set to 0 by the */
+ /* FT_Stream_EnterFrame() call, and this is not an error. */
+
+ FT_TRACE7(( "FT_Stream_ExitFrame\n" ));
+
+ FT_ASSERT( stream );
+
+ if ( stream->read )
+ {
+ FT_Memory memory = stream->memory;
+
+
+#ifdef FT_DEBUG_MEMORY
+ ft_mem_free( memory, stream->base );
+ stream->base = NULL;
+#else
+ FT_FREE( stream->base );
+#endif
+ }
+
+ stream->cursor = NULL;
+ stream->limit = NULL;
+ }
+
+
+ FT_BASE_DEF( FT_Byte )
+ FT_Stream_GetByte( FT_Stream stream )
+ {
+ FT_Byte result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ if ( stream->cursor < stream->limit )
+ result = *stream->cursor++;
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt16 )
+ FT_Stream_GetUShort( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_UInt16 result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 1 < stream->limit )
+ result = FT_NEXT_USHORT( p );
+ stream->cursor = p;
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt16 )
+ FT_Stream_GetUShortLE( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_UInt16 result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 1 < stream->limit )
+ result = FT_NEXT_USHORT_LE( p );
+ stream->cursor = p;
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt32 )
+ FT_Stream_GetUOffset( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_UInt32 result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 2 < stream->limit )
+ result = FT_NEXT_UOFF3( p );
+ stream->cursor = p;
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt32 )
+ FT_Stream_GetULong( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_UInt32 result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 3 < stream->limit )
+ result = FT_NEXT_ULONG( p );
+ stream->cursor = p;
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt32 )
+ FT_Stream_GetULongLE( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_UInt32 result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 3 < stream->limit )
+ result = FT_NEXT_ULONG_LE( p );
+ stream->cursor = p;
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_Byte )
+ FT_Stream_ReadByte( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte result = 0;
+
+
+ FT_ASSERT( stream );
+
+ if ( stream->pos < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, &result, 1L ) != 1L )
+ goto Fail;
+ }
+ else
+ result = stream->base[stream->pos];
+ }
+ else
+ goto Fail;
+
+ stream->pos++;
+
+ *error = FT_Err_Ok;
+
+ return result;
+
+ Fail:
+ *error = FT_THROW( Invalid_Stream_Operation );
+ FT_ERROR(( "FT_Stream_ReadByte:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt16 )
+ FT_Stream_ReadUShort( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[2];
+ FT_Byte* p;
+ FT_UInt16 result = 0;
+
+
+ FT_ASSERT( stream );
+
+ if ( stream->pos + 1 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ p = stream->base + stream->pos;
+
+ if ( p )
+ result = FT_NEXT_USHORT( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 2;
+
+ *error = FT_Err_Ok;
+
+ return result;
+
+ Fail:
+ *error = FT_THROW( Invalid_Stream_Operation );
+ FT_ERROR(( "FT_Stream_ReadUShort:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt16 )
+ FT_Stream_ReadUShortLE( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[2];
+ FT_Byte* p;
+ FT_UInt16 result = 0;
+
+
+ FT_ASSERT( stream );
+
+ if ( stream->pos + 1 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ p = stream->base + stream->pos;
+
+ if ( p )
+ result = FT_NEXT_USHORT_LE( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 2;
+
+ *error = FT_Err_Ok;
+
+ return result;
+
+ Fail:
+ *error = FT_THROW( Invalid_Stream_Operation );
+ FT_ERROR(( "FT_Stream_ReadUShortLE:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_ULong )
+ FT_Stream_ReadUOffset( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[3];
+ FT_Byte* p;
+ FT_ULong result = 0;
+
+
+ FT_ASSERT( stream );
+
+ if ( stream->pos + 2 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if (stream->read( stream, stream->pos, reads, 3L ) != 3L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ p = stream->base + stream->pos;
+
+ if ( p )
+ result = FT_NEXT_UOFF3( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 3;
+
+ *error = FT_Err_Ok;
+
+ return result;
+
+ Fail:
+ *error = FT_THROW( Invalid_Stream_Operation );
+ FT_ERROR(( "FT_Stream_ReadUOffset:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt32 )
+ FT_Stream_ReadULong( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[4];
+ FT_Byte* p;
+ FT_UInt32 result = 0;
+
+
+ FT_ASSERT( stream );
+
+ if ( stream->pos + 3 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ p = stream->base + stream->pos;
+
+ if ( p )
+ result = FT_NEXT_ULONG( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 4;
+
+ *error = FT_Err_Ok;
+
+ return result;
+
+ Fail:
+ *error = FT_THROW( Invalid_Stream_Operation );
+ FT_ERROR(( "FT_Stream_ReadULong:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_UInt32 )
+ FT_Stream_ReadULongLE( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[4];
+ FT_Byte* p;
+ FT_UInt32 result = 0;
+
+
+ FT_ASSERT( stream );
+
+ if ( stream->pos + 3 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ p = stream->base + stream->pos;
+
+ if ( p )
+ result = FT_NEXT_ULONG_LE( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 4;
+
+ *error = FT_Err_Ok;
+
+ return result;
+
+ Fail:
+ *error = FT_THROW( Invalid_Stream_Operation );
+ FT_ERROR(( "FT_Stream_ReadULongLE:"
+ " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_ReadFields( FT_Stream stream,
+ const FT_Frame_Field* fields,
+ void* structure )
+ {
+ FT_Error error;
+ FT_Bool frame_accessed = 0;
+ FT_Byte* cursor;
+
+
+ if ( !fields )
+ return FT_THROW( Invalid_Argument );
+
+ if ( !stream )
+ return FT_THROW( Invalid_Stream_Handle );
+
+ cursor = stream->cursor;
+
+ error = FT_Err_Ok;
+ do
+ {
+ FT_ULong value;
+ FT_Int sign_shift;
+ FT_Byte* p;
+
+
+ switch ( fields->value )
+ {
+ case ft_frame_start: /* access a new frame */
+ error = FT_Stream_EnterFrame( stream, fields->offset );
+ if ( error )
+ goto Exit;
+
+ frame_accessed = 1;
+ cursor = stream->cursor;
+ fields++;
+ continue; /* loop! */
+
+ case ft_frame_bytes: /* read a byte sequence */
+ case ft_frame_skip: /* skip some bytes */
+ {
+ FT_UInt len = fields->size;
+
+
+ if ( cursor + len > stream->limit )
+ {
+ error = FT_THROW( Invalid_Stream_Operation );
+ goto Exit;
+ }
+
+ if ( fields->value == ft_frame_bytes )
+ {
+ p = (FT_Byte*)structure + fields->offset;
+ FT_MEM_COPY( p, cursor, len );
+ }
+ cursor += len;
+ fields++;
+ continue;
+ }
+
+ case ft_frame_byte:
+ case ft_frame_schar: /* read a single byte */
+ value = FT_NEXT_BYTE( cursor );
+ sign_shift = 24;
+ break;
+
+ case ft_frame_short_be:
+ case ft_frame_ushort_be: /* read a 2-byte big-endian short */
+ value = FT_NEXT_USHORT( cursor );
+ sign_shift = 16;
+ break;
+
+ case ft_frame_short_le:
+ case ft_frame_ushort_le: /* read a 2-byte little-endian short */
+ value = FT_NEXT_USHORT_LE( cursor );
+ sign_shift = 16;
+ break;
+
+ case ft_frame_long_be:
+ case ft_frame_ulong_be: /* read a 4-byte big-endian long */
+ value = FT_NEXT_ULONG( cursor );
+ sign_shift = 0;
+ break;
+
+ case ft_frame_long_le:
+ case ft_frame_ulong_le: /* read a 4-byte little-endian long */
+ value = FT_NEXT_ULONG_LE( cursor );
+ sign_shift = 0;
+ break;
+
+ case ft_frame_off3_be:
+ case ft_frame_uoff3_be: /* read a 3-byte big-endian long */
+ value = FT_NEXT_UOFF3( cursor );
+ sign_shift = 8;
+ break;
+
+ case ft_frame_off3_le:
+ case ft_frame_uoff3_le: /* read a 3-byte little-endian long */
+ value = FT_NEXT_UOFF3_LE( cursor );
+ sign_shift = 8;
+ break;
+
+ default:
+ /* otherwise, exit the loop */
+ stream->cursor = cursor;
+ goto Exit;
+ }
+
+ /* now, compute the signed value is necessary */
+ if ( fields->value & FT_FRAME_OP_SIGNED )
+ value = (FT_ULong)( (FT_Int32)( value << sign_shift ) >> sign_shift );
+
+ /* finally, store the value in the object */
+
+ p = (FT_Byte*)structure + fields->offset;
+ switch ( fields->size )
+ {
+ case ( 8 / FT_CHAR_BIT ):
+ *(FT_Byte*)p = (FT_Byte)value;
+ break;
+
+ case ( 16 / FT_CHAR_BIT ):
+ *(FT_UShort*)p = (FT_UShort)value;
+ break;
+
+ case ( 32 / FT_CHAR_BIT ):
+ *(FT_UInt32*)p = (FT_UInt32)value;
+ break;
+
+ default: /* for 64-bit systems */
+ *(FT_ULong*)p = (FT_ULong)value;
+ }
+
+ /* go to next field */
+ fields++;
+ }
+ while ( 1 );
+
+ Exit:
+ /* close the frame if it was opened by this read */
+ if ( frame_accessed )
+ FT_Stream_ExitFrame( stream );
+
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftstroke.c b/modules/freetype2/src/base/ftstroke.c
new file mode 100644
index 0000000000..db358e772e
--- /dev/null
+++ b/modules/freetype2/src/base/ftstroke.c
@@ -0,0 +1,2408 @@
+/****************************************************************************
+ *
+ * ftstroke.c
+ *
+ * FreeType path stroker (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/ftstroke.h>
+#include <freetype/fttrigon.h>
+#include <freetype/ftoutln.h>
+#include <freetype/internal/ftmemory.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftobjs.h>
+
+
+ /* declare an extern to access `ft_outline_glyph_class' globally */
+ /* allocated in `ftglyph.c' */
+ FT_CALLBACK_TABLE const FT_Glyph_Class ft_outline_glyph_class;
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_StrokerBorder )
+ FT_Outline_GetInsideBorder( FT_Outline* outline )
+ {
+ FT_Orientation o = FT_Outline_Get_Orientation( outline );
+
+
+ return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
+ : FT_STROKER_BORDER_LEFT;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_StrokerBorder )
+ FT_Outline_GetOutsideBorder( FT_Outline* outline )
+ {
+ FT_Orientation o = FT_Outline_Get_Orientation( outline );
+
+
+ return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
+ : FT_STROKER_BORDER_RIGHT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** BEZIER COMPUTATIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 )
+#define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 8 )
+
+#define FT_EPSILON 2
+
+#define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
+
+
+ static FT_Pos
+ ft_pos_abs( FT_Pos x )
+ {
+ return x >= 0 ? x : -x;
+ }
+
+
+ static void
+ ft_conic_split( FT_Vector* base )
+ {
+ FT_Pos a, b;
+
+
+ base[4].x = base[2].x;
+ a = base[0].x + base[1].x;
+ b = base[1].x + base[2].x;
+ base[3].x = b >> 1;
+ base[2].x = ( a + b ) >> 2;
+ base[1].x = a >> 1;
+
+ base[4].y = base[2].y;
+ a = base[0].y + base[1].y;
+ b = base[1].y + base[2].y;
+ base[3].y = b >> 1;
+ base[2].y = ( a + b ) >> 2;
+ base[1].y = a >> 1;
+ }
+
+
+ static FT_Bool
+ ft_conic_is_small_enough( FT_Vector* base,
+ FT_Angle *angle_in,
+ FT_Angle *angle_out )
+ {
+ FT_Vector d1, d2;
+ FT_Angle theta;
+ FT_Int close1, close2;
+
+
+ d1.x = base[1].x - base[2].x;
+ d1.y = base[1].y - base[2].y;
+ d2.x = base[0].x - base[1].x;
+ d2.y = base[0].y - base[1].y;
+
+ close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
+ close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
+
+ if ( close1 )
+ {
+ if ( close2 )
+ {
+ /* basically a point; */
+ /* do nothing to retain original direction */
+ }
+ else
+ {
+ *angle_in =
+ *angle_out = FT_Atan2( d2.x, d2.y );
+ }
+ }
+ else /* !close1 */
+ {
+ if ( close2 )
+ {
+ *angle_in =
+ *angle_out = FT_Atan2( d1.x, d1.y );
+ }
+ else
+ {
+ *angle_in = FT_Atan2( d1.x, d1.y );
+ *angle_out = FT_Atan2( d2.x, d2.y );
+ }
+ }
+
+ theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
+
+ return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
+ }
+
+
+ static void
+ ft_cubic_split( FT_Vector* base )
+ {
+ FT_Pos a, b, c;
+
+
+ base[6].x = base[3].x;
+ a = base[0].x + base[1].x;
+ b = base[1].x + base[2].x;
+ c = base[2].x + base[3].x;
+ base[5].x = c >> 1;
+ c += b;
+ base[4].x = c >> 2;
+ base[1].x = a >> 1;
+ a += b;
+ base[2].x = a >> 2;
+ base[3].x = ( a + c ) >> 3;
+
+ base[6].y = base[3].y;
+ a = base[0].y + base[1].y;
+ b = base[1].y + base[2].y;
+ c = base[2].y + base[3].y;
+ base[5].y = c >> 1;
+ c += b;
+ base[4].y = c >> 2;
+ base[1].y = a >> 1;
+ a += b;
+ base[2].y = a >> 2;
+ base[3].y = ( a + c ) >> 3;
+ }
+
+
+ /* Return the average of `angle1' and `angle2'. */
+ /* This gives correct result even if `angle1' and `angle2' */
+ /* have opposite signs. */
+ static FT_Angle
+ ft_angle_mean( FT_Angle angle1,
+ FT_Angle angle2 )
+ {
+ return angle1 + FT_Angle_Diff( angle1, angle2 ) / 2;
+ }
+
+
+ static FT_Bool
+ ft_cubic_is_small_enough( FT_Vector* base,
+ FT_Angle *angle_in,
+ FT_Angle *angle_mid,
+ FT_Angle *angle_out )
+ {
+ FT_Vector d1, d2, d3;
+ FT_Angle theta1, theta2;
+ FT_Int close1, close2, close3;
+
+
+ d1.x = base[2].x - base[3].x;
+ d1.y = base[2].y - base[3].y;
+ d2.x = base[1].x - base[2].x;
+ d2.y = base[1].y - base[2].y;
+ d3.x = base[0].x - base[1].x;
+ d3.y = base[0].y - base[1].y;
+
+ close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
+ close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
+ close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
+
+ if ( close1 )
+ {
+ if ( close2 )
+ {
+ if ( close3 )
+ {
+ /* basically a point; */
+ /* do nothing to retain original direction */
+ }
+ else /* !close3 */
+ {
+ *angle_in =
+ *angle_mid =
+ *angle_out = FT_Atan2( d3.x, d3.y );
+ }
+ }
+ else /* !close2 */
+ {
+ if ( close3 )
+ {
+ *angle_in =
+ *angle_mid =
+ *angle_out = FT_Atan2( d2.x, d2.y );
+ }
+ else /* !close3 */
+ {
+ *angle_in =
+ *angle_mid = FT_Atan2( d2.x, d2.y );
+ *angle_out = FT_Atan2( d3.x, d3.y );
+ }
+ }
+ }
+ else /* !close1 */
+ {
+ if ( close2 )
+ {
+ if ( close3 )
+ {
+ *angle_in =
+ *angle_mid =
+ *angle_out = FT_Atan2( d1.x, d1.y );
+ }
+ else /* !close3 */
+ {
+ *angle_in = FT_Atan2( d1.x, d1.y );
+ *angle_out = FT_Atan2( d3.x, d3.y );
+ *angle_mid = ft_angle_mean( *angle_in, *angle_out );
+ }
+ }
+ else /* !close2 */
+ {
+ if ( close3 )
+ {
+ *angle_in = FT_Atan2( d1.x, d1.y );
+ *angle_mid =
+ *angle_out = FT_Atan2( d2.x, d2.y );
+ }
+ else /* !close3 */
+ {
+ *angle_in = FT_Atan2( d1.x, d1.y );
+ *angle_mid = FT_Atan2( d2.x, d2.y );
+ *angle_out = FT_Atan2( d3.x, d3.y );
+ }
+ }
+ }
+
+ theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) );
+ theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
+
+ return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
+ theta2 < FT_SMALL_CUBIC_THRESHOLD );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** STROKE BORDERS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef enum FT_StrokeTags_
+ {
+ FT_STROKE_TAG_ON = 1, /* on-curve point */
+ FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */
+ FT_STROKE_TAG_BEGIN = 4, /* sub-path start */
+ FT_STROKE_TAG_END = 8 /* sub-path end */
+
+ } FT_StrokeTags;
+
+#define FT_STROKE_TAG_BEGIN_END ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END )
+
+ typedef struct FT_StrokeBorderRec_
+ {
+ FT_UInt num_points;
+ FT_UInt max_points;
+ FT_Vector* points;
+ FT_Byte* tags;
+ FT_Bool movable; /* TRUE for ends of lineto borders */
+ FT_Int start; /* index of current sub-path start point */
+ FT_Memory memory;
+ FT_Bool valid;
+
+ } FT_StrokeBorderRec, *FT_StrokeBorder;
+
+
+ static FT_Error
+ ft_stroke_border_grow( FT_StrokeBorder border,
+ FT_UInt new_points )
+ {
+ FT_UInt old_max = border->max_points;
+ FT_UInt new_max = border->num_points + new_points;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( new_max > old_max )
+ {
+ FT_UInt cur_max = old_max;
+ FT_Memory memory = border->memory;
+
+
+ while ( cur_max < new_max )
+ cur_max += ( cur_max >> 1 ) + 16;
+
+ if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
+ FT_RENEW_ARRAY( border->tags, old_max, cur_max ) )
+ goto Exit;
+
+ border->max_points = cur_max;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ ft_stroke_border_close( FT_StrokeBorder border,
+ FT_Bool reverse )
+ {
+ FT_UInt start = (FT_UInt)border->start;
+ FT_UInt count = border->num_points;
+
+
+ FT_ASSERT( border->start >= 0 );
+
+ /* don't record empty paths! */
+ if ( count <= start + 1U )
+ border->num_points = start;
+ else
+ {
+ /* copy the last point to the start of this sub-path, since */
+ /* it contains the `adjusted' starting coordinates */
+ border->num_points = --count;
+ border->points[start] = border->points[count];
+ border->tags[start] = border->tags[count];
+
+ if ( reverse )
+ {
+ /* reverse the points */
+ {
+ FT_Vector* vec1 = border->points + start + 1;
+ FT_Vector* vec2 = border->points + count - 1;
+
+
+ for ( ; vec1 < vec2; vec1++, vec2-- )
+ {
+ FT_Vector tmp;
+
+
+ tmp = *vec1;
+ *vec1 = *vec2;
+ *vec2 = tmp;
+ }
+ }
+
+ /* then the tags */
+ {
+ FT_Byte* tag1 = border->tags + start + 1;
+ FT_Byte* tag2 = border->tags + count - 1;
+
+
+ for ( ; tag1 < tag2; tag1++, tag2-- )
+ {
+ FT_Byte tmp;
+
+
+ tmp = *tag1;
+ *tag1 = *tag2;
+ *tag2 = tmp;
+ }
+ }
+ }
+
+ border->tags[start ] |= FT_STROKE_TAG_BEGIN;
+ border->tags[count - 1] |= FT_STROKE_TAG_END;
+ }
+
+ border->start = -1;
+ border->movable = FALSE;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_lineto( FT_StrokeBorder border,
+ FT_Vector* to,
+ FT_Bool movable )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ FT_ASSERT( border->start >= 0 );
+
+ if ( border->movable )
+ {
+ /* move last point */
+ border->points[border->num_points - 1] = *to;
+ }
+ else
+ {
+ /* don't add zero-length lineto, but always add moveto */
+ if ( border->num_points > (FT_UInt)border->start &&
+ FT_IS_SMALL( border->points[border->num_points - 1].x - to->x ) &&
+ FT_IS_SMALL( border->points[border->num_points - 1].y - to->y ) )
+ return error;
+
+ /* add one point */
+ error = ft_stroke_border_grow( border, 1 );
+ if ( !error )
+ {
+ FT_Vector* vec = border->points + border->num_points;
+ FT_Byte* tag = border->tags + border->num_points;
+
+
+ vec[0] = *to;
+ tag[0] = FT_STROKE_TAG_ON;
+
+ border->num_points += 1;
+ }
+ }
+ border->movable = movable;
+ return error;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_conicto( FT_StrokeBorder border,
+ FT_Vector* control,
+ FT_Vector* to )
+ {
+ FT_Error error;
+
+
+ FT_ASSERT( border->start >= 0 );
+
+ error = ft_stroke_border_grow( border, 2 );
+ if ( !error )
+ {
+ FT_Vector* vec = border->points + border->num_points;
+ FT_Byte* tag = border->tags + border->num_points;
+
+
+ vec[0] = *control;
+ vec[1] = *to;
+
+ tag[0] = 0;
+ tag[1] = FT_STROKE_TAG_ON;
+
+ border->num_points += 2;
+ }
+
+ border->movable = FALSE;
+
+ return error;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_cubicto( FT_StrokeBorder border,
+ FT_Vector* control1,
+ FT_Vector* control2,
+ FT_Vector* to )
+ {
+ FT_Error error;
+
+
+ FT_ASSERT( border->start >= 0 );
+
+ error = ft_stroke_border_grow( border, 3 );
+ if ( !error )
+ {
+ FT_Vector* vec = border->points + border->num_points;
+ FT_Byte* tag = border->tags + border->num_points;
+
+
+ vec[0] = *control1;
+ vec[1] = *control2;
+ vec[2] = *to;
+
+ tag[0] = FT_STROKE_TAG_CUBIC;
+ tag[1] = FT_STROKE_TAG_CUBIC;
+ tag[2] = FT_STROKE_TAG_ON;
+
+ border->num_points += 3;
+ }
+
+ border->movable = FALSE;
+
+ return error;
+ }
+
+
+#define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
+
+
+ static FT_Error
+ ft_stroke_border_arcto( FT_StrokeBorder border,
+ FT_Vector* center,
+ FT_Fixed radius,
+ FT_Angle angle_start,
+ FT_Angle angle_diff )
+ {
+ FT_Fixed coef;
+ FT_Vector a0, a1, a2, a3;
+ FT_Int i, arcs = 1;
+ FT_Error error = FT_Err_Ok;
+
+
+ /* number of cubic arcs to draw */
+ while ( angle_diff > FT_ARC_CUBIC_ANGLE * arcs ||
+ -angle_diff > FT_ARC_CUBIC_ANGLE * arcs )
+ arcs++;
+
+ /* control tangents */
+ coef = FT_Tan( angle_diff / ( 4 * arcs ) );
+ coef += coef / 3;
+
+ /* compute start and first control point */
+ FT_Vector_From_Polar( &a0, radius, angle_start );
+ a1.x = FT_MulFix( -a0.y, coef );
+ a1.y = FT_MulFix( a0.x, coef );
+
+ a0.x += center->x;
+ a0.y += center->y;
+ a1.x += a0.x;
+ a1.y += a0.y;
+
+ for ( i = 1; i <= arcs; i++ )
+ {
+ /* compute end and second control point */
+ FT_Vector_From_Polar( &a3, radius,
+ angle_start + i * angle_diff / arcs );
+ a2.x = FT_MulFix( a3.y, coef );
+ a2.y = FT_MulFix( -a3.x, coef );
+
+ a3.x += center->x;
+ a3.y += center->y;
+ a2.x += a3.x;
+ a2.y += a3.y;
+
+ /* add cubic arc */
+ error = ft_stroke_border_cubicto( border, &a1, &a2, &a3 );
+ if ( error )
+ break;
+
+ /* a0 = a3; */
+ a1.x = a3.x - a2.x + a3.x;
+ a1.y = a3.y - a2.y + a3.y;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_moveto( FT_StrokeBorder border,
+ FT_Vector* to )
+ {
+ /* close current open path if any ? */
+ if ( border->start >= 0 )
+ ft_stroke_border_close( border, FALSE );
+
+ border->start = (FT_Int)border->num_points;
+ border->movable = FALSE;
+
+ return ft_stroke_border_lineto( border, to, FALSE );
+ }
+
+
+ static void
+ ft_stroke_border_init( FT_StrokeBorder border,
+ FT_Memory memory )
+ {
+ border->memory = memory;
+ border->points = NULL;
+ border->tags = NULL;
+
+ border->num_points = 0;
+ border->max_points = 0;
+ border->start = -1;
+ border->valid = FALSE;
+ }
+
+
+ static void
+ ft_stroke_border_reset( FT_StrokeBorder border )
+ {
+ border->num_points = 0;
+ border->start = -1;
+ border->valid = FALSE;
+ }
+
+
+ static void
+ ft_stroke_border_done( FT_StrokeBorder border )
+ {
+ FT_Memory memory = border->memory;
+
+
+ FT_FREE( border->points );
+ FT_FREE( border->tags );
+
+ border->num_points = 0;
+ border->max_points = 0;
+ border->start = -1;
+ border->valid = FALSE;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_get_counts( FT_StrokeBorder border,
+ FT_UInt *anum_points,
+ FT_UInt *anum_contours )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_UInt num_points = 0;
+ FT_UInt num_contours = 0;
+
+ FT_UInt count = border->num_points;
+ FT_Vector* point = border->points;
+ FT_Byte* tags = border->tags;
+ FT_Int in_contour = 0;
+
+
+ for ( ; count > 0; count--, num_points++, point++, tags++ )
+ {
+ if ( tags[0] & FT_STROKE_TAG_BEGIN )
+ {
+ if ( in_contour != 0 )
+ goto Fail;
+
+ in_contour = 1;
+ }
+ else if ( in_contour == 0 )
+ goto Fail;
+
+ if ( tags[0] & FT_STROKE_TAG_END )
+ {
+ in_contour = 0;
+ num_contours++;
+ }
+ }
+
+ if ( in_contour != 0 )
+ goto Fail;
+
+ border->valid = TRUE;
+
+ Exit:
+ *anum_points = num_points;
+ *anum_contours = num_contours;
+ return error;
+
+ Fail:
+ num_points = 0;
+ num_contours = 0;
+ goto Exit;
+ }
+
+
+ static void
+ ft_stroke_border_export( FT_StrokeBorder border,
+ FT_Outline* outline )
+ {
+ /* copy point locations */
+ if ( border->num_points )
+ FT_ARRAY_COPY( outline->points + outline->n_points,
+ border->points,
+ border->num_points );
+
+ /* copy tags */
+ {
+ FT_UInt count = border->num_points;
+ FT_Byte* read = border->tags;
+ FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points;
+
+
+ for ( ; count > 0; count--, read++, write++ )
+ {
+ if ( *read & FT_STROKE_TAG_ON )
+ *write = FT_CURVE_TAG_ON;
+ else if ( *read & FT_STROKE_TAG_CUBIC )
+ *write = FT_CURVE_TAG_CUBIC;
+ else
+ *write = FT_CURVE_TAG_CONIC;
+ }
+ }
+
+ /* copy contours */
+ {
+ FT_UInt count = border->num_points;
+ FT_Byte* tags = border->tags;
+ FT_Short* write = outline->contours + outline->n_contours;
+ FT_Short idx = (FT_Short)outline->n_points;
+
+
+ for ( ; count > 0; count--, tags++, idx++ )
+ {
+ if ( *tags & FT_STROKE_TAG_END )
+ {
+ *write++ = idx;
+ outline->n_contours++;
+ }
+ }
+ }
+
+ outline->n_points += (short)border->num_points;
+
+ FT_ASSERT( FT_Outline_Check( outline ) == 0 );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** STROKER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
+
+ typedef struct FT_StrokerRec_
+ {
+ FT_Angle angle_in; /* direction into curr join */
+ FT_Angle angle_out; /* direction out of join */
+ FT_Vector center; /* current position */
+ FT_Fixed line_length; /* length of last lineto */
+ FT_Bool first_point; /* is this the start? */
+ FT_Bool subpath_open; /* is the subpath open? */
+ FT_Angle subpath_angle; /* subpath start direction */
+ FT_Vector subpath_start; /* subpath start position */
+ FT_Fixed subpath_line_length; /* subpath start lineto len */
+ FT_Bool handle_wide_strokes; /* use wide strokes logic? */
+
+ FT_Stroker_LineCap line_cap;
+ FT_Stroker_LineJoin line_join;
+ FT_Stroker_LineJoin line_join_saved;
+ FT_Fixed miter_limit;
+ FT_Fixed radius;
+
+ FT_StrokeBorderRec borders[2];
+ FT_Library library;
+
+ } FT_StrokerRec;
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_New( FT_Library library,
+ FT_Stroker *astroker )
+ {
+ FT_Error error; /* assigned in FT_NEW */
+ FT_Memory memory;
+ FT_Stroker stroker = NULL;
+
+
+ if ( !library )
+ return FT_THROW( Invalid_Library_Handle );
+
+ if ( !astroker )
+ return FT_THROW( Invalid_Argument );
+
+ memory = library->memory;
+
+ if ( !FT_NEW( stroker ) )
+ {
+ stroker->library = library;
+
+ ft_stroke_border_init( &stroker->borders[0], memory );
+ ft_stroke_border_init( &stroker->borders[1], memory );
+ }
+
+ *astroker = stroker;
+
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_Set( FT_Stroker stroker,
+ FT_Fixed radius,
+ FT_Stroker_LineCap line_cap,
+ FT_Stroker_LineJoin line_join,
+ FT_Fixed miter_limit )
+ {
+ if ( !stroker )
+ return;
+
+ stroker->radius = radius;
+ stroker->line_cap = line_cap;
+ stroker->line_join = line_join;
+ stroker->miter_limit = miter_limit;
+
+ /* ensure miter limit has sensible value */
+ if ( stroker->miter_limit < 0x10000L )
+ stroker->miter_limit = 0x10000L;
+
+ /* save line join style: */
+ /* line join style can be temporarily changed when stroking curves */
+ stroker->line_join_saved = line_join;
+
+ FT_Stroker_Rewind( stroker );
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_Rewind( FT_Stroker stroker )
+ {
+ if ( stroker )
+ {
+ ft_stroke_border_reset( &stroker->borders[0] );
+ ft_stroke_border_reset( &stroker->borders[1] );
+ }
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_Done( FT_Stroker stroker )
+ {
+ if ( stroker )
+ {
+ FT_Memory memory = stroker->library->memory;
+
+
+ ft_stroke_border_done( &stroker->borders[0] );
+ ft_stroke_border_done( &stroker->borders[1] );
+
+ stroker->library = NULL;
+ FT_FREE( stroker );
+ }
+ }
+
+
+ /* create a circular arc at a corner or cap */
+ static FT_Error
+ ft_stroker_arcto( FT_Stroker stroker,
+ FT_Int side )
+ {
+ FT_Angle total, rotate;
+ FT_Fixed radius = stroker->radius;
+ FT_Error error = FT_Err_Ok;
+ FT_StrokeBorder border = stroker->borders + side;
+
+
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+ if ( total == FT_ANGLE_PI )
+ total = -rotate * 2;
+
+ error = ft_stroke_border_arcto( border,
+ &stroker->center,
+ radius,
+ stroker->angle_in + rotate,
+ total );
+ border->movable = FALSE;
+ return error;
+ }
+
+
+ /* add a cap at the end of an opened path */
+ static FT_Error
+ ft_stroker_cap( FT_Stroker stroker,
+ FT_Angle angle,
+ FT_Int side )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
+ {
+ /* add a round cap */
+ stroker->angle_in = angle;
+ stroker->angle_out = angle + FT_ANGLE_PI;
+
+ error = ft_stroker_arcto( stroker, side );
+ }
+ else
+ {
+ /* add a square or butt cap */
+ FT_Vector middle, delta;
+ FT_Fixed radius = stroker->radius;
+ FT_StrokeBorder border = stroker->borders + side;
+
+
+ /* compute middle point and first angle point */
+ FT_Vector_From_Polar( &middle, radius, angle );
+ delta.x = side ? middle.y : -middle.y;
+ delta.y = side ? -middle.x : middle.x;
+
+ if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
+ {
+ middle.x += stroker->center.x;
+ middle.y += stroker->center.y;
+ }
+ else /* FT_STROKER_LINECAP_BUTT */
+ {
+ middle.x = stroker->center.x;
+ middle.y = stroker->center.y;
+ }
+
+ delta.x += middle.x;
+ delta.y += middle.y;
+
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ if ( error )
+ goto Exit;
+
+ /* compute second angle point */
+ delta.x = middle.x - delta.x + middle.x;
+ delta.y = middle.y - delta.y + middle.y;
+
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* process an inside corner, i.e. compute intersection */
+ static FT_Error
+ ft_stroker_inside( FT_Stroker stroker,
+ FT_Int side,
+ FT_Fixed line_length )
+ {
+ FT_StrokeBorder border = stroker->borders + side;
+ FT_Angle phi, theta, rotate;
+ FT_Fixed length;
+ FT_Vector sigma = { 0, 0 };
+ FT_Vector delta;
+ FT_Error error = FT_Err_Ok;
+ FT_Bool intersect; /* use intersection of lines? */
+
+
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2;
+
+ /* Only intersect borders if between two lineto's and both */
+ /* lines are long enough (line_length is zero for curves). */
+ /* Also avoid U-turns of nearly 180 degree. */
+ if ( !border->movable || line_length == 0 ||
+ theta > 0x59C000 || theta < -0x59C000 )
+ intersect = FALSE;
+ else
+ {
+ /* compute minimum required length of lines */
+ FT_Fixed min_length;
+
+
+ FT_Vector_Unit( &sigma, theta );
+ min_length =
+ ft_pos_abs( FT_MulDiv( stroker->radius, sigma.y, sigma.x ) );
+
+ intersect = FT_BOOL( min_length &&
+ stroker->line_length >= min_length &&
+ line_length >= min_length );
+ }
+
+ if ( !intersect )
+ {
+ FT_Vector_From_Polar( &delta, stroker->radius,
+ stroker->angle_out + rotate );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+
+ border->movable = FALSE;
+ }
+ else
+ {
+ /* compute median angle */
+ phi = stroker->angle_in + theta + rotate;
+
+ length = FT_DivFix( stroker->radius, sigma.x );
+
+ FT_Vector_From_Polar( &delta, length, phi );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+ }
+
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+
+ return error;
+ }
+
+
+ /* process an outside corner, i.e. compute bevel/miter/round */
+ static FT_Error
+ ft_stroker_outside( FT_Stroker stroker,
+ FT_Int side,
+ FT_Fixed line_length )
+ {
+ FT_StrokeBorder border = stroker->borders + side;
+ FT_Error error;
+ FT_Angle rotate;
+
+
+ if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
+ error = ft_stroker_arcto( stroker, side );
+ else
+ {
+ /* this is a mitered (pointed) or beveled (truncated) corner */
+ FT_Fixed radius = stroker->radius;
+ FT_Vector sigma = { 0, 0 };
+ FT_Angle theta = 0, phi = 0;
+ FT_Bool bevel, fixed_bevel;
+
+
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ bevel =
+ FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_BEVEL );
+
+ fixed_bevel =
+ FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_MITER_VARIABLE );
+
+ /* check miter limit first */
+ if ( !bevel )
+ {
+ theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2;
+
+ if ( theta == FT_ANGLE_PI2 )
+ theta = -rotate;
+
+ phi = stroker->angle_in + theta + rotate;
+
+ FT_Vector_From_Polar( &sigma, stroker->miter_limit, theta );
+
+ /* is miter limit exceeded? */
+ if ( sigma.x < 0x10000L )
+ {
+ /* don't create variable bevels for very small deviations; */
+ /* FT_Sin(x) = 0 for x <= 57 */
+ if ( fixed_bevel || ft_pos_abs( theta ) > 57 )
+ bevel = TRUE;
+ }
+ }
+
+ if ( bevel ) /* this is a bevel (broken angle) */
+ {
+ if ( fixed_bevel )
+ {
+ /* the outer corners are simply joined together */
+ FT_Vector delta;
+
+
+ /* add bevel */
+ FT_Vector_From_Polar( &delta,
+ radius,
+ stroker->angle_out + rotate );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+
+ border->movable = FALSE;
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ }
+ else /* variable bevel or clipped miter */
+ {
+ /* the miter is truncated */
+ FT_Vector middle, delta;
+ FT_Fixed coef;
+
+
+ /* compute middle point and first angle point */
+ FT_Vector_From_Polar( &middle,
+ FT_MulFix( radius, stroker->miter_limit ),
+ phi );
+
+ coef = FT_DivFix( 0x10000L - sigma.x, sigma.y );
+ delta.x = FT_MulFix( middle.y, coef );
+ delta.y = FT_MulFix( -middle.x, coef );
+
+ middle.x += stroker->center.x;
+ middle.y += stroker->center.y;
+ delta.x += middle.x;
+ delta.y += middle.y;
+
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ if ( error )
+ goto Exit;
+
+ /* compute second angle point */
+ delta.x = middle.x - delta.x + middle.x;
+ delta.y = middle.y - delta.y + middle.y;
+
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ if ( error )
+ goto Exit;
+
+ /* finally, add an end point; only needed if not lineto */
+ /* (line_length is zero for curves) */
+ if ( line_length == 0 )
+ {
+ FT_Vector_From_Polar( &delta,
+ radius,
+ stroker->angle_out + rotate );
+
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ }
+ }
+ }
+ else /* this is a miter (intersection) */
+ {
+ FT_Fixed length;
+ FT_Vector delta;
+
+
+ length = FT_MulDiv( stroker->radius, stroker->miter_limit, sigma.x );
+
+ FT_Vector_From_Polar( &delta, length, phi );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ if ( error )
+ goto Exit;
+
+ /* now add an end point; only needed if not lineto */
+ /* (line_length is zero for curves) */
+ if ( line_length == 0 )
+ {
+ FT_Vector_From_Polar( &delta,
+ stroker->radius,
+ stroker->angle_out + rotate );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ }
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ ft_stroker_process_corner( FT_Stroker stroker,
+ FT_Fixed line_length )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Angle turn;
+ FT_Int inside_side;
+
+
+ turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+
+ /* no specific corner processing is required if the turn is 0 */
+ if ( turn == 0 )
+ goto Exit;
+
+ /* when we turn to the right, the inside side is 0 */
+ /* otherwise, the inside side is 1 */
+ inside_side = ( turn < 0 );
+
+ /* process the inside side */
+ error = ft_stroker_inside( stroker, inside_side, line_length );
+ if ( error )
+ goto Exit;
+
+ /* process the outside side */
+ error = ft_stroker_outside( stroker, !inside_side, line_length );
+
+ Exit:
+ return error;
+ }
+
+
+ /* add two points to the left and right borders corresponding to the */
+ /* start of the subpath */
+ static FT_Error
+ ft_stroker_subpath_start( FT_Stroker stroker,
+ FT_Angle start_angle,
+ FT_Fixed line_length )
+ {
+ FT_Vector delta;
+ FT_Vector point;
+ FT_Error error;
+ FT_StrokeBorder border;
+
+
+ FT_Vector_From_Polar( &delta, stroker->radius,
+ start_angle + FT_ANGLE_PI2 );
+
+ point.x = stroker->center.x + delta.x;
+ point.y = stroker->center.y + delta.y;
+
+ border = stroker->borders;
+ error = ft_stroke_border_moveto( border, &point );
+ if ( error )
+ goto Exit;
+
+ point.x = stroker->center.x - delta.x;
+ point.y = stroker->center.y - delta.y;
+
+ border++;
+ error = ft_stroke_border_moveto( border, &point );
+
+ /* save angle, position, and line length for last join */
+ /* (line_length is zero for curves) */
+ stroker->subpath_angle = start_angle;
+ stroker->first_point = FALSE;
+ stroker->subpath_line_length = line_length;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_LineTo( FT_Stroker stroker,
+ FT_Vector* to )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_StrokeBorder border;
+ FT_Vector delta;
+ FT_Angle angle;
+ FT_Int side;
+ FT_Fixed line_length;
+
+
+ if ( !stroker || !to )
+ return FT_THROW( Invalid_Argument );
+
+ delta.x = to->x - stroker->center.x;
+ delta.y = to->y - stroker->center.y;
+
+ /* a zero-length lineto is a no-op; avoid creating a spurious corner */
+ if ( delta.x == 0 && delta.y == 0 )
+ goto Exit;
+
+ /* compute length of line */
+ line_length = FT_Vector_Length( &delta );
+
+ angle = FT_Atan2( delta.x, delta.y );
+ FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
+
+ /* process corner if necessary */
+ if ( stroker->first_point )
+ {
+ /* This is the first segment of a subpath. We need to */
+ /* add a point to each border at their respective starting */
+ /* point locations. */
+ error = ft_stroker_subpath_start( stroker, angle, line_length );
+ if ( error )
+ goto Exit;
+ }
+ else
+ {
+ /* process the current corner */
+ stroker->angle_out = angle;
+ error = ft_stroker_process_corner( stroker, line_length );
+ if ( error )
+ goto Exit;
+ }
+
+ /* now add a line segment to both the `inside' and `outside' paths */
+ for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
+ {
+ FT_Vector point;
+
+
+ point.x = to->x + delta.x;
+ point.y = to->y + delta.y;
+
+ /* the ends of lineto borders are movable */
+ error = ft_stroke_border_lineto( border, &point, TRUE );
+ if ( error )
+ goto Exit;
+
+ delta.x = -delta.x;
+ delta.y = -delta.y;
+ }
+
+ stroker->angle_in = angle;
+ stroker->center = *to;
+ stroker->line_length = line_length;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_ConicTo( FT_Stroker stroker,
+ FT_Vector* control,
+ FT_Vector* to )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Vector bez_stack[34];
+ FT_Vector* arc;
+ FT_Vector* limit = bez_stack + 30;
+ FT_Bool first_arc = TRUE;
+
+
+ if ( !stroker || !control || !to )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ /* if all control points are coincident, this is a no-op; */
+ /* avoid creating a spurious corner */
+ if ( FT_IS_SMALL( stroker->center.x - control->x ) &&
+ FT_IS_SMALL( stroker->center.y - control->y ) &&
+ FT_IS_SMALL( control->x - to->x ) &&
+ FT_IS_SMALL( control->y - to->y ) )
+ {
+ stroker->center = *to;
+ goto Exit;
+ }
+
+ arc = bez_stack;
+ arc[0] = *to;
+ arc[1] = *control;
+ arc[2] = stroker->center;
+
+ while ( arc >= bez_stack )
+ {
+ FT_Angle angle_in, angle_out;
+
+
+ /* initialize with current direction */
+ angle_in = angle_out = stroker->angle_in;
+
+ if ( arc < limit &&
+ !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
+ {
+ if ( stroker->first_point )
+ stroker->angle_in = angle_in;
+
+ ft_conic_split( arc );
+ arc += 2;
+ continue;
+ }
+
+ if ( first_arc )
+ {
+ first_arc = FALSE;
+
+ /* process corner if necessary */
+ if ( stroker->first_point )
+ error = ft_stroker_subpath_start( stroker, angle_in, 0 );
+ else
+ {
+ stroker->angle_out = angle_in;
+ error = ft_stroker_process_corner( stroker, 0 );
+ }
+ }
+ else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) >
+ FT_SMALL_CONIC_THRESHOLD / 4 )
+ {
+ /* if the deviation from one arc to the next is too great, */
+ /* add a round corner */
+ stroker->center = arc[2];
+ stroker->angle_out = angle_in;
+ stroker->line_join = FT_STROKER_LINEJOIN_ROUND;
+
+ error = ft_stroker_process_corner( stroker, 0 );
+
+ /* reinstate line join style */
+ stroker->line_join = stroker->line_join_saved;
+ }
+
+ if ( error )
+ goto Exit;
+
+ /* the arc's angle is small enough; we can add it directly to each */
+ /* border */
+ {
+ FT_Vector ctrl, end;
+ FT_Angle theta, phi, rotate, alpha0 = 0;
+ FT_Fixed length;
+ FT_StrokeBorder border;
+ FT_Int side;
+
+
+ theta = FT_Angle_Diff( angle_in, angle_out ) / 2;
+ phi = angle_in + theta;
+ length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
+
+ /* compute direction of original arc */
+ if ( stroker->handle_wide_strokes )
+ alpha0 = FT_Atan2( arc[0].x - arc[2].x, arc[0].y - arc[2].y );
+
+ for ( border = stroker->borders, side = 0;
+ side <= 1;
+ side++, border++ )
+ {
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ /* compute control point */
+ FT_Vector_From_Polar( &ctrl, length, phi + rotate );
+ ctrl.x += arc[1].x;
+ ctrl.y += arc[1].y;
+
+ /* compute end point */
+ FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
+ end.x += arc[0].x;
+ end.y += arc[0].y;
+
+ if ( stroker->handle_wide_strokes )
+ {
+ FT_Vector start;
+ FT_Angle alpha1;
+
+
+ /* determine whether the border radius is greater than the */
+ /* radius of curvature of the original arc */
+ start = border->points[border->num_points - 1];
+
+ alpha1 = FT_Atan2( end.x - start.x, end.y - start.y );
+
+ /* is the direction of the border arc opposite to */
+ /* that of the original arc? */
+ if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) >
+ FT_ANGLE_PI / 2 )
+ {
+ FT_Angle beta, gamma;
+ FT_Vector bvec, delta;
+ FT_Fixed blen, sinA, sinB, alen;
+
+
+ /* use the sine rule to find the intersection point */
+ beta = FT_Atan2( arc[2].x - start.x, arc[2].y - start.y );
+ gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y );
+
+ bvec.x = end.x - start.x;
+ bvec.y = end.y - start.y;
+
+ blen = FT_Vector_Length( &bvec );
+
+ sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) );
+ sinB = ft_pos_abs( FT_Sin( beta - gamma ) );
+
+ alen = FT_MulDiv( blen, sinA, sinB );
+
+ FT_Vector_From_Polar( &delta, alen, beta );
+ delta.x += start.x;
+ delta.y += start.y;
+
+ /* circumnavigate the negative sector backwards */
+ border->movable = FALSE;
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ if ( error )
+ goto Exit;
+ error = ft_stroke_border_lineto( border, &end, FALSE );
+ if ( error )
+ goto Exit;
+ error = ft_stroke_border_conicto( border, &ctrl, &start );
+ if ( error )
+ goto Exit;
+ /* and then move to the endpoint */
+ error = ft_stroke_border_lineto( border, &end, FALSE );
+ if ( error )
+ goto Exit;
+
+ continue;
+ }
+
+ /* else fall through */
+ }
+
+ /* simply add an arc */
+ error = ft_stroke_border_conicto( border, &ctrl, &end );
+ if ( error )
+ goto Exit;
+ }
+ }
+
+ arc -= 2;
+
+ stroker->angle_in = angle_out;
+ }
+
+ stroker->center = *to;
+ stroker->line_length = 0;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_CubicTo( FT_Stroker stroker,
+ FT_Vector* control1,
+ FT_Vector* control2,
+ FT_Vector* to )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Vector bez_stack[37];
+ FT_Vector* arc;
+ FT_Vector* limit = bez_stack + 32;
+ FT_Bool first_arc = TRUE;
+
+
+ if ( !stroker || !control1 || !control2 || !to )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ /* if all control points are coincident, this is a no-op; */
+ /* avoid creating a spurious corner */
+ if ( FT_IS_SMALL( stroker->center.x - control1->x ) &&
+ FT_IS_SMALL( stroker->center.y - control1->y ) &&
+ FT_IS_SMALL( control1->x - control2->x ) &&
+ FT_IS_SMALL( control1->y - control2->y ) &&
+ FT_IS_SMALL( control2->x - to->x ) &&
+ FT_IS_SMALL( control2->y - to->y ) )
+ {
+ stroker->center = *to;
+ goto Exit;
+ }
+
+ arc = bez_stack;
+ arc[0] = *to;
+ arc[1] = *control2;
+ arc[2] = *control1;
+ arc[3] = stroker->center;
+
+ while ( arc >= bez_stack )
+ {
+ FT_Angle angle_in, angle_mid, angle_out;
+
+
+ /* initialize with current direction */
+ angle_in = angle_out = angle_mid = stroker->angle_in;
+
+ if ( arc < limit &&
+ !ft_cubic_is_small_enough( arc, &angle_in,
+ &angle_mid, &angle_out ) )
+ {
+ if ( stroker->first_point )
+ stroker->angle_in = angle_in;
+
+ ft_cubic_split( arc );
+ arc += 3;
+ continue;
+ }
+
+ if ( first_arc )
+ {
+ first_arc = FALSE;
+
+ /* process corner if necessary */
+ if ( stroker->first_point )
+ error = ft_stroker_subpath_start( stroker, angle_in, 0 );
+ else
+ {
+ stroker->angle_out = angle_in;
+ error = ft_stroker_process_corner( stroker, 0 );
+ }
+ }
+ else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) >
+ FT_SMALL_CUBIC_THRESHOLD / 4 )
+ {
+ /* if the deviation from one arc to the next is too great, */
+ /* add a round corner */
+ stroker->center = arc[3];
+ stroker->angle_out = angle_in;
+ stroker->line_join = FT_STROKER_LINEJOIN_ROUND;
+
+ error = ft_stroker_process_corner( stroker, 0 );
+
+ /* reinstate line join style */
+ stroker->line_join = stroker->line_join_saved;
+ }
+
+ if ( error )
+ goto Exit;
+
+ /* the arc's angle is small enough; we can add it directly to each */
+ /* border */
+ {
+ FT_Vector ctrl1, ctrl2, end;
+ FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0;
+ FT_Fixed length1, length2;
+ FT_StrokeBorder border;
+ FT_Int side;
+
+
+ theta1 = FT_Angle_Diff( angle_in, angle_mid ) / 2;
+ theta2 = FT_Angle_Diff( angle_mid, angle_out ) / 2;
+ phi1 = ft_angle_mean( angle_in, angle_mid );
+ phi2 = ft_angle_mean( angle_mid, angle_out );
+ length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
+ length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
+
+ /* compute direction of original arc */
+ if ( stroker->handle_wide_strokes )
+ alpha0 = FT_Atan2( arc[0].x - arc[3].x, arc[0].y - arc[3].y );
+
+ for ( border = stroker->borders, side = 0;
+ side <= 1;
+ side++, border++ )
+ {
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ /* compute control points */
+ FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
+ ctrl1.x += arc[2].x;
+ ctrl1.y += arc[2].y;
+
+ FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
+ ctrl2.x += arc[1].x;
+ ctrl2.y += arc[1].y;
+
+ /* compute end point */
+ FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
+ end.x += arc[0].x;
+ end.y += arc[0].y;
+
+ if ( stroker->handle_wide_strokes )
+ {
+ FT_Vector start;
+ FT_Angle alpha1;
+
+
+ /* determine whether the border radius is greater than the */
+ /* radius of curvature of the original arc */
+ start = border->points[border->num_points - 1];
+
+ alpha1 = FT_Atan2( end.x - start.x, end.y - start.y );
+
+ /* is the direction of the border arc opposite to */
+ /* that of the original arc? */
+ if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) >
+ FT_ANGLE_PI / 2 )
+ {
+ FT_Angle beta, gamma;
+ FT_Vector bvec, delta;
+ FT_Fixed blen, sinA, sinB, alen;
+
+
+ /* use the sine rule to find the intersection point */
+ beta = FT_Atan2( arc[3].x - start.x, arc[3].y - start.y );
+ gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y );
+
+ bvec.x = end.x - start.x;
+ bvec.y = end.y - start.y;
+
+ blen = FT_Vector_Length( &bvec );
+
+ sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) );
+ sinB = ft_pos_abs( FT_Sin( beta - gamma ) );
+
+ alen = FT_MulDiv( blen, sinA, sinB );
+
+ FT_Vector_From_Polar( &delta, alen, beta );
+ delta.x += start.x;
+ delta.y += start.y;
+
+ /* circumnavigate the negative sector backwards */
+ border->movable = FALSE;
+ error = ft_stroke_border_lineto( border, &delta, FALSE );
+ if ( error )
+ goto Exit;
+ error = ft_stroke_border_lineto( border, &end, FALSE );
+ if ( error )
+ goto Exit;
+ error = ft_stroke_border_cubicto( border,
+ &ctrl2,
+ &ctrl1,
+ &start );
+ if ( error )
+ goto Exit;
+ /* and then move to the endpoint */
+ error = ft_stroke_border_lineto( border, &end, FALSE );
+ if ( error )
+ goto Exit;
+
+ continue;
+ }
+
+ /* else fall through */
+ }
+
+ /* simply add an arc */
+ error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end );
+ if ( error )
+ goto Exit;
+ }
+ }
+
+ arc -= 3;
+
+ stroker->angle_in = angle_out;
+ }
+
+ stroker->center = *to;
+ stroker->line_length = 0;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_BeginSubPath( FT_Stroker stroker,
+ FT_Vector* to,
+ FT_Bool open )
+ {
+ if ( !stroker || !to )
+ return FT_THROW( Invalid_Argument );
+
+ /* We cannot process the first point, because there is not enough */
+ /* information regarding its corner/cap. The latter will be processed */
+ /* in the `FT_Stroker_EndSubPath' routine. */
+ /* */
+ stroker->first_point = TRUE;
+ stroker->center = *to;
+ stroker->subpath_open = open;
+
+ /* Determine if we need to check whether the border radius is greater */
+ /* than the radius of curvature of a curve, to handle this case */
+ /* specially. This is only required if bevel joins or butt caps may */
+ /* be created, because round & miter joins and round & square caps */
+ /* cover the negative sector created with wide strokes. */
+ stroker->handle_wide_strokes =
+ FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_ROUND ||
+ ( stroker->subpath_open &&
+ stroker->line_cap == FT_STROKER_LINECAP_BUTT ) );
+
+ /* record the subpath start point for each border */
+ stroker->subpath_start = *to;
+
+ stroker->angle_in = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Error
+ ft_stroker_add_reverse_left( FT_Stroker stroker,
+ FT_Bool open )
+ {
+ FT_StrokeBorder right = stroker->borders + 0;
+ FT_StrokeBorder left = stroker->borders + 1;
+ FT_Int new_points;
+ FT_Error error = FT_Err_Ok;
+
+
+ FT_ASSERT( left->start >= 0 );
+
+ new_points = (FT_Int)left->num_points - left->start;
+ if ( new_points > 0 )
+ {
+ error = ft_stroke_border_grow( right, (FT_UInt)new_points );
+ if ( error )
+ goto Exit;
+
+ {
+ FT_Vector* dst_point = right->points + right->num_points;
+ FT_Byte* dst_tag = right->tags + right->num_points;
+ FT_Vector* src_point = left->points + left->num_points - 1;
+ FT_Byte* src_tag = left->tags + left->num_points - 1;
+
+
+ while ( src_point >= left->points + left->start )
+ {
+ *dst_point = *src_point;
+ *dst_tag = *src_tag;
+
+ if ( open )
+ dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
+ else
+ {
+ FT_Byte ttag =
+ (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
+
+
+ /* switch begin/end tags if necessary */
+ if ( ttag == FT_STROKE_TAG_BEGIN ||
+ ttag == FT_STROKE_TAG_END )
+ dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
+ }
+
+ src_point--;
+ src_tag--;
+ dst_point++;
+ dst_tag++;
+ }
+ }
+
+ left->num_points = (FT_UInt)left->start;
+ right->num_points += (FT_UInt)new_points;
+
+ right->movable = FALSE;
+ left->movable = FALSE;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ /* there's a lot of magic in this function! */
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_EndSubPath( FT_Stroker stroker )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( !stroker )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ if ( stroker->subpath_open )
+ {
+ FT_StrokeBorder right = stroker->borders;
+
+
+ /* All right, this is an opened path, we need to add a cap between */
+ /* right & left, add the reverse of left, then add a final cap */
+ /* between left & right. */
+ error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
+ if ( error )
+ goto Exit;
+
+ /* add reversed points from `left' to `right' */
+ error = ft_stroker_add_reverse_left( stroker, TRUE );
+ if ( error )
+ goto Exit;
+
+ /* now add the final cap */
+ stroker->center = stroker->subpath_start;
+ error = ft_stroker_cap( stroker,
+ stroker->subpath_angle + FT_ANGLE_PI, 0 );
+ if ( error )
+ goto Exit;
+
+ /* Now end the right subpath accordingly. The left one is */
+ /* rewind and doesn't need further processing. */
+ ft_stroke_border_close( right, FALSE );
+ }
+ else
+ {
+ /* close the path if needed */
+ if ( !FT_IS_SMALL( stroker->center.x - stroker->subpath_start.x ) ||
+ !FT_IS_SMALL( stroker->center.y - stroker->subpath_start.y ) )
+ {
+ error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
+ if ( error )
+ goto Exit;
+ }
+
+ /* process the corner */
+ stroker->angle_out = stroker->subpath_angle;
+
+ error = ft_stroker_process_corner( stroker,
+ stroker->subpath_line_length );
+ if ( error )
+ goto Exit;
+
+ /* then end our two subpaths */
+ ft_stroke_border_close( stroker->borders + 0, FALSE );
+ ft_stroke_border_close( stroker->borders + 1, TRUE );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_GetBorderCounts( FT_Stroker stroker,
+ FT_StrokerBorder border,
+ FT_UInt *anum_points,
+ FT_UInt *anum_contours )
+ {
+ FT_UInt num_points = 0, num_contours = 0;
+ FT_Error error;
+
+
+ if ( !stroker || border > 1 )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ error = ft_stroke_border_get_counts( stroker->borders + border,
+ &num_points, &num_contours );
+ Exit:
+ if ( anum_points )
+ *anum_points = num_points;
+
+ if ( anum_contours )
+ *anum_contours = num_contours;
+
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_GetCounts( FT_Stroker stroker,
+ FT_UInt *anum_points,
+ FT_UInt *anum_contours )
+ {
+ FT_UInt count1, count2, num_points = 0;
+ FT_UInt count3, count4, num_contours = 0;
+ FT_Error error;
+
+
+ if ( !stroker )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Exit;
+ }
+
+ error = ft_stroke_border_get_counts( stroker->borders + 0,
+ &count1, &count2 );
+ if ( error )
+ goto Exit;
+
+ error = ft_stroke_border_get_counts( stroker->borders + 1,
+ &count3, &count4 );
+ if ( error )
+ goto Exit;
+
+ num_points = count1 + count3;
+ num_contours = count2 + count4;
+
+ Exit:
+ if ( anum_points )
+ *anum_points = num_points;
+
+ if ( anum_contours )
+ *anum_contours = num_contours;
+
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_ExportBorder( FT_Stroker stroker,
+ FT_StrokerBorder border,
+ FT_Outline* outline )
+ {
+ if ( !stroker || !outline )
+ return;
+
+ if ( border == FT_STROKER_BORDER_LEFT ||
+ border == FT_STROKER_BORDER_RIGHT )
+ {
+ FT_StrokeBorder sborder = & stroker->borders[border];
+
+
+ if ( sborder->valid )
+ ft_stroke_border_export( sborder, outline );
+ }
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_Export( FT_Stroker stroker,
+ FT_Outline* outline )
+ {
+ FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
+ FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ /*
+ * The following is very similar to FT_Outline_Decompose, except
+ * that we do support opened paths, and do not scale the outline.
+ */
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_ParseOutline( FT_Stroker stroker,
+ FT_Outline* outline,
+ FT_Bool opened )
+ {
+ FT_Vector v_last;
+ FT_Vector v_control;
+ FT_Vector v_start;
+
+ FT_Vector* point;
+ FT_Vector* limit;
+ char* tags;
+
+ FT_Error error;
+
+ FT_Int n; /* index of contour in outline */
+ FT_UInt first; /* index of first point in contour */
+ FT_Int tag; /* current point's state */
+
+
+ if ( !outline )
+ return FT_THROW( Invalid_Outline );
+
+ if ( !stroker )
+ return FT_THROW( Invalid_Argument );
+
+ FT_Stroker_Rewind( stroker );
+
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ FT_UInt last; /* index of last point in contour */
+
+
+ last = (FT_UInt)outline->contours[n];
+ limit = outline->points + last;
+
+ /* skip empty points; we don't stroke these */
+ if ( last <= first )
+ {
+ first = last + 1;
+ continue;
+ }
+
+ v_start = outline->points[first];
+ v_last = outline->points[last];
+
+ v_control = v_start;
+
+ point = outline->points + first;
+ tags = outline->tags + first;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == FT_CURVE_TAG_CONIC )
+ {
+ /* First point is conic control. Yes, this happens. */
+ if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+ }
+ point--;
+ tags--;
+ }
+
+ error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
+ if ( error )
+ goto Exit;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = FT_CURVE_TAG( tags[0] );
+ switch ( tag )
+ {
+ case FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ FT_Vector vec;
+
+
+ vec.x = point->x;
+ vec.y = point->y;
+
+ error = FT_Stroker_LineTo( stroker, &vec );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ case FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ v_control.x = point->x;
+ v_control.y = point->y;
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ FT_Vector vec;
+ FT_Vector v_middle;
+
+
+ point++;
+ tags++;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ vec = point[0];
+
+ if ( tag == FT_CURVE_TAG_ON )
+ {
+ error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ if ( tag != FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + vec.x ) / 2;
+ v_middle.y = ( v_control.y + vec.y ) / 2;
+
+ error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
+ if ( error )
+ goto Exit;
+
+ v_control = vec;
+ goto Do_Conic;
+ }
+
+ error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
+ goto Close;
+
+ default: /* FT_CURVE_TAG_CUBIC */
+ {
+ FT_Vector vec1, vec2;
+
+
+ if ( point + 1 > limit ||
+ FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ vec1 = point[-2];
+ vec2 = point[-1];
+
+ if ( point <= limit )
+ {
+ FT_Vector vec;
+
+
+ vec = point[0];
+
+ error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
+ goto Close;
+ }
+ }
+ }
+
+ Close:
+ if ( error )
+ goto Exit;
+
+ /* don't try to end the path if no segments have been generated */
+ if ( !stroker->first_point )
+ {
+ error = FT_Stroker_EndSubPath( stroker );
+ if ( error )
+ goto Exit;
+ }
+
+ first = last + 1;
+ }
+
+ return FT_Err_Ok;
+
+ Exit:
+ return error;
+
+ Invalid_Outline:
+ return FT_THROW( Invalid_Outline );
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_Stroke( FT_Glyph *pglyph,
+ FT_Stroker stroker,
+ FT_Bool destroy )
+ {
+ FT_Error error = FT_ERR( Invalid_Argument );
+ FT_Glyph glyph = NULL;
+
+
+ if ( !pglyph )
+ goto Exit;
+
+ glyph = *pglyph;
+ if ( !glyph || glyph->clazz != &ft_outline_glyph_class )
+ goto Exit;
+
+ {
+ FT_Glyph copy;
+
+
+ error = FT_Glyph_Copy( glyph, &copy );
+ if ( error )
+ goto Exit;
+
+ glyph = copy;
+ }
+
+ {
+ FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph;
+ FT_Outline* outline = &oglyph->outline;
+ FT_UInt num_points, num_contours;
+
+
+ error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
+ if ( error )
+ goto Fail;
+
+ FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
+
+ FT_Outline_Done( glyph->library, outline );
+
+ error = FT_Outline_New( glyph->library,
+ num_points,
+ (FT_Int)num_contours,
+ outline );
+ if ( error )
+ goto Fail;
+
+ outline->n_points = 0;
+ outline->n_contours = 0;
+
+ FT_Stroker_Export( stroker, outline );
+ }
+
+ if ( destroy )
+ FT_Done_Glyph( *pglyph );
+
+ *pglyph = glyph;
+ goto Exit;
+
+ Fail:
+ FT_Done_Glyph( glyph );
+ glyph = NULL;
+
+ if ( !destroy )
+ *pglyph = NULL;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_StrokeBorder( FT_Glyph *pglyph,
+ FT_Stroker stroker,
+ FT_Bool inside,
+ FT_Bool destroy )
+ {
+ FT_Error error = FT_ERR( Invalid_Argument );
+ FT_Glyph glyph = NULL;
+
+
+ if ( !pglyph )
+ goto Exit;
+
+ glyph = *pglyph;
+ if ( !glyph || glyph->clazz != &ft_outline_glyph_class )
+ goto Exit;
+
+ {
+ FT_Glyph copy;
+
+
+ error = FT_Glyph_Copy( glyph, &copy );
+ if ( error )
+ goto Exit;
+
+ glyph = copy;
+ }
+
+ {
+ FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph;
+ FT_StrokerBorder border;
+ FT_Outline* outline = &oglyph->outline;
+ FT_UInt num_points, num_contours;
+
+
+ border = FT_Outline_GetOutsideBorder( outline );
+ if ( inside )
+ {
+ if ( border == FT_STROKER_BORDER_LEFT )
+ border = FT_STROKER_BORDER_RIGHT;
+ else
+ border = FT_STROKER_BORDER_LEFT;
+ }
+
+ error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
+ if ( error )
+ goto Fail;
+
+ FT_Stroker_GetBorderCounts( stroker, border,
+ &num_points, &num_contours );
+
+ FT_Outline_Done( glyph->library, outline );
+
+ error = FT_Outline_New( glyph->library,
+ num_points,
+ (FT_Int)num_contours,
+ outline );
+ if ( error )
+ goto Fail;
+
+ outline->n_points = 0;
+ outline->n_contours = 0;
+
+ FT_Stroker_ExportBorder( stroker, border, outline );
+ }
+
+ if ( destroy )
+ FT_Done_Glyph( *pglyph );
+
+ *pglyph = glyph;
+ goto Exit;
+
+ Fail:
+ FT_Done_Glyph( glyph );
+ glyph = NULL;
+
+ if ( !destroy )
+ *pglyph = NULL;
+
+ Exit:
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftsynth.c b/modules/freetype2/src/base/ftsynth.c
new file mode 100644
index 0000000000..6ec25e13e4
--- /dev/null
+++ b/modules/freetype2/src/base/ftsynth.c
@@ -0,0 +1,172 @@
+/****************************************************************************
+ *
+ * ftsynth.c
+ *
+ * FreeType synthesizing code for emboldening and slanting (body).
+ *
+ * Copyright (C) 2000-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/ftsynth.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftbitmap.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 synth
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** EXPERIMENTAL OBLIQUING SUPPORT ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* documentation is in ftsynth.h */
+
+ FT_EXPORT_DEF( void )
+ FT_GlyphSlot_Oblique( FT_GlyphSlot slot )
+ {
+ /* Value '0x0366A' corresponds to a shear angle of about 12 degrees. */
+ FT_GlyphSlot_Slant( slot, 0x0366A, 0 );
+ }
+
+
+ /* documentation is in ftsynth.h */
+
+ FT_EXPORT_DEF( void )
+ FT_GlyphSlot_Slant( FT_GlyphSlot slot,
+ FT_Fixed xslant,
+ FT_Fixed yslant )
+ {
+ FT_Matrix transform;
+ FT_Outline* outline;
+
+
+ if ( !slot )
+ return;
+
+ outline = &slot->outline;
+
+ /* only oblique outline glyphs */
+ if ( slot->format != FT_GLYPH_FORMAT_OUTLINE )
+ return;
+
+ /* we don't touch the advance width */
+
+ /* For italic, simply apply a shear transform */
+ transform.xx = 0x10000L;
+ transform.yx = -yslant;
+
+ transform.xy = xslant;
+ transform.yy = 0x10000L;
+
+ FT_Outline_Transform( outline, &transform );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** EXPERIMENTAL EMBOLDENING SUPPORT ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* documentation is in ftsynth.h */
+
+ FT_EXPORT_DEF( void )
+ FT_GlyphSlot_Embolden( FT_GlyphSlot slot )
+ {
+ FT_Library library;
+ FT_Face face;
+ FT_Error error;
+ FT_Pos xstr, ystr;
+
+
+ if ( !slot )
+ return;
+
+ library = slot->library;
+ face = slot->face;
+
+ if ( slot->format != FT_GLYPH_FORMAT_OUTLINE &&
+ slot->format != FT_GLYPH_FORMAT_BITMAP )
+ return;
+
+ /* some reasonable strength */
+ xstr = FT_MulFix( face->units_per_EM,
+ face->size->metrics.y_scale ) / 24;
+ ystr = xstr;
+
+ if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+ FT_Outline_EmboldenXY( &slot->outline, xstr, ystr );
+
+ else /* slot->format == FT_GLYPH_FORMAT_BITMAP */
+ {
+ /* round to full pixels */
+ xstr &= ~63;
+ if ( xstr == 0 )
+ xstr = 1 << 6;
+ ystr &= ~63;
+
+ /*
+ * XXX: overflow check for 16-bit system, for compatibility
+ * with FT_GlyphSlot_Embolden() since FreeType 2.1.10.
+ * unfortunately, this function return no informations
+ * about the cause of error.
+ */
+ if ( ( ystr >> 6 ) > FT_INT_MAX || ( ystr >> 6 ) < FT_INT_MIN )
+ {
+ FT_TRACE1(( "FT_GlyphSlot_Embolden:" ));
+ FT_TRACE1(( "too strong emboldening parameter ystr=%ld\n", ystr ));
+ return;
+ }
+ error = FT_GlyphSlot_Own_Bitmap( slot );
+ if ( error )
+ return;
+
+ error = FT_Bitmap_Embolden( library, &slot->bitmap, xstr, ystr );
+ if ( error )
+ return;
+ }
+
+ if ( slot->advance.x )
+ slot->advance.x += xstr;
+
+ if ( slot->advance.y )
+ slot->advance.y += ystr;
+
+ slot->metrics.width += xstr;
+ slot->metrics.height += ystr;
+ slot->metrics.horiAdvance += xstr;
+ slot->metrics.vertAdvance += ystr;
+ slot->metrics.horiBearingY += ystr;
+
+ /* XXX: 16-bit overflow case must be excluded before here */
+ if ( slot->format == FT_GLYPH_FORMAT_BITMAP )
+ slot->bitmap_top += (FT_Int)( ystr >> 6 );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftsystem.c b/modules/freetype2/src/base/ftsystem.c
new file mode 100644
index 0000000000..fcd289d19f
--- /dev/null
+++ b/modules/freetype2/src/base/ftsystem.c
@@ -0,0 +1,333 @@
+/****************************************************************************
+ *
+ * ftsystem.c
+ *
+ * ANSI-specific FreeType low-level system 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.
+ *
+ */
+
+ /**************************************************************************
+ *
+ * This file contains the default interface used by FreeType to access
+ * low-level, i.e. memory management, i/o access as well as thread
+ * synchronisation. It can be replaced by user-specific routines if
+ * necessary.
+ *
+ */
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/ftsystem.h>
+#include <freetype/fterrors.h>
+#include <freetype/fttypes.h>
+
+
+ /**************************************************************************
+ *
+ * MEMORY MANAGEMENT INTERFACE
+ *
+ */
+
+ /**************************************************************************
+ *
+ * It is not necessary to do any error checking for the
+ * allocation-related functions. This will be done by the higher level
+ * routines like ft_mem_alloc() or ft_mem_realloc().
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ft_alloc
+ *
+ * @Description:
+ * The memory allocation function.
+ *
+ * @Input:
+ * memory ::
+ * A pointer to the memory object.
+ *
+ * size ::
+ * The requested size in bytes.
+ *
+ * @Return:
+ * The address of newly allocated block.
+ */
+ FT_CALLBACK_DEF( void* )
+ ft_alloc( FT_Memory memory,
+ long size )
+ {
+ FT_UNUSED( memory );
+
+ return ft_smalloc( (size_t)size );
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ft_realloc
+ *
+ * @Description:
+ * The memory reallocation function.
+ *
+ * @Input:
+ * memory ::
+ * A pointer to the memory object.
+ *
+ * cur_size ::
+ * The current size of the allocated memory block.
+ *
+ * new_size ::
+ * The newly requested size in bytes.
+ *
+ * block ::
+ * The current address of the block in memory.
+ *
+ * @Return:
+ * The address of the reallocated memory block.
+ */
+ FT_CALLBACK_DEF( void* )
+ ft_realloc( FT_Memory memory,
+ long cur_size,
+ long new_size,
+ void* block )
+ {
+ FT_UNUSED( memory );
+ FT_UNUSED( cur_size );
+
+ return ft_srealloc( block, (size_t)new_size );
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ft_free
+ *
+ * @Description:
+ * The memory release function.
+ *
+ * @Input:
+ * memory ::
+ * A pointer to the memory object.
+ *
+ * block ::
+ * The address of block in memory to be freed.
+ */
+ FT_CALLBACK_DEF( void )
+ ft_free( FT_Memory memory,
+ void* block )
+ {
+ FT_UNUSED( memory );
+
+ ft_sfree( block );
+ }
+
+
+ /**************************************************************************
+ *
+ * RESOURCE MANAGEMENT INTERFACE
+ *
+ */
+
+#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT
+
+ /**************************************************************************
+ *
+ * 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 io
+
+ /* We use the macro STREAM_FILE for convenience to extract the */
+ /* system-specific stream handle from a given FreeType stream object */
+#define STREAM_FILE( stream ) ( (FT_FILE*)stream->descriptor.pointer )
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ft_ansi_stream_close
+ *
+ * @Description:
+ * The function to close a stream.
+ *
+ * @Input:
+ * stream ::
+ * A pointer to the stream object.
+ */
+ FT_CALLBACK_DEF( void )
+ ft_ansi_stream_close( FT_Stream stream )
+ {
+ ft_fclose( STREAM_FILE( stream ) );
+
+ stream->descriptor.pointer = NULL;
+ stream->size = 0;
+ stream->base = NULL;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ft_ansi_stream_io
+ *
+ * @Description:
+ * The function to open a stream.
+ *
+ * @Input:
+ * stream ::
+ * A pointer to the stream object.
+ *
+ * offset ::
+ * The position in the data stream to start reading.
+ *
+ * buffer ::
+ * The address of buffer to store the read data.
+ *
+ * count ::
+ * The number of bytes to read from the stream.
+ *
+ * @Return:
+ * The number of bytes actually read. If `count' is zero (this is,
+ * the function is used for seeking), a non-zero return value
+ * indicates an error.
+ */
+ FT_CALLBACK_DEF( unsigned long )
+ ft_ansi_stream_io( FT_Stream stream,
+ unsigned long offset,
+ unsigned char* buffer,
+ unsigned long count )
+ {
+ FT_FILE* file;
+
+
+ if ( !count && offset > stream->size )
+ return 1;
+
+ file = STREAM_FILE( stream );
+
+ if ( stream->pos != offset )
+ ft_fseek( file, (long)offset, SEEK_SET );
+
+ return (unsigned long)ft_fread( buffer, 1, count, file );
+ }
+
+
+ /* documentation is in ftstream.h */
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_Open( FT_Stream stream,
+ const char* filepathname )
+ {
+ FT_FILE* file;
+
+
+ if ( !stream )
+ return FT_THROW( Invalid_Stream_Handle );
+
+ stream->descriptor.pointer = NULL;
+ stream->pathname.pointer = (char*)filepathname;
+ stream->base = NULL;
+ stream->pos = 0;
+ stream->read = NULL;
+ stream->close = NULL;
+
+ file = ft_fopen( filepathname, "rb" );
+ if ( !file )
+ {
+ FT_ERROR(( "FT_Stream_Open:"
+ " could not open `%s'\n", filepathname ));
+
+ return FT_THROW( Cannot_Open_Resource );
+ }
+
+ ft_fseek( file, 0, SEEK_END );
+ stream->size = (unsigned long)ft_ftell( file );
+ if ( !stream->size )
+ {
+ FT_ERROR(( "FT_Stream_Open:" ));
+ FT_ERROR(( " opened `%s' but zero-sized\n", filepathname ));
+ ft_fclose( file );
+ return FT_THROW( Cannot_Open_Stream );
+ }
+ ft_fseek( file, 0, SEEK_SET );
+
+ stream->descriptor.pointer = file;
+ stream->read = ft_ansi_stream_io;
+ stream->close = ft_ansi_stream_close;
+
+ FT_TRACE1(( "FT_Stream_Open:" ));
+ FT_TRACE1(( " opened `%s' (%ld bytes) successfully\n",
+ filepathname, stream->size ));
+
+ return FT_Err_Ok;
+ }
+
+#endif /* !FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */
+
+#ifdef FT_DEBUG_MEMORY
+
+ extern FT_Int
+ ft_mem_debug_init( FT_Memory memory );
+
+ extern void
+ ft_mem_debug_done( FT_Memory memory );
+
+#endif
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( FT_Memory )
+ FT_New_Memory( void )
+ {
+ FT_Memory memory;
+
+
+ memory = (FT_Memory)ft_smalloc( sizeof ( *memory ) );
+ if ( memory )
+ {
+ memory->user = NULL;
+ memory->alloc = ft_alloc;
+ memory->realloc = ft_realloc;
+ memory->free = ft_free;
+#ifdef FT_DEBUG_MEMORY
+ ft_mem_debug_init( memory );
+#endif
+ }
+
+ return memory;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( void )
+ FT_Done_Memory( FT_Memory memory )
+ {
+#ifdef FT_DEBUG_MEMORY
+ ft_mem_debug_done( memory );
+#endif
+ ft_sfree( memory );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/fttrigon.c b/modules/freetype2/src/base/fttrigon.c
new file mode 100644
index 0000000000..2dd2c3459e
--- /dev/null
+++ b/modules/freetype2/src/base/fttrigon.c
@@ -0,0 +1,517 @@
+/****************************************************************************
+ *
+ * fttrigon.c
+ *
+ * FreeType trigonometric functions (body).
+ *
+ * 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 is a fixed-point CORDIC implementation of trigonometric
+ * functions as well as transformations between Cartesian and polar
+ * coordinates. The angles are represented as 16.16 fixed-point values
+ * in degrees, i.e., the angular resolution is 2^-16 degrees. Note that
+ * only vectors longer than 2^16*180/pi (or at least 22 bits) on a
+ * discrete Cartesian grid can have the same or better angular
+ * resolution. Therefore, to maintain this precision, some functions
+ * require an interim upscaling of the vectors, whereas others operate
+ * with 24-bit long vectors directly.
+ *
+ */
+
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftcalc.h>
+#include <freetype/fttrigon.h>
+
+
+ /* the Cordic shrink factor 0.858785336480436 * 2^32 */
+#define FT_TRIG_SCALE 0xDBD95B16UL
+
+ /* the highest bit in overflow-safe vector components, */
+ /* MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
+#define FT_TRIG_SAFE_MSB 29
+
+ /* this table was generated for FT_PI = 180L << 16, i.e. degrees */
+#define FT_TRIG_MAX_ITERS 23
+
+ static const FT_Angle
+ ft_trig_arctan_table[] =
+ {
+ 1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
+ 14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
+ 57L, 29L, 14L, 7L, 4L, 2L, 1L
+ };
+
+
+#ifdef FT_INT64
+
+ /* multiply a given value by the CORDIC shrink factor */
+ static FT_Fixed
+ ft_trig_downscale( FT_Fixed val )
+ {
+ FT_Int s = 1;
+
+
+ if ( val < 0 )
+ {
+ val = -val;
+ s = -1;
+ }
+
+ /* 0x40000000 comes from regression analysis between true */
+ /* and CORDIC hypotenuse, so it minimizes the error */
+ val = (FT_Fixed)(
+ ( (FT_UInt64)val * FT_TRIG_SCALE + 0x40000000UL ) >> 32 );
+
+ return s < 0 ? -val : val;
+ }
+
+#else /* !FT_INT64 */
+
+ /* multiply a given value by the CORDIC shrink factor */
+ static FT_Fixed
+ ft_trig_downscale( FT_Fixed val )
+ {
+ FT_Int s = 1;
+ FT_UInt32 lo1, hi1, lo2, hi2, lo, hi, i1, i2;
+
+
+ if ( val < 0 )
+ {
+ val = -val;
+ s = -1;
+ }
+
+ lo1 = (FT_UInt32)val & 0x0000FFFFU;
+ hi1 = (FT_UInt32)val >> 16;
+ lo2 = FT_TRIG_SCALE & 0x0000FFFFU;
+ hi2 = FT_TRIG_SCALE >> 16;
+
+ lo = lo1 * lo2;
+ i1 = lo1 * hi2;
+ i2 = lo2 * hi1;
+ hi = hi1 * hi2;
+
+ /* Check carry overflow of i1 + i2 */
+ i1 += i2;
+ hi += (FT_UInt32)( i1 < i2 ) << 16;
+
+ hi += i1 >> 16;
+ i1 = i1 << 16;
+
+ /* Check carry overflow of i1 + lo */
+ lo += i1;
+ hi += ( lo < i1 );
+
+ /* 0x40000000 comes from regression analysis between true */
+ /* and CORDIC hypotenuse, so it minimizes the error */
+
+ /* Check carry overflow of lo + 0x40000000 */
+ lo += 0x40000000UL;
+ hi += ( lo < 0x40000000UL );
+
+ val = (FT_Fixed)hi;
+
+ return s < 0 ? -val : val;
+ }
+
+#endif /* !FT_INT64 */
+
+
+ /* undefined and never called for zero vector */
+ static FT_Int
+ ft_trig_prenorm( FT_Vector* vec )
+ {
+ FT_Pos x, y;
+ FT_Int shift;
+
+
+ x = vec->x;
+ y = vec->y;
+
+ shift = FT_MSB( (FT_UInt32)( FT_ABS( x ) | FT_ABS( y ) ) );
+
+ if ( shift <= FT_TRIG_SAFE_MSB )
+ {
+ shift = FT_TRIG_SAFE_MSB - shift;
+ vec->x = (FT_Pos)( (FT_ULong)x << shift );
+ vec->y = (FT_Pos)( (FT_ULong)y << shift );
+ }
+ else
+ {
+ shift -= FT_TRIG_SAFE_MSB;
+ vec->x = x >> shift;
+ vec->y = y >> shift;
+ shift = -shift;
+ }
+
+ return shift;
+ }
+
+
+ static void
+ ft_trig_pseudo_rotate( FT_Vector* vec,
+ FT_Angle theta )
+ {
+ FT_Int i;
+ FT_Fixed x, y, xtemp, b;
+ const FT_Angle *arctanptr;
+
+
+ x = vec->x;
+ y = vec->y;
+
+ /* Rotate inside [-PI/4,PI/4] sector */
+ while ( theta < -FT_ANGLE_PI4 )
+ {
+ xtemp = y;
+ y = -x;
+ x = xtemp;
+ theta += FT_ANGLE_PI2;
+ }
+
+ while ( theta > FT_ANGLE_PI4 )
+ {
+ xtemp = -y;
+ y = x;
+ x = xtemp;
+ theta -= FT_ANGLE_PI2;
+ }
+
+ arctanptr = ft_trig_arctan_table;
+
+ /* Pseudorotations, with right shifts */
+ for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ )
+ {
+ if ( theta < 0 )
+ {
+ xtemp = x + ( ( y + b ) >> i );
+ y = y - ( ( x + b ) >> i );
+ x = xtemp;
+ theta += *arctanptr++;
+ }
+ else
+ {
+ xtemp = x - ( ( y + b ) >> i );
+ y = y + ( ( x + b ) >> i );
+ x = xtemp;
+ theta -= *arctanptr++;
+ }
+ }
+
+ vec->x = x;
+ vec->y = y;
+ }
+
+
+ static void
+ ft_trig_pseudo_polarize( FT_Vector* vec )
+ {
+ FT_Angle theta;
+ FT_Int i;
+ FT_Fixed x, y, xtemp, b;
+ const FT_Angle *arctanptr;
+
+
+ x = vec->x;
+ y = vec->y;
+
+ /* Get the vector into [-PI/4,PI/4] sector */
+ if ( y > x )
+ {
+ if ( y > -x )
+ {
+ theta = FT_ANGLE_PI2;
+ xtemp = y;
+ y = -x;
+ x = xtemp;
+ }
+ else
+ {
+ theta = y > 0 ? FT_ANGLE_PI : -FT_ANGLE_PI;
+ x = -x;
+ y = -y;
+ }
+ }
+ else
+ {
+ if ( y < -x )
+ {
+ theta = -FT_ANGLE_PI2;
+ xtemp = -y;
+ y = x;
+ x = xtemp;
+ }
+ else
+ {
+ theta = 0;
+ }
+ }
+
+ arctanptr = ft_trig_arctan_table;
+
+ /* Pseudorotations, with right shifts */
+ for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ )
+ {
+ if ( y > 0 )
+ {
+ xtemp = x + ( ( y + b ) >> i );
+ y = y - ( ( x + b ) >> i );
+ x = xtemp;
+ theta += *arctanptr++;
+ }
+ else
+ {
+ xtemp = x - ( ( y + b ) >> i );
+ y = y + ( ( x + b ) >> i );
+ x = xtemp;
+ theta -= *arctanptr++;
+ }
+ }
+
+ /* round theta to acknowledge its error that mostly comes */
+ /* from accumulated rounding errors in the arctan table */
+ if ( theta >= 0 )
+ theta = FT_PAD_ROUND( theta, 16 );
+ else
+ theta = -FT_PAD_ROUND( -theta, 16 );
+
+ vec->x = x;
+ vec->y = theta;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_Cos( FT_Angle angle )
+ {
+ FT_Vector v;
+
+
+ FT_Vector_Unit( &v, angle );
+
+ return v.x;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_Sin( FT_Angle angle )
+ {
+ FT_Vector v;
+
+
+ FT_Vector_Unit( &v, angle );
+
+ return v.y;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_Tan( FT_Angle angle )
+ {
+ FT_Vector v = { 1 << 24, 0 };
+
+
+ ft_trig_pseudo_rotate( &v, angle );
+
+ return FT_DivFix( v.y, v.x );
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Angle )
+ FT_Atan2( FT_Fixed dx,
+ FT_Fixed dy )
+ {
+ FT_Vector v;
+
+
+ if ( dx == 0 && dy == 0 )
+ return 0;
+
+ v.x = dx;
+ v.y = dy;
+ ft_trig_prenorm( &v );
+ ft_trig_pseudo_polarize( &v );
+
+ return v.y;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_Unit( FT_Vector* vec,
+ FT_Angle angle )
+ {
+ if ( !vec )
+ return;
+
+ vec->x = FT_TRIG_SCALE >> 8;
+ vec->y = 0;
+ ft_trig_pseudo_rotate( vec, angle );
+ vec->x = ( vec->x + 0x80L ) >> 8;
+ vec->y = ( vec->y + 0x80L ) >> 8;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_Rotate( FT_Vector* vec,
+ FT_Angle angle )
+ {
+ FT_Int shift;
+ FT_Vector v;
+
+
+ if ( !vec || !angle )
+ return;
+
+ v = *vec;
+
+ if ( v.x == 0 && v.y == 0 )
+ return;
+
+ shift = ft_trig_prenorm( &v );
+ ft_trig_pseudo_rotate( &v, angle );
+ v.x = ft_trig_downscale( v.x );
+ v.y = ft_trig_downscale( v.y );
+
+ if ( shift > 0 )
+ {
+ FT_Int32 half = (FT_Int32)1L << ( shift - 1 );
+
+
+ vec->x = ( v.x + half - ( v.x < 0 ) ) >> shift;
+ vec->y = ( v.y + half - ( v.y < 0 ) ) >> shift;
+ }
+ else
+ {
+ shift = -shift;
+ vec->x = (FT_Pos)( (FT_ULong)v.x << shift );
+ vec->y = (FT_Pos)( (FT_ULong)v.y << shift );
+ }
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_Vector_Length( FT_Vector* vec )
+ {
+ FT_Int shift;
+ FT_Vector v;
+
+
+ if ( !vec )
+ return 0;
+
+ v = *vec;
+
+ /* handle trivial cases */
+ if ( v.x == 0 )
+ {
+ return FT_ABS( v.y );
+ }
+ else if ( v.y == 0 )
+ {
+ return FT_ABS( v.x );
+ }
+
+ /* general case */
+ shift = ft_trig_prenorm( &v );
+ ft_trig_pseudo_polarize( &v );
+
+ v.x = ft_trig_downscale( v.x );
+
+ if ( shift > 0 )
+ return ( v.x + ( 1L << ( shift - 1 ) ) ) >> shift;
+
+ return (FT_Fixed)( (FT_UInt32)v.x << -shift );
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_Polarize( FT_Vector* vec,
+ FT_Fixed *length,
+ FT_Angle *angle )
+ {
+ FT_Int shift;
+ FT_Vector v;
+
+
+ if ( !vec || !length || !angle )
+ return;
+
+ v = *vec;
+
+ if ( v.x == 0 && v.y == 0 )
+ return;
+
+ shift = ft_trig_prenorm( &v );
+ ft_trig_pseudo_polarize( &v );
+
+ v.x = ft_trig_downscale( v.x );
+
+ *length = shift >= 0 ? ( v.x >> shift )
+ : (FT_Fixed)( (FT_UInt32)v.x << -shift );
+ *angle = v.y;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_From_Polar( FT_Vector* vec,
+ FT_Fixed length,
+ FT_Angle angle )
+ {
+ if ( !vec )
+ return;
+
+ vec->x = length;
+ vec->y = 0;
+
+ FT_Vector_Rotate( vec, angle );
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Angle )
+ FT_Angle_Diff( FT_Angle angle1,
+ FT_Angle angle2 )
+ {
+ FT_Angle delta = angle2 - angle1;
+
+
+ while ( delta <= -FT_ANGLE_PI )
+ delta += FT_ANGLE_2PI;
+
+ while ( delta > FT_ANGLE_PI )
+ delta -= FT_ANGLE_2PI;
+
+ return delta;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/fttype1.c b/modules/freetype2/src/base/fttype1.c
new file mode 100644
index 0000000000..637c5cf775
--- /dev/null
+++ b/modules/freetype2/src/base/fttype1.c
@@ -0,0 +1,126 @@
+/****************************************************************************
+ *
+ * fttype1.c
+ *
+ * FreeType utility file for PS names 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 <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftserv.h>
+#include <freetype/internal/services/svpsinfo.h>
+
+
+ /* documentation is in t1tables.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PS_Font_Info( FT_Face face,
+ PS_FontInfoRec* afont_info )
+ {
+ FT_Error error;
+ FT_Service_PsInfo service;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !afont_info )
+ return FT_THROW( Invalid_Argument );
+
+ FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO );
+
+ if ( service && service->ps_get_font_info )
+ error = service->ps_get_font_info( face, afont_info );
+ else
+ error = FT_THROW( Invalid_Argument );
+
+ return error;
+ }
+
+
+ /* documentation is in t1tables.h */
+
+ FT_EXPORT_DEF( FT_Int )
+ FT_Has_PS_Glyph_Names( FT_Face face )
+ {
+ FT_Int result = 0;
+ FT_Service_PsInfo service;
+
+
+ if ( face )
+ {
+ FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO );
+
+ if ( service && service->ps_has_glyph_names )
+ result = service->ps_has_glyph_names( face );
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in t1tables.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PS_Font_Private( FT_Face face,
+ PS_PrivateRec* afont_private )
+ {
+ FT_Error error;
+ FT_Service_PsInfo service;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !afont_private )
+ return FT_THROW( Invalid_Argument );
+
+ FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO );
+
+ if ( service && service->ps_get_font_private )
+ error = service->ps_get_font_private( face, afont_private );
+ else
+ error = FT_THROW( Invalid_Argument );
+
+ return error;
+ }
+
+
+ /* documentation is in t1tables.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_Get_PS_Font_Value( FT_Face face,
+ PS_Dict_Keys key,
+ FT_UInt idx,
+ void *value,
+ FT_Long value_len )
+ {
+ FT_Int result = 0;
+ FT_Service_PsInfo service = NULL;
+
+
+ if ( face )
+ {
+ FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO );
+
+ if ( service && service->ps_get_font_value )
+ result = service->ps_get_font_value( face, key, idx,
+ value, value_len );
+ }
+
+ return result;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftutil.c b/modules/freetype2/src/base/ftutil.c
new file mode 100644
index 0000000000..6120846d2c
--- /dev/null
+++ b/modules/freetype2/src/base/ftutil.c
@@ -0,0 +1,442 @@
+/****************************************************************************
+ *
+ * ftutil.c
+ *
+ * FreeType utility file for memory and list management (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 <freetype/internal/ftmemory.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/ftlist.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 memory
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** *****/
+ /***** M E M O R Y M A N A G E M E N T *****/
+ /***** *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_alloc( FT_Memory memory,
+ FT_Long size,
+ FT_Error *p_error )
+ {
+ FT_Error error;
+ FT_Pointer block = ft_mem_qalloc( memory, size, &error );
+
+ if ( !error && block && size > 0 )
+ FT_MEM_ZERO( block, size );
+
+ *p_error = error;
+ return block;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_qalloc( FT_Memory memory,
+ FT_Long size,
+ FT_Error *p_error )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Pointer block = NULL;
+
+
+ if ( size > 0 )
+ {
+ block = memory->alloc( memory, size );
+ if ( !block )
+ error = FT_THROW( Out_Of_Memory );
+ }
+ else if ( size < 0 )
+ {
+ /* may help catch/prevent security issues */
+ error = FT_THROW( Invalid_Argument );
+ }
+
+ *p_error = error;
+ return block;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_realloc( FT_Memory memory,
+ FT_Long item_size,
+ FT_Long cur_count,
+ FT_Long new_count,
+ void* block,
+ FT_Error *p_error )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ block = ft_mem_qrealloc( memory, item_size,
+ cur_count, new_count, block, &error );
+ if ( !error && block && new_count > cur_count )
+ FT_MEM_ZERO( (char*)block + cur_count * item_size,
+ ( new_count - cur_count ) * item_size );
+
+ *p_error = error;
+ return block;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_qrealloc( FT_Memory memory,
+ FT_Long item_size,
+ FT_Long cur_count,
+ FT_Long new_count,
+ void* block,
+ FT_Error *p_error )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ /* Note that we now accept `item_size == 0' as a valid parameter, in
+ * order to cover very weird cases where an ALLOC_MULT macro would be
+ * called.
+ */
+ if ( cur_count < 0 || new_count < 0 || item_size < 0 )
+ {
+ /* may help catch/prevent nasty security issues */
+ error = FT_THROW( Invalid_Argument );
+ }
+ else if ( new_count == 0 || item_size == 0 )
+ {
+ ft_mem_free( memory, block );
+ block = NULL;
+ }
+ else if ( new_count > FT_INT_MAX / item_size )
+ {
+ error = FT_THROW( Array_Too_Large );
+ }
+ else if ( cur_count == 0 )
+ {
+ FT_ASSERT( !block );
+
+ block = memory->alloc( memory, new_count * item_size );
+ if ( block == NULL )
+ error = FT_THROW( Out_Of_Memory );
+ }
+ else
+ {
+ FT_Pointer block2;
+ FT_Long cur_size = cur_count * item_size;
+ FT_Long new_size = new_count * item_size;
+
+
+ block2 = memory->realloc( memory, cur_size, new_size, block );
+ if ( !block2 )
+ error = FT_THROW( Out_Of_Memory );
+ else
+ block = block2;
+ }
+
+ *p_error = error;
+ return block;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_mem_free( FT_Memory memory,
+ const void *P )
+ {
+ if ( P )
+ memory->free( memory, (void*)P );
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_dup( FT_Memory memory,
+ const void* address,
+ FT_ULong size,
+ FT_Error *p_error )
+ {
+ FT_Error error;
+ FT_Pointer p = ft_mem_qalloc( memory, (FT_Long)size, &error );
+
+
+ if ( !error && address && size > 0 )
+ ft_memcpy( p, address, size );
+
+ *p_error = error;
+ return p;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_strdup( FT_Memory memory,
+ const char* str,
+ FT_Error *p_error )
+ {
+ FT_ULong len = str ? (FT_ULong)ft_strlen( str ) + 1
+ : 0;
+
+
+ return ft_mem_dup( memory, str, len, p_error );
+ }
+
+
+ FT_BASE_DEF( FT_Int )
+ ft_mem_strcpyn( char* dst,
+ const char* src,
+ FT_ULong size )
+ {
+ while ( size > 1 && *src != 0 )
+ {
+ *dst++ = *src++;
+ size--;
+ }
+
+ *dst = 0; /* always zero-terminate */
+
+ return *src != 0;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** *****/
+ /***** D O U B L Y L I N K E D L I S T S *****/
+ /***** *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#undef FT_COMPONENT
+#define FT_COMPONENT list
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( FT_ListNode )
+ FT_List_Find( FT_List list,
+ void* data )
+ {
+ FT_ListNode cur;
+
+
+ if ( !list )
+ return NULL;
+
+ cur = list->head;
+ while ( cur )
+ {
+ if ( cur->data == data )
+ return cur;
+
+ cur = cur->next;
+ }
+
+ return NULL;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Add( FT_List list,
+ FT_ListNode node )
+ {
+ FT_ListNode before;
+
+
+ if ( !list || !node )
+ return;
+
+ before = list->tail;
+
+ node->next = NULL;
+ node->prev = before;
+
+ if ( before )
+ before->next = node;
+ else
+ list->head = node;
+
+ list->tail = node;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Insert( FT_List list,
+ FT_ListNode node )
+ {
+ FT_ListNode after;
+
+
+ if ( !list || !node )
+ return;
+
+ after = list->head;
+
+ node->next = after;
+ node->prev = NULL;
+
+ if ( !after )
+ list->tail = node;
+ else
+ after->prev = node;
+
+ list->head = node;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Remove( FT_List list,
+ FT_ListNode node )
+ {
+ FT_ListNode before, after;
+
+
+ if ( !list || !node )
+ return;
+
+ before = node->prev;
+ after = node->next;
+
+ if ( before )
+ before->next = after;
+ else
+ list->head = after;
+
+ if ( after )
+ after->prev = before;
+ else
+ list->tail = before;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Up( FT_List list,
+ FT_ListNode node )
+ {
+ FT_ListNode before, after;
+
+
+ if ( !list || !node )
+ return;
+
+ before = node->prev;
+ after = node->next;
+
+ /* check whether we are already on top of the list */
+ if ( !before )
+ return;
+
+ before->next = after;
+
+ if ( after )
+ after->prev = before;
+ else
+ list->tail = before;
+
+ node->prev = NULL;
+ node->next = list->head;
+ list->head->prev = node;
+ list->head = node;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_List_Iterate( FT_List list,
+ FT_List_Iterator iterator,
+ void* user )
+ {
+ FT_ListNode cur;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( !list || !iterator )
+ return FT_THROW( Invalid_Argument );
+
+ cur = list->head;
+
+ while ( cur )
+ {
+ FT_ListNode next = cur->next;
+
+
+ error = iterator( cur, user );
+ if ( error )
+ break;
+
+ cur = next;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Finalize( FT_List list,
+ FT_List_Destructor destroy,
+ FT_Memory memory,
+ void* user )
+ {
+ FT_ListNode cur;
+
+
+ if ( !list || !memory )
+ return;
+
+ cur = list->head;
+ while ( cur )
+ {
+ FT_ListNode next = cur->next;
+ void* data = cur->data;
+
+
+ if ( destroy )
+ destroy( memory, data, user );
+
+ FT_FREE( cur );
+ cur = next;
+ }
+
+ list->head = NULL;
+ list->tail = NULL;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/ftver.rc b/modules/freetype2/src/base/ftver.rc
new file mode 100644
index 0000000000..f113cb892b
--- /dev/null
+++ b/modules/freetype2/src/base/ftver.rc
@@ -0,0 +1,61 @@
+/***************************************************************************/
+/* */
+/* ftver.rc */
+/* */
+/* FreeType VERSIONINFO resource for Windows DLLs. */
+/* */
+/* Copyright (C) 2018-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<windows.h>
+
+#define FT_VERSION 2,13,0,0
+#define FT_VERSION_STR "2.13.0"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION FT_VERSION
+PRODUCTVERSION FT_VERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+FILEFLAGS VS_FF_DEBUG
+#endif
+#ifdef DLL_EXPORT
+FILETYPE VFT_DLL
+#define FT_FILENAME "freetype.dll"
+#else
+FILETYPE VFT_STATIC_LIB
+#define FT_FILENAME "freetype.lib"
+#endif
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "The FreeType Project"
+ VALUE "FileDescription", "Font Rendering Library"
+ VALUE "FileVersion", FT_VERSION_STR
+ VALUE "ProductName", "FreeType"
+ VALUE "ProductVersion", FT_VERSION_STR
+ VALUE "LegalCopyright", L"\x00A9 2000-2023 The FreeType Project www.freetype.org. All rights reserved."
+ VALUE "InternalName", "freetype"
+ VALUE "OriginalFilename", FT_FILENAME
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ /* The following line should only be modified for localized versions. */
+ /* It consists of any number of WORD,WORD pairs, with each pair */
+ /* describing a "language,codepage" combination supported by the file. */
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/modules/freetype2/src/base/ftwinfnt.c b/modules/freetype2/src/base/ftwinfnt.c
new file mode 100644
index 0000000000..03b023e079
--- /dev/null
+++ b/modules/freetype2/src/base/ftwinfnt.c
@@ -0,0 +1,52 @@
+/****************************************************************************
+ *
+ * ftwinfnt.c
+ *
+ * FreeType API for accessing Windows FNT specific info (body).
+ *
+ * Copyright (C) 2003-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/ftwinfnt.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/services/svwinfnt.h>
+
+
+ /* documentation is in ftwinfnt.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_WinFNT_Header( FT_Face face,
+ FT_WinFNT_HeaderRec *header )
+ {
+ FT_Service_WinFnt service;
+ FT_Error error;
+
+
+ if ( !face )
+ return FT_THROW( Invalid_Face_Handle );
+
+ if ( !header )
+ return FT_THROW( Invalid_Argument );
+
+ FT_FACE_LOOKUP_SERVICE( face, service, WINFNT );
+
+ if ( service )
+ error = service->get_header( face, header );
+ else
+ error = FT_THROW( Invalid_Argument );
+
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/base/md5.c b/modules/freetype2/src/base/md5.c
new file mode 100644
index 0000000000..b235e17a56
--- /dev/null
+++ b/modules/freetype2/src/base/md5.c
@@ -0,0 +1,291 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's. No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible. Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ */
+
+#ifndef HAVE_OPENSSL
+
+#include <string.h>
+
+#include "md5.h"
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) (((x) ^ (y)) ^ (z))
+#define H2(x, y, z) ((x) ^ ((y) ^ (z)))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them in a
+ * properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned memory
+ * accesses is just an optimization. Nothing will break if it fails to detect
+ * a suitable architecture.
+ *
+ * Unfortunately, this optimization may be a C strict aliasing rules violation
+ * if the caller's data buffer has effective type that cannot be aliased by
+ * MD5_u32plus. In practice, this problem may occur if these MD5 routines are
+ * inlined into a calling function, or with future and dangerously advanced
+ * link-time optimizations. For the time being, keeping these MD5 routines in
+ * their own translation unit avoids the problem.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+#define SET(n) \
+ (*(MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+#else
+#define SET(n) \
+ (ctx->block[(n)] = \
+ (MD5_u32plus)ptr[(n) * 4] | \
+ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+ (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update the bit
+ * counters. There are no alignment requirements.
+ */
+static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
+{
+ const unsigned char *ptr;
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ ptr = (const unsigned char *)data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+/* Round 1 */
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+void MD5_Init(MD5_CTX *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
+{
+ MD5_u32plus saved_lo;
+ unsigned long used, available;
+
+ saved_lo = ctx->lo;
+ if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+ ctx->hi++;
+ ctx->hi += size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if (used) {
+ available = 64 - used;
+
+ if (size < available) {
+ memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ memcpy(&ctx->buffer[used], data, available);
+ data = (const unsigned char *)data + available;
+ size -= available;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = body(ctx, data, size & ~(unsigned long)0x3f);
+ size &= 0x3f;
+ }
+
+ memcpy(ctx->buffer, data, size);
+}
+
+#define OUT(dst, src) \
+ (dst)[0] = (unsigned char)(src); \
+ (dst)[1] = (unsigned char)((src) >> 8); \
+ (dst)[2] = (unsigned char)((src) >> 16); \
+ (dst)[3] = (unsigned char)((src) >> 24);
+
+void MD5_Final(unsigned char *result, MD5_CTX *ctx)
+{
+ unsigned long used, available;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ available = 64 - used;
+
+ if (available < 8) {
+ memset(&ctx->buffer[used], 0, available);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ available = 64;
+ }
+
+ memset(&ctx->buffer[used], 0, available - 8);
+
+ ctx->lo <<= 3;
+ OUT(&ctx->buffer[56], ctx->lo)
+ OUT(&ctx->buffer[60], ctx->hi)
+
+ body(ctx, ctx->buffer, 64);
+
+ OUT(&result[0], ctx->a)
+ OUT(&result[4], ctx->b)
+ OUT(&result[8], ctx->c)
+ OUT(&result[12], ctx->d)
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#endif
diff --git a/modules/freetype2/src/base/md5.h b/modules/freetype2/src/base/md5.h
new file mode 100644
index 0000000000..2da44bf355
--- /dev/null
+++ b/modules/freetype2/src/base/md5.h
@@ -0,0 +1,45 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See md5.c for more information.
+ */
+
+#ifdef HAVE_OPENSSL
+#include <openssl/md5.h>
+#elif !defined(_MD5_H)
+#define _MD5_H
+
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef unsigned int MD5_u32plus;
+
+typedef struct {
+ MD5_u32plus lo, hi;
+ MD5_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+} MD5_CTX;
+
+extern void MD5_Init(MD5_CTX *ctx);
+extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
+extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
+
+#endif
diff --git a/modules/freetype2/src/base/rules.mk b/modules/freetype2/src/base/rules.mk
new file mode 100644
index 0000000000..b7de9b5ca9
--- /dev/null
+++ b/modules/freetype2/src/base/rules.mk
@@ -0,0 +1,108 @@
+#
+# FreeType 2 base layer 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.
+
+
+# It sets the following variables which are used by the master Makefile
+# after the call:
+#
+# BASE_OBJ_S: The single-object base layer.
+# BASE_OBJ_M: A list of all objects for a multiple-objects build.
+# BASE_EXT_OBJ: A list of base layer extensions, i.e., components found
+# in `src/base' which are not compiled within the base
+# layer proper.
+
+
+BASE_COMPILE := $(CC) $(ANSIFLAGS) \
+ $I$(subst /,$(COMPILER_SEP),$(BASE_DIR)) \
+ $(INCLUDE_FLAGS) \
+ $(FT_CFLAGS)
+
+
+# Base layer sources
+#
+# ftsystem, ftinit, and ftdebug are handled by freetype.mk
+#
+# All files listed here should be included in `ftbase.c' (for a `single'
+# build).
+#
+BASE_SRC := $(BASE_DIR)/ftadvanc.c \
+ $(BASE_DIR)/ftcalc.c \
+ $(BASE_DIR)/ftcolor.c \
+ $(BASE_DIR)/ftdbgmem.c \
+ $(BASE_DIR)/fterrors.c \
+ $(BASE_DIR)/ftfntfmt.c \
+ $(BASE_DIR)/ftgloadr.c \
+ $(BASE_DIR)/fthash.c \
+ $(BASE_DIR)/ftlcdfil.c \
+ $(BASE_DIR)/ftobjs.c \
+ $(BASE_DIR)/ftoutln.c \
+ $(BASE_DIR)/ftpsprop.c \
+ $(BASE_DIR)/ftrfork.c \
+ $(BASE_DIR)/ftsnames.c \
+ $(BASE_DIR)/ftstream.c \
+ $(BASE_DIR)/fttrigon.c \
+ $(BASE_DIR)/ftutil.c
+
+
+ifneq ($(ftmac_c),)
+ BASE_SRC += $(BASE_DIR)/$(ftmac_c)
+endif
+
+# for simplicity, we also handle `md5.c' (which gets included by `ftobjs.h')
+BASE_H := $(BASE_DIR)/ftbase.h \
+ $(BASE_DIR)/md5.c \
+ $(BASE_DIR)/md5.h
+
+# Base layer `extensions' sources
+#
+# An extension is added to the library file as a separate object. It is
+# then linked to the final executable only if one of its symbols is used by
+# the application.
+#
+BASE_EXT_SRC := $(patsubst %,$(BASE_DIR)/%,$(BASE_EXTENSIONS))
+
+# Default extensions objects
+#
+BASE_EXT_OBJ := $(BASE_EXT_SRC:$(BASE_DIR)/%.c=$(OBJ_DIR)/%.$O)
+
+
+# Base layer object(s)
+#
+# BASE_OBJ_M is used during `multi' builds (each base source file compiles
+# to a single object file).
+#
+# BASE_OBJ_S is used during `single' builds (the whole base layer is
+# compiled as a single object file using ftbase.c).
+#
+BASE_OBJ_M := $(BASE_SRC:$(BASE_DIR)/%.c=$(OBJ_DIR)/%.$O)
+BASE_OBJ_S := $(OBJ_DIR)/ftbase.$O
+
+# Base layer root source file for single build
+#
+BASE_SRC_S := $(BASE_DIR)/ftbase.c
+
+
+# Base layer - single object build
+#
+$(BASE_OBJ_S): $(BASE_SRC_S) $(BASE_SRC) $(FREETYPE_H) $(BASE_H)
+ $(BASE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(BASE_SRC_S))
+
+
+# Multiple objects build + extensions
+#
+$(OBJ_DIR)/%.$O: $(BASE_DIR)/%.c $(FREETYPE_H) $(BASE_H)
+ $(BASE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# EOF