michael@0: /* michael@0: * Copyright © 2009 Red Hat, Inc. michael@0: * Copyright © 2009 Keith Stribley michael@0: * michael@0: * This is part of HarfBuzz, a text shaping library. michael@0: * michael@0: * Permission is hereby granted, without written agreement and without michael@0: * license or royalty fees, to use, copy, modify, and distribute this michael@0: * software and its documentation for any purpose, provided that the michael@0: * above copyright notice and the following two paragraphs appear in michael@0: * all copies of this software. michael@0: * michael@0: * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR michael@0: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES michael@0: * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN michael@0: * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH michael@0: * DAMAGE. michael@0: * michael@0: * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, michael@0: * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND michael@0: * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS michael@0: * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO michael@0: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. michael@0: * michael@0: * Red Hat Author(s): Behdad Esfahbod michael@0: */ michael@0: michael@0: #include "hb-private.hh" michael@0: michael@0: #include "hb-ft.h" michael@0: michael@0: #include "hb-font-private.hh" michael@0: michael@0: #include FT_ADVANCES_H michael@0: #include FT_TRUETYPE_TABLES_H michael@0: michael@0: michael@0: michael@0: #ifndef HB_DEBUG_FT michael@0: #define HB_DEBUG_FT (HB_DEBUG+0) michael@0: #endif michael@0: michael@0: michael@0: /* TODO: michael@0: * michael@0: * In general, this file does a fine job of what it's supposed to do. michael@0: * There are, however, things that need more work: michael@0: * michael@0: * - We don't handle any load_flags. That definitely has API implications. :( michael@0: * I believe hb_ft_font_create() should take load_flags input. michael@0: * In particular, FT_Get_Advance() without the NO_HINTING flag seems to be michael@0: * buggy. michael@0: * michael@0: * - We don't handle / allow for emboldening / obliqueing. michael@0: * michael@0: * - In the future, we should add constructors to create fonts in font space? michael@0: * michael@0: * - FT_Load_Glyph() is exteremely costly. Do something about it? michael@0: */ michael@0: michael@0: michael@0: static hb_bool_t michael@0: hb_ft_get_glyph (hb_font_t *font HB_UNUSED, michael@0: void *font_data, michael@0: hb_codepoint_t unicode, michael@0: hb_codepoint_t variation_selector, michael@0: hb_codepoint_t *glyph, michael@0: void *user_data HB_UNUSED) michael@0: michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: michael@0: #ifdef HAVE_FT_FACE_GETCHARVARIANTINDEX michael@0: if (unlikely (variation_selector)) { michael@0: *glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector); michael@0: return *glyph != 0; michael@0: } michael@0: #endif michael@0: michael@0: *glyph = FT_Get_Char_Index (ft_face, unicode); michael@0: return *glyph != 0; michael@0: } michael@0: michael@0: static hb_position_t michael@0: hb_ft_get_glyph_h_advance (hb_font_t *font HB_UNUSED, michael@0: void *font_data, michael@0: hb_codepoint_t glyph, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; michael@0: FT_Fixed v; michael@0: michael@0: if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v))) michael@0: return 0; michael@0: michael@0: return (v + (1<<9)) >> 10; michael@0: } michael@0: michael@0: static hb_position_t michael@0: hb_ft_get_glyph_v_advance (hb_font_t *font HB_UNUSED, michael@0: void *font_data, michael@0: hb_codepoint_t glyph, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING | FT_LOAD_VERTICAL_LAYOUT; michael@0: FT_Fixed v; michael@0: michael@0: if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v))) michael@0: return 0; michael@0: michael@0: /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates michael@0: * have a Y growing upward. Hence the extra negation. */ michael@0: return (-v + (1<<9)) >> 10; michael@0: } michael@0: michael@0: static hb_bool_t michael@0: hb_ft_get_glyph_h_origin (hb_font_t *font HB_UNUSED, michael@0: void *font_data HB_UNUSED, michael@0: hb_codepoint_t glyph HB_UNUSED, michael@0: hb_position_t *x HB_UNUSED, michael@0: hb_position_t *y HB_UNUSED, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: /* We always work in the horizontal coordinates. */ michael@0: return true; michael@0: } michael@0: michael@0: static hb_bool_t michael@0: hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED, michael@0: void *font_data, michael@0: hb_codepoint_t glyph, michael@0: hb_position_t *x, michael@0: hb_position_t *y, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: int load_flags = FT_LOAD_DEFAULT; michael@0: michael@0: if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) michael@0: return false; michael@0: michael@0: /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates michael@0: * have a Y growing upward. Hence the extra negation. */ michael@0: *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; michael@0: *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static hb_position_t michael@0: hb_ft_get_glyph_h_kerning (hb_font_t *font, michael@0: void *font_data, michael@0: hb_codepoint_t left_glyph, michael@0: hb_codepoint_t right_glyph, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: FT_Vector kerningv; michael@0: michael@0: FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; michael@0: if (FT_Get_Kerning (ft_face, left_glyph, right_glyph, mode, &kerningv)) michael@0: return 0; michael@0: michael@0: return kerningv.x; michael@0: } michael@0: michael@0: static hb_position_t michael@0: hb_ft_get_glyph_v_kerning (hb_font_t *font HB_UNUSED, michael@0: void *font_data HB_UNUSED, michael@0: hb_codepoint_t top_glyph HB_UNUSED, michael@0: hb_codepoint_t bottom_glyph HB_UNUSED, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: /* FreeType API doesn't support vertical kerning */ michael@0: return 0; michael@0: } michael@0: michael@0: static hb_bool_t michael@0: hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED, michael@0: void *font_data, michael@0: hb_codepoint_t glyph, michael@0: hb_glyph_extents_t *extents, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: int load_flags = FT_LOAD_DEFAULT; michael@0: michael@0: if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) michael@0: return false; michael@0: michael@0: extents->x_bearing = ft_face->glyph->metrics.horiBearingX; michael@0: extents->y_bearing = ft_face->glyph->metrics.horiBearingY; michael@0: extents->width = ft_face->glyph->metrics.width; michael@0: extents->height = -ft_face->glyph->metrics.height; michael@0: return true; michael@0: } michael@0: michael@0: static hb_bool_t michael@0: hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, michael@0: void *font_data, michael@0: hb_codepoint_t glyph, michael@0: unsigned int point_index, michael@0: hb_position_t *x, michael@0: hb_position_t *y, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: int load_flags = FT_LOAD_DEFAULT; michael@0: michael@0: if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags))) michael@0: return false; michael@0: michael@0: if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) michael@0: return false; michael@0: michael@0: if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) michael@0: return false; michael@0: michael@0: *x = ft_face->glyph->outline.points[point_index].x; michael@0: *y = ft_face->glyph->outline.points[point_index].y; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static hb_bool_t michael@0: hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, michael@0: void *font_data, michael@0: hb_codepoint_t glyph, michael@0: char *name, unsigned int size, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: michael@0: hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); michael@0: if (ret && (size && !*name)) michael@0: ret = false; michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: static hb_bool_t michael@0: hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, michael@0: void *font_data, michael@0: const char *name, int len, /* -1 means nul-terminated */ michael@0: hb_codepoint_t *glyph, michael@0: void *user_data HB_UNUSED) michael@0: { michael@0: FT_Face ft_face = (FT_Face) font_data; michael@0: michael@0: if (len < 0) michael@0: *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name); michael@0: else { michael@0: /* Make a nul-terminated version. */ michael@0: char buf[128]; michael@0: len = MIN (len, (int) sizeof (buf) - 1); michael@0: strncpy (buf, name, len); michael@0: buf[len] = '\0'; michael@0: *glyph = FT_Get_Name_Index (ft_face, buf); michael@0: } michael@0: michael@0: if (*glyph == 0) michael@0: { michael@0: /* Check whether the given name was actually the name of glyph 0. */ michael@0: char buf[128]; michael@0: if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) && michael@0: len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len)) michael@0: return true; michael@0: } michael@0: michael@0: return *glyph != 0; michael@0: } michael@0: michael@0: michael@0: static hb_font_funcs_t * michael@0: _hb_ft_get_font_funcs (void) michael@0: { michael@0: static const hb_font_funcs_t ft_ffuncs = { michael@0: HB_OBJECT_HEADER_STATIC, michael@0: michael@0: true, /* immutable */ michael@0: michael@0: { michael@0: #define HB_FONT_FUNC_IMPLEMENT(name) hb_ft_get_##name, michael@0: HB_FONT_FUNCS_IMPLEMENT_CALLBACKS michael@0: #undef HB_FONT_FUNC_IMPLEMENT michael@0: } michael@0: }; michael@0: michael@0: return const_cast (&ft_ffuncs); michael@0: } michael@0: michael@0: michael@0: static hb_blob_t * michael@0: reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) michael@0: { michael@0: FT_Face ft_face = (FT_Face) user_data; michael@0: FT_Byte *buffer; michael@0: FT_ULong length = 0; michael@0: FT_Error error; michael@0: michael@0: /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ michael@0: michael@0: error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length); michael@0: if (error) michael@0: return NULL; michael@0: michael@0: buffer = (FT_Byte *) malloc (length); michael@0: if (buffer == NULL) michael@0: return NULL; michael@0: michael@0: error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); michael@0: if (error) michael@0: return NULL; michael@0: michael@0: return hb_blob_create ((const char *) buffer, length, michael@0: HB_MEMORY_MODE_WRITABLE, michael@0: buffer, free); michael@0: } michael@0: michael@0: /** michael@0: * hb_ft_face_create: michael@0: * @ft_face: (destroy destroy) (scope notified): michael@0: * @destroy: michael@0: * michael@0: * michael@0: * michael@0: * Return value: (transfer full): michael@0: * Since: 1.0 michael@0: **/ michael@0: hb_face_t * michael@0: hb_ft_face_create (FT_Face ft_face, michael@0: hb_destroy_func_t destroy) michael@0: { michael@0: hb_face_t *face; michael@0: michael@0: if (ft_face->stream->read == NULL) { michael@0: hb_blob_t *blob; michael@0: michael@0: blob = hb_blob_create ((const char *) ft_face->stream->base, michael@0: (unsigned int) ft_face->stream->size, michael@0: /* TODO: We assume that it's mmap()'ed, but FreeType code michael@0: * suggests that there are cases we reach here but font is michael@0: * not mmapped. For example, when mmap() fails. No idea michael@0: * how to deal with it better here. */ michael@0: HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, michael@0: ft_face, destroy); michael@0: face = hb_face_create (blob, ft_face->face_index); michael@0: hb_blob_destroy (blob); michael@0: } else { michael@0: face = hb_face_create_for_tables (reference_table, ft_face, destroy); michael@0: } michael@0: michael@0: hb_face_set_index (face, ft_face->face_index); michael@0: hb_face_set_upem (face, ft_face->units_per_EM); michael@0: michael@0: return face; michael@0: } michael@0: michael@0: static void michael@0: hb_ft_face_finalize (FT_Face ft_face) michael@0: { michael@0: hb_face_destroy ((hb_face_t *) ft_face->generic.data); michael@0: } michael@0: michael@0: /** michael@0: * hb_ft_face_create_cached: michael@0: * @ft_face: michael@0: * michael@0: * michael@0: * michael@0: * Return value: (transfer full): michael@0: * Since: 1.0 michael@0: **/ michael@0: hb_face_t * michael@0: hb_ft_face_create_cached (FT_Face ft_face) michael@0: { michael@0: if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) michael@0: { michael@0: if (ft_face->generic.finalizer) michael@0: ft_face->generic.finalizer (ft_face); michael@0: michael@0: ft_face->generic.data = hb_ft_face_create (ft_face, NULL); michael@0: ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; michael@0: } michael@0: michael@0: return hb_face_reference ((hb_face_t *) ft_face->generic.data); michael@0: } michael@0: michael@0: static void michael@0: _do_nothing (void) michael@0: { michael@0: } michael@0: michael@0: michael@0: /** michael@0: * hb_ft_font_create: michael@0: * @ft_face: (destroy destroy) (scope notified): michael@0: * @destroy: michael@0: * michael@0: * michael@0: * michael@0: * Return value: (transfer full): michael@0: * Since: 1.0 michael@0: **/ michael@0: hb_font_t * michael@0: hb_ft_font_create (FT_Face ft_face, michael@0: hb_destroy_func_t destroy) michael@0: { michael@0: hb_font_t *font; michael@0: hb_face_t *face; michael@0: michael@0: face = hb_ft_face_create (ft_face, destroy); michael@0: font = hb_font_create (face); michael@0: hb_face_destroy (face); michael@0: hb_font_set_funcs (font, michael@0: _hb_ft_get_font_funcs (), michael@0: ft_face, (hb_destroy_func_t) _do_nothing); michael@0: hb_font_set_scale (font, michael@0: (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16), michael@0: (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16)); michael@0: hb_font_set_ppem (font, michael@0: ft_face->size->metrics.x_ppem, michael@0: ft_face->size->metrics.y_ppem); michael@0: michael@0: return font; michael@0: } michael@0: michael@0: michael@0: /* Thread-safe, lock-free, FT_Library */ michael@0: michael@0: static FT_Library ft_library; michael@0: michael@0: static inline michael@0: void free_ft_library (void) michael@0: { michael@0: FT_Done_FreeType (ft_library); michael@0: } michael@0: michael@0: static FT_Library michael@0: get_ft_library (void) michael@0: { michael@0: retry: michael@0: FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library); michael@0: michael@0: if (unlikely (!library)) michael@0: { michael@0: /* Not found; allocate one. */ michael@0: if (FT_Init_FreeType (&library)) michael@0: return NULL; michael@0: michael@0: if (!hb_atomic_ptr_cmpexch (&ft_library, NULL, library)) { michael@0: FT_Done_FreeType (library); michael@0: goto retry; michael@0: } michael@0: michael@0: #ifdef HAVE_ATEXIT michael@0: atexit (free_ft_library); /* First person registers atexit() callback. */ michael@0: #endif michael@0: } michael@0: michael@0: return library; michael@0: } michael@0: michael@0: static void michael@0: _release_blob (FT_Face ft_face) michael@0: { michael@0: hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); michael@0: } michael@0: michael@0: void michael@0: hb_ft_font_set_funcs (hb_font_t *font) michael@0: { michael@0: hb_blob_t *blob = hb_face_reference_blob (font->face); michael@0: unsigned int blob_length; michael@0: const char *blob_data = hb_blob_get_data (blob, &blob_length); michael@0: if (unlikely (!blob_length)) michael@0: DEBUG_MSG (FT, font, "Font face has empty blob"); michael@0: michael@0: FT_Face ft_face = NULL; michael@0: FT_Error err = FT_New_Memory_Face (get_ft_library (), michael@0: (const FT_Byte *) blob_data, michael@0: blob_length, michael@0: hb_face_get_index (font->face), michael@0: &ft_face); michael@0: michael@0: if (unlikely (err)) { michael@0: hb_blob_destroy (blob); michael@0: DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed"); michael@0: return; michael@0: } michael@0: michael@0: FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); michael@0: michael@0: assert (font->y_scale >= 0); michael@0: FT_Set_Char_Size (ft_face, michael@0: font->x_scale, font->y_scale, michael@0: 0, 0); michael@0: #if 0 michael@0: font->x_ppem * 72 * 64 / font->x_scale, michael@0: font->y_ppem * 72 * 64 / font->y_scale); michael@0: #endif michael@0: michael@0: ft_face->generic.data = blob; michael@0: ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; michael@0: michael@0: hb_font_set_funcs (font, michael@0: _hb_ft_get_font_funcs (), michael@0: ft_face, michael@0: (hb_destroy_func_t) FT_Done_Face); michael@0: } michael@0: michael@0: FT_Face michael@0: hb_ft_font_get_face (hb_font_t *font) michael@0: { michael@0: if (font->destroy == (hb_destroy_func_t) FT_Done_Face || michael@0: font->destroy == (hb_destroy_func_t) _do_nothing) michael@0: return (FT_Face) font->user_data; michael@0: michael@0: return NULL; michael@0: }