summaryrefslogtreecommitdiff
path: root/gs/base/fapi_ft.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/fapi_ft.c')
-rw-r--r--gs/base/fapi_ft.c891
1 files changed, 891 insertions, 0 deletions
diff --git a/gs/base/fapi_ft.c b/gs/base/fapi_ft.c
new file mode 100644
index 000000000..8af24478e
--- /dev/null
+++ b/gs/base/fapi_ft.c
@@ -0,0 +1,891 @@
+/* Copyright (C) 2001-2006 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+/* $Id$ */
+
+/*
+GhostScript Font API plug-in that allows fonts to be rendered by FreeType.
+Started by Graham Asher, 6th June 2002.
+*/
+
+/* GhostScript headers. */
+#include "stdio_.h"
+#include "malloc_.h"
+#include "ierrors.h"
+#include "ifapi.h"
+#include "write_t1.h"
+#include "write_t2.h"
+#include "math_.h"
+#include "gserror.h"
+
+/* FreeType headers */
+#include "freetype/freetype.h"
+#include "freetype/ftincrem.h"
+#include "freetype/ftglyph.h"
+#include "freetype/ftoutln.h"
+#include "freetype/fttrigon.h"
+
+/* Note: structure definitions here start with FF_, which stands for 'FAPI FreeType". */
+
+typedef struct FF_server_
+ {
+ FAPI_server m_fapi_server;
+ FT_Library m_freetype_library;
+ FT_OutlineGlyph m_outline_glyph;
+ FT_BitmapGlyph m_bitmap_glyph;
+ } FF_server;
+
+typedef struct FF_face_
+ {
+ FT_Face m_ft_face; /* The FreeType typeface object. */
+ FT_Incremental_InterfaceRec* m_ft_inc_int; /* If non-null, the incremental interface object passed to FreeType. */
+ unsigned char* m_font_data; /* Non-null if font data is owned by this object. */
+ } FF_face;
+
+/*
+This structure has to have the tag FT_IncrementalRec to be compatible with
+the functions defined in FT_Incremental_FuncsRec.
+*/
+typedef struct FT_IncrementalRec_
+ {
+ FAPI_font* m_fapi_font; /* The font. */
+ unsigned char* m_glyph_data; /* A one-shot buffer for glyph data. */
+ /* If it is already in use glyph data is allocated on the heap. */
+ size_t m_glyph_data_length; /* Length in bytes of m_glyph_data. */
+ bool m_glyph_data_in_use; /* True if m_glyph_data is already in use. */
+ FT_Incremental_MetricsRec m_glyph_metrics; /* Incremental glyph metrics supplied by GhostScript. */
+ unsigned long m_glyph_metrics_index; /* m_glyph_metrics contains data for this glyph index unless it is 0xFFFFFFFF. */
+ FAPI_metrics_type m_metrics_type; /* The metrics type determines whether metrics are replaced, added, etc. */
+ } FT_IncrementalRec;
+
+static FF_face* new_face(FT_Face a_ft_face,FT_Incremental_InterfaceRec* a_ft_inc_int,
+ unsigned char* a_font_data)
+ {
+ FF_face* face = (FF_face *)malloc(sizeof(FF_face));
+ if (face)
+ {
+ face->m_ft_face = a_ft_face;
+ face->m_ft_inc_int = a_ft_inc_int;
+ face->m_font_data = a_font_data;
+ }
+ return face;
+ }
+
+static void delete_face(FF_face* a_face)
+ {
+ if (a_face)
+ {
+ FT_Done_Face(a_face->m_ft_face);
+ free(a_face->m_ft_inc_int);
+ free(a_face->m_font_data);
+ free(a_face);
+ }
+ }
+
+static FT_IncrementalRec* new_inc_int_info(FAPI_font* a_fapi_font)
+ {
+ FT_IncrementalRec* info = (FT_IncrementalRec*)malloc(sizeof(FT_IncrementalRec));
+ if (info)
+ {
+ info->m_fapi_font = a_fapi_font;
+ info->m_glyph_data = NULL;
+ info->m_glyph_data_length = 0;
+ info->m_glyph_data_in_use = false;
+ info->m_glyph_metrics_index = 0xFFFFFFFF;
+ info->m_metrics_type = FAPI_METRICS_NOTDEF;
+ }
+ return info;
+ }
+
+static void delete_inc_int_info(FT_IncrementalRec* a_inc_int_info)
+ {
+ if (a_inc_int_info)
+ {
+ free(a_inc_int_info->m_glyph_data);
+ free(a_inc_int_info);
+ }
+ }
+
+static FT_Error get_fapi_glyph_data(FT_Incremental a_info,FT_UInt a_index,FT_Data* a_data)
+ {
+ FAPI_font* ff = a_info->m_fapi_font;
+ ushort length = 0;
+
+ /* Tell the FAPI interface that we need to decrypt the glyph data. */
+ ff->need_decrypt = true;
+
+ /* If m_glyph_data is already in use (as will happen for composite glyphs) create a new buffer on the heap. */
+ if (a_info->m_glyph_data_in_use)
+ {
+ unsigned char* buffer = NULL;
+ length = ff->get_glyph(ff,a_index,NULL,0);
+ buffer = malloc(length);
+ if (!buffer)
+ return FT_Err_Out_Of_Memory;
+ ff->get_glyph(ff,a_index,buffer,length);
+ a_data->pointer = buffer;
+ }
+ else
+ {
+ /*
+ Save ff->char_data, which is set to null by FAPI_FF_get_glyph as part of a hack to
+ make the deprecated Type 2 endchar ('seac') work, so that it can be restored
+ if we need to try again with a longer buffer.
+ */
+ const void* saved_char_data = ff->char_data;
+
+ /* Get as much of the glyph data as possible into the buffer */
+ length = ff->get_glyph(ff,a_index,a_info->m_glyph_data,(ushort)a_info->m_glyph_data_length);
+
+ /* If the buffer was too small enlarge it and try again. */
+ if (length > a_info->m_glyph_data_length)
+ {
+ a_info->m_glyph_data = realloc(a_info->m_glyph_data,length);
+ if (!a_info->m_glyph_data)
+ {
+ a_info->m_glyph_data_length = 0;
+ return FT_Err_Out_Of_Memory;
+ }
+ a_info->m_glyph_data_length = length;
+ ff->char_data = saved_char_data;
+ ff->get_glyph(ff,a_index,a_info->m_glyph_data,length);
+ }
+
+ /* Set the returned pointer and length. */
+ a_data->pointer = a_info->m_glyph_data;
+
+ a_info->m_glyph_data_in_use = true;
+ }
+
+ a_data->length = length;
+ return 0;
+ }
+
+static void free_fapi_glyph_data(FT_Incremental a_info,FT_Data* a_data)
+ {
+ if (a_data->pointer == (const FT_Byte*)a_info->m_glyph_data)
+ a_info->m_glyph_data_in_use = false;
+ else
+ free((FT_Byte*)a_data->pointer);
+ }
+
+static FT_Error get_fapi_glyph_metrics(FT_Incremental a_info,FT_UInt a_glyph_index,
+ FT_Bool bVertical, FT_Incremental_MetricsRec* a_metrics)
+ {
+ /* fixme : bVertical is not implemented. */
+ if (a_info->m_glyph_metrics_index == a_glyph_index)
+ {
+ switch (a_info->m_metrics_type)
+ {
+ case FAPI_METRICS_ADD:
+ a_metrics->advance += a_info->m_glyph_metrics.advance;
+ break;
+ case FAPI_METRICS_REPLACE_WIDTH:
+ a_metrics->advance = a_info->m_glyph_metrics.advance;
+ break;
+ case FAPI_METRICS_REPLACE:
+ *a_metrics = a_info->m_glyph_metrics;
+ break;
+ default:
+ /* This can't happen. */
+ return FT_Err_Invalid_Argument;
+ }
+ }
+ return 0;
+ }
+
+static const FT_Incremental_FuncsRec TheFAPIIncrementalInterfaceFuncs =
+ {
+ get_fapi_glyph_data,
+ free_fapi_glyph_data,
+ get_fapi_glyph_metrics
+ };
+
+static FT_Incremental_InterfaceRec* new_inc_int(FAPI_font* a_fapi_font)
+ {
+ FT_Incremental_InterfaceRec* i = (FT_Incremental_InterfaceRec*)malloc(sizeof(FT_Incremental_InterfaceRec));
+ if (i)
+ {
+ i->object = (FT_Incremental)new_inc_int_info(a_fapi_font);
+ i->funcs = &TheFAPIIncrementalInterfaceFuncs;
+ }
+ if (!i->object)
+ {
+ free(i);
+ i = NULL;
+ }
+ return i;
+ }
+
+static void delete_inc_int(FT_Incremental_InterfaceRec* a_inc_int)
+ {
+ if (a_inc_int)
+ {
+ delete_inc_int_info(a_inc_int->object);
+ free(a_inc_int);
+ }
+ }
+
+/*
+Convert FreeType error codes to GhostScript ones.
+Very rudimentary because most don't correspond.
+*/
+static int ft_to_gs_error(FT_Error a_error)
+ {
+ if (a_error)
+ {
+ if (a_error == FT_Err_Out_Of_Memory)
+ return e_VMerror;
+ else
+ return e_unknownerror;
+ }
+ return 0;
+ }
+
+/*
+Load a glyph and optionally rasterize it. Return its metrics in a_metrics.
+If a_bitmap is true convert the glyph to a bitmap.
+*/
+static FAPI_retcode load_glyph(FAPI_font* a_fapi_font,const FAPI_char_ref *a_char_ref,
+ FAPI_metrics* a_metrics,FT_Glyph* a_glyph,bool a_bitmap)
+ {
+ FT_Error ft_error = 0;
+ FF_face* face = (FF_face*)a_fapi_font->server_font_data;
+ FT_Face ft_face = face->m_ft_face;
+ int index = a_char_ref->char_code;
+
+ /*
+ Save a_fapi_font->char_data, which is set to null by FAPI_FF_get_glyph as part of a hack to
+ make the deprecated Type 2 endchar ('seac') work, so that it can be restored
+ after the first call to FT_Load_Glyph.
+ */
+ const void* saved_char_data = a_fapi_font->char_data;
+
+ if (!a_char_ref->is_glyph_index)
+ {
+ if (ft_face->num_charmaps)
+ index = FT_Get_Char_Index(ft_face,index);
+ else
+ /*
+ If there are no character maps and no glyph index, loading the glyph will still work
+ properly if both glyph data and metrics are supplied by the incremental interface.
+ In that case we use a dummy glyph index which will be passed
+ back to FAPI_FF_get_glyph by get_fapi_glyph_data.
+
+ */
+ {
+ /*
+ Type 1 fonts don't use the code and can appear to FreeType to have only one glyph,
+ so we have to set the index to 0.
+ */
+ if (a_fapi_font->is_type1)
+ index = 0;
+ /*
+ For other font types, FAPI_FF_get_glyph requires the character code when getting
+ data.
+ */
+ else
+ index = a_char_ref->char_code;
+ }
+ }
+
+ /* Refresh the pointer to the FAPI_font held by the incremental interface. */
+ if (face->m_ft_inc_int)
+ face->m_ft_inc_int->object->m_fapi_font = a_fapi_font;
+
+ /* Store the overriding metrics if they have been supplied. */
+ if (face->m_ft_inc_int && a_char_ref->metrics_type != FAPI_METRICS_NOTDEF)
+ {
+ FT_Incremental_MetricsRec* m = &face->m_ft_inc_int->object->m_glyph_metrics;
+ m->bearing_x = a_char_ref->sb_x >> 16;
+ m->bearing_y = a_char_ref->sb_y >> 16;
+ m->advance = a_char_ref->aw_x >> 16;
+ face->m_ft_inc_int->object->m_glyph_metrics_index = index;
+ face->m_ft_inc_int->object->m_metrics_type = a_char_ref->metrics_type;
+ }
+
+ ft_error = FT_Load_Glyph(ft_face,index,FT_LOAD_MONOCHROME | FT_LOAD_NO_SCALE);
+ if (!ft_error && a_metrics)
+ {
+ a_metrics->bbox_x0 = ft_face->glyph->metrics.horiBearingX;
+ a_metrics->bbox_y0 = ft_face->glyph->metrics.horiBearingY - ft_face->glyph->metrics.height;
+ a_metrics->bbox_x1 = a_metrics->bbox_x0 + ft_face->glyph->metrics.width;
+ a_metrics->bbox_y1 = a_metrics->bbox_y0 + ft_face->glyph->metrics.height;
+ a_metrics->escapement = ft_face->glyph->metrics.horiAdvance;
+ a_metrics->em_x = ft_face->units_per_EM;
+ a_metrics->em_y = ft_face->units_per_EM;
+ }
+
+ /* We have to load the glyph again, scale it correctly, and render it if we need a bitmap. */
+ if (!ft_error)
+ {
+ a_fapi_font->char_data = saved_char_data;
+ ft_error = FT_Load_Glyph(ft_face,index,a_bitmap ? FT_LOAD_MONOCHROME | FT_LOAD_RENDER: FT_LOAD_MONOCHROME);
+ }
+ if (!ft_error && a_glyph)
+ ft_error = FT_Get_Glyph(ft_face->glyph,a_glyph);
+ return ft_to_gs_error(ft_error);
+ }
+
+/**
+Ensure that the rasterizer is open.
+
+In the case of FreeType this means creating the FreeType library object.
+*/
+static FAPI_retcode ensure_open(FAPI_server* a_server, const byte *server_param, int server_param_size)
+ {
+ FF_server* s = (FF_server*)a_server;
+ if (!s->m_freetype_library)
+ {
+ FT_Error ft_error = FT_Init_FreeType(&s->m_freetype_library);
+ if (ft_error)
+ return ft_to_gs_error(ft_error);
+ }
+ return 0;
+ }
+
+static void transform_concat(FT_Matrix* a_A,const FT_Matrix* a_B)
+ {
+ FT_Matrix result = *a_B;
+ FT_Matrix_Multiply(a_A,&result);
+ *a_A = result;
+ }
+
+/** Create a transform representing an angle defined as a vector. */
+static void make_rotation(FT_Matrix* a_transform,const FT_Vector* a_vector)
+ {
+ FT_Fixed length, cos, sin;
+ if (a_vector->x >= 0 && a_vector->y == 0)
+ {
+ a_transform->xx = a_transform->yy = 65536;
+ a_transform->xy = a_transform->yx = 0;
+ return;
+ }
+
+ length = FT_Vector_Length((FT_Vector*)a_vector);
+ cos = FT_DivFix(a_vector->x,length);
+ sin = FT_DivFix(a_vector->y,length);
+ a_transform->xx = a_transform->yy = cos;
+ a_transform->xy = -sin;
+ a_transform->yx = sin;
+ }
+
+/**
+Divide a transformation into a scaling part and a rotation-and-shear part.
+The scaling part is used for setting the pixel size for hinting.
+*/
+static void transform_decompose(FT_Matrix* a_transform,
+ FT_Fixed* a_x_scale,FT_Fixed* a_y_scale)
+ {
+ FT_Matrix rotation;
+ bool have_rotation = false;
+ FT_Vector v;
+
+ /*
+ Set v to the result of applying the matrix to the (1,0) vector
+ and reverse the direction of rotation by negating the y coordinate.
+ */
+ v.x = a_transform->xx;
+ v.y = -a_transform->yx;
+ if (v.y || v.x < 0)
+ {
+ have_rotation = true;
+
+ /* Get the inverse of the rotation. */
+ make_rotation(&rotation,&v);
+
+ /* Remove the rotation. */
+ transform_concat(a_transform,&rotation);
+ }
+
+ /* Separate the scales from the transform. */
+ *a_x_scale = a_transform->xx;
+ if (*a_x_scale < 0)
+ {
+ *a_x_scale = -*a_x_scale;
+ a_transform->xx = -65536;
+ }
+ else
+ a_transform->xx = 65536;
+ *a_y_scale = a_transform->yy;
+ if (*a_y_scale < 0)
+ {
+ *a_y_scale = -*a_y_scale;
+ a_transform->yy = -65536;
+ }
+ else
+ a_transform->yy = 65536;
+ a_transform->yx = FT_DivFix(a_transform->yx,*a_x_scale);
+ a_transform->xy = FT_DivFix(a_transform->xy,*a_y_scale);
+
+ if (have_rotation)
+ {
+ /* Add back the rotation. */
+ rotation.xy = -rotation.xy;
+ rotation.yx = -rotation.yx;
+ transform_concat(a_transform,&rotation);
+ }
+ }
+
+/**
+Open a font and set its size.
+*/
+static FAPI_retcode get_scaled_font(FAPI_server* a_server,FAPI_font* a_font,
+ const FAPI_font_scale* a_font_scale,
+ const char* a_map,
+ FAPI_descendant_code a_descendant_code)
+ {
+ FF_server* s = (FF_server*)a_server;
+ FF_face* face = (FF_face*)a_font->server_font_data;
+ FT_Error ft_error = 0;
+
+ /* dpf("get_scaled_font enter: is_type1=%d is_cid=%d font_file_path='%s' a_descendant_code=%d\n",
+ a_font->is_type1,a_font->is_cid,a_font->font_file_path ? a_font->font_file_path : "",a_descendant_code); */
+
+ /*
+ If this font is the top level font of an embedded CID type 0 font (font type 9)
+ do nothing. See the comment in FAPI_prepare_font. The descendant fonts are
+ passed in individually.
+ */
+ if (a_font->is_cid && a_font->is_type1 && a_font->font_file_path == NULL &&
+ (a_descendant_code == FAPI_TOPLEVEL_BEGIN ||
+ a_descendant_code == FAPI_TOPLEVEL_COMPLETE))
+ {
+ /* dpf("get_scaled_font return 0\n"); */
+ return 0;
+ }
+
+ /* Create the face if it doesn't already exist. */
+ if (!face)
+ {
+ FT_Face ft_face = NULL;
+ FT_Parameter ft_param;
+ FT_Incremental_InterfaceRec* ft_inc_int = NULL;
+ unsigned char* own_font_data = NULL;
+
+ /* dpf("get_scaled_font creating face\n"); */
+
+ /* Load a typeface from a file. */
+ if (a_font->font_file_path)
+ {
+ ft_error = FT_New_Face(s->m_freetype_library,a_font->font_file_path,a_font->subfont,&ft_face);
+ if (!ft_error && ft_face)
+ ft_error = FT_Select_Charmap(ft_face,ft_encoding_unicode);
+ }
+
+ /* Load a typeface from a representation in GhostScript's memory. */
+ else
+ {
+ FT_Open_Args open_args;
+ open_args.flags = FT_OPEN_MEMORY;
+
+ if (a_font->is_type1)
+ {
+ long length;
+ int type = a_font->get_word(a_font,FAPI_FONT_FEATURE_FontType,0);
+
+ /* Tell the FAPI interface that we need to decrypt the /Subrs data. */
+ a_font->need_decrypt = true;
+
+ /*
+ Serialise a type 1 font in PostScript source form, or
+ a Type 2 font in binary form, so that FreeType can read it.
+ */
+ if (type == 1)
+ length = FF_serialize_type1_font(a_font,NULL,0);
+ else
+ length = FF_serialize_type2_font(a_font,NULL,0);
+ open_args.memory_base = own_font_data = malloc(length);
+ if (!open_args.memory_base)
+ return e_VMerror;
+ if (type == 1)
+ open_args.memory_size = FF_serialize_type1_font(a_font,own_font_data,length);
+ else
+ open_args.memory_size = FF_serialize_type2_font(a_font,own_font_data,length);
+ if (open_args.memory_size != length)
+ return_error(e_unregistered); /* Must not happen. */
+ ft_inc_int = new_inc_int(a_font);
+ if (!ft_inc_int)
+ {
+ free(own_font_data);
+ return e_VMerror;
+ }
+ }
+
+ /* It must be type 42 (see code in FAPI_FF_get_glyph in zfapi.c). */
+ else
+ {
+ /* Get the length of the TrueType data. */
+ open_args.memory_size = a_font->get_long(a_font,FAPI_FONT_FEATURE_TT_size,0);
+ if (open_args.memory_size == 0)
+ return e_invalidfont;
+
+ /* Load the TrueType data into a single buffer. */
+ open_args.memory_base = own_font_data = malloc(open_args.memory_size);
+ if (!own_font_data)
+ return e_VMerror;
+ if (a_font->serialize_tt_font(a_font,own_font_data,open_args.memory_size))
+ return e_invalidfont;
+
+ /* We always load incrementally. */
+ ft_inc_int = new_inc_int(a_font);
+ if (!ft_inc_int)
+ {
+ free(own_font_data);
+ return e_VMerror;
+ }
+ }
+
+ if (ft_inc_int)
+ {
+ open_args.flags = (FT_UInt)(open_args.flags | FT_OPEN_PARAMS);
+ ft_param.tag = FT_PARAM_TAG_INCREMENTAL;
+ ft_param.data = ft_inc_int;
+ open_args.num_params = 1;
+ open_args.params = &ft_param;
+ }
+ ft_error = FT_Open_Face(s->m_freetype_library,&open_args,a_font->subfont,&ft_face);
+ }
+
+ if (ft_face)
+ {
+ face = new_face(ft_face,ft_inc_int,own_font_data);
+ if (!face)
+ {
+ free(own_font_data);
+ FT_Done_Face(ft_face);
+ delete_inc_int(ft_inc_int);
+ return e_VMerror;
+ }
+ a_font->server_font_data = face;
+ }
+ else
+ a_font->server_font_data = NULL;
+ }
+
+ /*
+ Set the point size and transformation.
+ The matrix is scaled by the shift specified in the server, 16,
+ so we divide by 65536 when converting to a gs_matrix.
+ */
+ if (face)
+ {
+ static const FT_Matrix ft_reflection = { 65536, 0, 0, -65536 };
+ FT_Matrix ft_transform;
+ FT_F26Dot6 width, height;
+
+ /*
+ Convert the GS transform into an FT transform.
+ Ignore the translation elements because they contain very large values
+ derived from the current transformation matrix and so are of no use.
+ */
+ ft_transform.xx = a_font_scale->matrix[0];
+ ft_transform.xy = a_font_scale->matrix[1];
+ ft_transform.yx = -a_font_scale->matrix[2];
+ ft_transform.yy = -a_font_scale->matrix[3];
+
+ /*
+ Split the transform into scale factors and a rotation-and-shear
+ transform.
+ */
+ transform_decompose(&ft_transform,&width,&height);
+
+ /* Convert width and height to 64ths of pixels and set the FreeType sizes. */
+ width >>= 10;
+ height >>= 10;
+ ft_error = FT_Set_Char_Size(face->m_ft_face,width,height,
+ a_font_scale->HWResolution[0] >> 16,
+ a_font_scale->HWResolution[1] >> 16);
+ if (ft_error)
+ {
+ delete_face(face);
+ a_font->server_font_data = NULL;
+ return ft_to_gs_error(ft_error);
+ }
+
+ /*
+ Concatenate the transform to a reflection around (y=0) so that it
+ produces a glyph that is upside down in FreeType terms, with its
+ first row at the bottom. That is what GhostScript needs.
+ */
+ FT_Matrix_Multiply(&ft_reflection,&ft_transform);
+
+ FT_Set_Transform(face->m_ft_face,&ft_transform,NULL);
+ }
+
+ /* dpf("get_scaled_font return %d\n",a_font->server_font_data ? 0 : -1); */
+ return a_font->server_font_data ? 0 : -1;
+ }
+
+/**
+Return the name of a resource which maps names to character codes. Do this by setting a_decoding_id
+to point to a null-terminated string. The resource is in the 'decoding' directory in the directory named by
+/GenericResourceDir in \lib\gs_res.ps.
+*/
+static FAPI_retcode get_decodingID(FAPI_server* a_server,FAPI_font* a_font,const char** a_decoding_id)
+ {
+ *a_decoding_id = "Unicode";
+ return 0;
+ }
+
+/**
+Get the font bounding box in font units.
+*/
+static FAPI_retcode get_font_bbox(FAPI_server* a_server,FAPI_font* a_font,int a_box[4])
+ {
+ FF_face* face = (FF_face*)a_font->server_font_data;
+ a_box[0] = face->m_ft_face->bbox.xMin;
+ a_box[1] = face->m_ft_face->bbox.yMin;
+ a_box[2] = face->m_ft_face->bbox.xMax;
+ a_box[3] = face->m_ft_face->bbox.yMax;
+ return 0;
+ }
+
+/**
+Return a boolean value in a_proportional stating whether the font is proportional
+or fixed-width.
+*/
+static FAPI_retcode get_font_proportional_feature(FAPI_server* a_server,FAPI_font* a_font,bool* a_proportional)
+ {
+ *a_proportional = true;
+ return 0;
+ }
+
+/**
+Convert the character name in a_char_ref.char_name to a character code or glyph index and put it in a_char_ref.char_code,
+setting a_char_ref.is_glyph_index as appropriate. If this is possible set a_result to true, otherwise set it to false.
+The return value is a standard error return code.
+*/
+static FAPI_retcode can_retrieve_char_by_name(FAPI_server* a_server,FAPI_font* a_font,FAPI_char_ref* a_char_ref,
+ bool* a_result)
+ {
+ FF_face* face = (FF_face*)a_font->server_font_data;
+ char name[128];
+ if (FT_HAS_GLYPH_NAMES(face->m_ft_face) && a_char_ref->char_name_length < sizeof(name))
+ {
+ memcpy(name,a_char_ref->char_name,a_char_ref->char_name_length);
+ name[a_char_ref->char_name_length] = 0;
+ a_char_ref->char_code = FT_Get_Name_Index(face->m_ft_face,name);
+ *a_result = a_char_ref->char_code != 0;
+ if (*a_result)
+ a_char_ref->is_glyph_index = true;
+ }
+ else
+ *a_result = false;
+ return 0;
+ }
+
+/**
+Return non-zero if the metrics can be replaced.
+*/
+static FAPI_retcode can_replace_metrics(FAPI_server *a_server,FAPI_font *a_font,FAPI_char_ref *a_char_ref,int *a_result)
+ {
+ /* Replace metrics only if the metrics are supplied in font units. */
+ *a_result = a_char_ref->metrics_scale == 0;
+ return 0;
+ }
+
+/**
+Retrieve the metrics of a_char_ref and put them in a_metrics.
+*/
+static FAPI_retcode get_char_width(FAPI_server *a_server,FAPI_font *a_font,FAPI_char_ref *a_char_ref,
+ FAPI_metrics *a_metrics)
+ {
+ return load_glyph(a_font,a_char_ref,a_metrics,NULL,false);
+ }
+
+/**
+Rasterize the character a_char and return its metrics. Do not return the bitmap but store this. It can be retrieved by
+a subsequent call to get_char_raster.
+*/
+static FAPI_retcode get_char_raster_metrics(FAPI_server* a_server,FAPI_font* a_font,FAPI_char_ref* a_char_ref,
+ FAPI_metrics* a_metrics)
+ {
+ FF_server* s = (FF_server*)a_server;
+ FAPI_retcode error = load_glyph(a_font,a_char_ref,a_metrics,(FT_Glyph*)&s->m_bitmap_glyph,true);
+ return error;
+ }
+
+/**
+Return the bitmap created by the last call to get_char_raster_metrics.
+*/
+static FAPI_retcode get_char_raster(FAPI_server *a_server,FAPI_raster *a_raster)
+ {
+ FF_server* s = (FF_server*)a_server;
+ if (!s->m_bitmap_glyph)
+ return_error(e_unregistered); /* Must not happen. */
+ a_raster->p = s->m_bitmap_glyph->bitmap.buffer;
+ a_raster->width = s->m_bitmap_glyph->bitmap.width;
+ a_raster->height = s->m_bitmap_glyph->bitmap.rows;
+ a_raster->line_step = s->m_bitmap_glyph->bitmap.pitch;
+ a_raster->orig_x = s->m_bitmap_glyph->left * 16;
+ a_raster->orig_y = s->m_bitmap_glyph->top * 16;
+ return 0;
+ }
+
+/**
+Create an outline for the character a_char and return its metrics. Do not return the outline but store this.
+It can be retrieved by a subsequent call to get_char_outline.
+*/
+static FAPI_retcode get_char_outline_metrics(FAPI_server *a_server,FAPI_font *a_font,FAPI_char_ref *a_char_ref,
+ FAPI_metrics *a_metrics)
+ {
+ FF_server* s = (FF_server*)a_server;
+ return load_glyph(a_font,a_char_ref,a_metrics,(FT_Glyph*)&s->m_outline_glyph,false);
+ }
+
+typedef struct FF_path_info_
+ {
+ FAPI_path* m_path;
+ FracInt m_x;
+ FracInt m_y;
+ } FF_path_info;
+
+static int move_to(FT_Vector* aTo,void* aObject)
+ {
+ FF_path_info* p = (FF_path_info*)aObject;
+ p->m_x = aTo->x;
+ p->m_y = aTo->y;
+ return p->m_path->moveto(p->m_path,aTo->x,aTo->y) ? -1 : 0;
+ }
+
+static int line_to(FT_Vector* aTo,void* aObject)
+ {
+ FF_path_info* p = (FF_path_info*)aObject;
+ p->m_x = aTo->x;
+ p->m_y = aTo->y;
+ return p->m_path->lineto(p->m_path,aTo->x,aTo->y) ? -1 : 0;
+ }
+
+static int conic_to(FT_Vector* aControl,FT_Vector* aTo,void* aObject)
+ {
+ FF_path_info* p = (FF_path_info*)aObject;
+ p->m_x = aTo->x;
+ p->m_y = aTo->y;
+ /*
+ Convert a quadratic spline to a cubic. Do this by changing the three points
+ A, B and C to A, 1/3(B,A), 1/3(B,C), C - that is, the two cubic control points are
+ a third of the way from the single quadratic control point to the end points. This
+ gives the same curve as the original quadratic.
+ */
+ return p->m_path->curveto(p->m_path,(p->m_x + aControl->x * 2) / 3,
+ (p->m_y + aControl->y * 2) / 3,
+ (aTo->x + aControl->x * 2) / 3,
+ (aTo->y + aControl->y * 2) / 3,
+ aTo->x,aTo->y) ? -1 : 0;
+ }
+
+static int cubic_to(FT_Vector* aControl1,FT_Vector* aControl2,FT_Vector* aTo,void* aObject)
+ {
+ FF_path_info* p = (FF_path_info*)aObject;
+ p->m_x = aTo->x;
+ p->m_y = aTo->y;
+ return p->m_path->curveto(p->m_path,aControl1->x,aControl1->y,aControl2->x,aControl2->y,aTo->x,aTo->y) ? -1 : 0;
+ }
+
+static const FT_Outline_Funcs TheFtOutlineFuncs =
+ {
+ move_to,
+ line_to,
+ conic_to,
+ cubic_to,
+ 0,
+ 0
+ };
+
+/**
+Return the outline created by the last call to get_char_outline_metrics.
+*/
+static FAPI_retcode get_char_outline(FAPI_server *a_server,FAPI_path *a_path)
+ {
+ FF_server* s = (FF_server*)a_server;
+ FF_path_info p;
+ FT_Error ft_error = 0;
+ p.m_path = a_path;
+ p.m_x = 0;
+ p.m_y = 0;
+ ft_error = FT_Outline_Decompose(&s->m_outline_glyph->outline,&TheFtOutlineFuncs,&p);
+ a_path->closepath(a_path);
+ return ft_to_gs_error(ft_error);
+ }
+
+static FAPI_retcode release_char_data(FAPI_server *a_server)
+ {
+ FF_server* s = (FF_server*)a_server;
+ FT_Done_Glyph(&s->m_outline_glyph->root);
+ FT_Done_Glyph(&s->m_bitmap_glyph->root);
+ s->m_outline_glyph = NULL;
+ s->m_bitmap_glyph = NULL;
+ return 0;
+ }
+
+static FAPI_retcode release_typeface(FAPI_server* a_server,void* a_server_font_data)
+ {
+ FF_face* face = (FF_face*)a_server_font_data;
+ delete_face(face);
+ return 0;
+ }
+
+static void gs_freetype_destroy(i_plugin_instance* a_instance,i_plugin_client_memory* a_memory);
+
+static const i_plugin_descriptor TheFreeTypeDescriptor =
+ {
+ "FAPI",
+ "FreeType",
+ gs_freetype_destroy
+ };
+
+static const FAPI_server TheFreeTypeServer =
+ {
+ { &TheFreeTypeDescriptor },
+ 16, /* frac_shift */
+ {gs_no_id},
+ {0},
+ ensure_open,
+ get_scaled_font,
+ get_decodingID,
+ get_font_bbox,
+ get_font_proportional_feature,
+ can_retrieve_char_by_name,
+ can_replace_metrics,
+ get_char_width,
+ get_char_raster_metrics,
+ get_char_raster,
+ get_char_outline_metrics,
+ get_char_outline,
+ release_char_data,
+ release_typeface
+ };
+
+plugin_instantiation_proc(gs_fapi_ft_instantiate);
+
+int gs_fapi_ft_instantiate( i_plugin_client_memory *a_memory,
+ i_plugin_instance **a_plugin_instance)
+ {
+ FF_server *server = (FF_server *)a_memory->alloc(a_memory,
+ sizeof(FF_server),"FF_server");
+ if (!server)
+ return e_VMerror;
+ memset(server,0,sizeof(*server));
+ server->m_fapi_server = TheFreeTypeServer;
+ *a_plugin_instance = &server->m_fapi_server.ig;
+ return 0;
+ }
+
+static void gs_freetype_destroy(i_plugin_instance *a_plugin_instance,i_plugin_client_memory *a_memory)
+ {
+ FF_server *server = (FF_server *)a_plugin_instance;
+ FT_Done_Glyph(&server->m_outline_glyph->root);
+ FT_Done_Glyph(&server->m_bitmap_glyph->root);
+ FT_Done_FreeType(server->m_freetype_library);
+ a_memory->free(a_memory,server,"FF_server");
+ }