Mercurial > emacs
changeset 93590:57eed2a0d99f
Font backend implementation for Uniscribe on Windows 2000 and later.
author | Jason Rumney <jasonr@gnu.org> |
---|---|
date | Thu, 03 Apr 2008 11:56:40 +0000 |
parents | 91c1bfd462dc |
children | e106254b75f2 |
files | src/w32uniscribe.c |
diffstat | 1 files changed, 865 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/w32uniscribe.c Thu Apr 03 11:56:40 2008 +0000 @@ -0,0 +1,865 @@ +/* Font backend for the Microsoft W32 Uniscribe API. + Copyright (C) 2008 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#ifdef USE_FONT_BACKEND + +#include <config.h> +/* Override API version - Uniscribe is only available as standard since + Windows 2000, though most users of older systems will have it + since it installs with Internet Explorer 5.0 and other software. + We only enable the feature if it is available, so there is no chance + of calling non-existant functions. */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x500 +#include <windows.h> +#include <usp10.h> + +#include "lisp.h" +#include "w32term.h" +#include "frame.h" +#include "dispextern.h" +#include "character.h" +#include "charset.h" +#include "fontset.h" +#include "font.h" +#include "w32font.h" + +struct uniscribe_font_info +{ + struct w32font_info w32_font; + SCRIPT_CACHE cache; +}; + +int uniscribe_available = 0; + +/* Defined in w32font.c, since it is required there as well. */ +extern Lisp_Object Quniscribe; +extern Lisp_Object Qopentype; + +extern int initialized; + +extern struct font_driver uniscribe_font_driver; + +/* EnumFontFamiliesEx callback. */ +static int CALLBACK add_opentype_font_name_to_list P_ ((ENUMLOGFONTEX *, + NEWTEXTMETRICEX *, + DWORD, LPARAM)); +/* Used by uniscribe_otf_capability. */ +static Lisp_Object otf_features (HDC context, char *table); + +static int +memq_no_quit (elt, list) + Lisp_Object elt, list; +{ + while (CONSP (list) && ! EQ (XCAR (list), elt)) + list = XCDR (list); + return (CONSP (list)); +} + + +/* Font backend interface implementation. */ +static Lisp_Object +uniscribe_list (frame, font_spec) + Lisp_Object frame, font_spec; +{ + return w32font_list_internal (frame, font_spec, 1); +} + +static Lisp_Object +uniscribe_match (frame, font_spec) + Lisp_Object frame, font_spec; +{ + return w32font_match_internal (frame, font_spec, 1); +} + +static Lisp_Object +uniscribe_list_family (frame) + Lisp_Object frame; +{ + Lisp_Object list = Qnil; + LOGFONT font_match_pattern; + HDC dc; + FRAME_PTR f = XFRAME (frame); + + bzero (&font_match_pattern, sizeof (font_match_pattern)); + /* Limit enumerated fonts to outline fonts to save time. */ + font_match_pattern.lfOutPrecision = OUT_OUTLINE_PRECIS; + + dc = get_frame_dc (f); + + EnumFontFamiliesEx (dc, &font_match_pattern, + (FONTENUMPROC) add_opentype_font_name_to_list, + (LPARAM) &list, 0); + release_frame_dc (f, dc); + + return list; +} + +static struct font * +uniscribe_open (f, font_entity, pixel_size) + FRAME_PTR f; + Lisp_Object font_entity; + int pixel_size; +{ + struct uniscribe_font_info *uniscribe_font + = xmalloc (sizeof (struct uniscribe_font_info)); + + if (uniscribe_font == NULL) + return NULL; + + if (!w32font_open_internal (f, font_entity, pixel_size, + (struct w32font_info *) uniscribe_font)) + { + xfree (uniscribe_font); + return NULL; + } + + /* Initialize the cache for this font. */ + uniscribe_font->cache = NULL; + /* Mark the format as opentype */ + uniscribe_font->w32_font.font.format = Qopentype; + uniscribe_font->w32_font.font.driver = &uniscribe_font_driver; + + return (struct font *) uniscribe_font; +} + +static void +uniscribe_close (f, font) + FRAME_PTR f; + struct font *font; +{ + struct uniscribe_font_info *uniscribe_font + = (struct uniscribe_font_info *) font; + + if (uniscribe_font->cache) + ScriptFreeCache (&uniscribe_font->cache); + + w32font_close (f, font); +} + +/* Return a list describing which scripts/languages FONT supports by + which GSUB/GPOS features of OpenType tables. */ +static Lisp_Object +uniscribe_otf_capability (font) + struct font *font; +{ + HDC context; + HFONT old_font; + struct frame *f; + Lisp_Object capability = Fcons (Qnil, Qnil); + Lisp_Object features; + + f = XFRAME (selected_frame); + context = get_frame_dc (f); + old_font = SelectObject (context, + ((W32FontStruct *) (font->font.font))->hfont); + + features = otf_features (context, "GSUB"); + XSETCAR (capability, features); + features = otf_features (context, "GPOS"); + XSETCDR (capability, features); + + SelectObject (context, old_font); + release_frame_dc (f, context); + + return capability; +} + +/* Uniscribe implementation of shape for font backend. + + Shape text in LGSTRING. See the docstring of `font-make-gstring' + for the format of LGSTRING. If the (N+1)th element of LGSTRING + is nil, input of shaping is from the 1st to (N)th elements. In + each input glyph, FROM, TO, CHAR, and CODE are already set. + + This function updates all fields of the input glyphs. If the + output glyphs (M) are more than the input glyphs (N), (N+1)th + through (M)th elements of LGSTRING are updated possibly by making + a new glyph object and storing it in LGSTRING. If (M) is greater + than the length of LGSTRING, nil should be return. In that case, + this function is called again with the larger LGSTRING. */ +static Lisp_Object +uniscribe_shape (lgstring) + Lisp_Object lgstring; +{ + struct font * font; + struct uniscribe_font_info * uniscribe_font; + EMACS_UINT nchars; + int nitems, max_items, i, max_glyphs, done_glyphs, done_chars; + wchar_t *chars; + WORD *glyphs, *clusters; + SCRIPT_ITEM *items; + SCRIPT_CONTROL control; + SCRIPT_VISATTR *attributes; + int *advances; + GOFFSET *offsets; + ABC overall_metrics; + MAT2 transform; + HDC context; + HFONT old_font; + HRESULT result; + struct frame * f; + + CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring), font); + uniscribe_font = (struct uniscribe_font_info *) font; + + /* Get the chars from lgstring in a form we can use with uniscribe. */ + max_glyphs = nchars = LGSTRING_LENGTH (lgstring); + done_glyphs = done_chars = 0; + chars = (wchar_t *) alloca (nchars * sizeof (wchar_t)); + for (i = 0; i < nchars; i++) + { + /* lgstring can be bigger than the number of characters in it, in + the case where more glyphs are required to display those characters. + If that is the case, note the real number of characters. */ + if (NILP (LGSTRING_GLYPH (lgstring, i))) + nchars = i; + else + chars[i] = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i)); + } + + /* First we need to break up the glyph string into runs of glyphs that + can be treated together. First try a single run. */ + max_items = 2; + items = (SCRIPT_ITEM *) xmalloc (sizeof (SCRIPT_ITEM) * max_items); + bzero (&control, sizeof (control)); + + while ((result = ScriptItemize (chars, nchars, max_items, &control, NULL, + items, &nitems)) == E_OUTOFMEMORY) + { + /* If that wasn't enough, keep trying with one more run. */ + max_items++; + items = (SCRIPT_ITEM *) xrealloc (items, + sizeof (SCRIPT_ITEM) * max_items); + } + + /* 0 = success in Microsoft's backwards world. */ + if (result) + { + xfree (items); + return Qnil; + } + + f = XFRAME (selected_frame); + context = get_frame_dc (f); + old_font = SelectObject (context, + ((W32FontStruct *) (font->font.font))->hfont); + + glyphs = alloca (max_glyphs * sizeof (WORD)); + clusters = alloca (nchars * sizeof (WORD)); + attributes = alloca (max_glyphs * sizeof (SCRIPT_VISATTR)); + advances = alloca (max_glyphs * sizeof (int)); + offsets = alloca (max_glyphs * sizeof (GOFFSET)); + bzero (&transform, sizeof (transform)); + transform.eM11.value = 1; + transform.eM22.value = 1; + + for (i = 0; i < nitems; i++) + { + int nglyphs, nchars_in_run; + nchars_in_run = items[i+1].iCharPos - items[i].iCharPos; + + result = ScriptShape (context, &(uniscribe_font->cache), + chars + items[i].iCharPos, nchars_in_run, + max_glyphs - done_glyphs, &(items[i].a), + glyphs, clusters, attributes, &nglyphs); + if (result == E_OUTOFMEMORY) + { + /* Need a bigger lgstring. */ + lgstring = Qnil; + break; + } + else if (result) + { + /* Can't shape this run - return results so far if any. */ + break; + } + else + { + result = ScriptPlace (context, &(uniscribe_font->cache), + glyphs, nglyphs, attributes, &(items[i].a), + advances, offsets, &overall_metrics); + if (!result) + { + int j, nclusters; + + for (j = 0; j < nglyphs; j++) + { + int lglyph_index = j + done_glyphs; + Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, lglyph_index); + GLYPHMETRICS metrics; + + if (NILP (lglyph)) + { + lglyph = Fmake_vector (make_number (LGLYPH_SIZE), Qnil); + LGSTRING_SET_GLYPH (lgstring, lglyph_index, lglyph); + } + LGLYPH_SET_CODE (lglyph, glyphs[j]); + LGLYPH_SET_WIDTH (lglyph, advances[j]); + if (offsets[j].du || offsets[j].dv) + { + Lisp_Object vec; + /* Convert from logical inches. */ + int dpi = FRAME_W32_DISPLAY_INFO (f)->resy; + int dx = (int)(offsets[j].du * dpi / 72.27 + 0.5); + int dy = (int)(offsets[j].dv * dpi / 72.27 + 0.5); + vec = Fmake_vector (make_number (3), Qnil); + ASET (vec, 0, make_number (dx)); + ASET (vec, 1, make_number (dy)); + /* Based on what ftfont.c does... */ + ASET (vec, 2, make_number (advances[j])); + LGLYPH_SET_ADJUSTMENT (lglyph, vec); + } + else + LGLYPH_SET_ADJUSTMENT (lglyph, Qnil); + + if (GetGlyphOutlineW (context, glyphs[j], + GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &transform) + != GDI_ERROR) + { + LGLYPH_SET_LBEARING (lglyph, metrics.gmptGlyphOrigin.x); + LGLYPH_SET_RBEARING (lglyph, + metrics.gmBlackBoxX + + metrics.gmptGlyphOrigin.x); + LGLYPH_SET_ASCENT (lglyph, metrics.gmBlackBoxY); + LGLYPH_SET_DESCENT (lglyph, + (metrics.gmCellIncY + - metrics.gmptGlyphOrigin.y + - metrics.gmBlackBoxY)); + } + else + { + /* Defaults based on what we know from elsewhere. */ + LGLYPH_SET_LBEARING (lglyph, 0); + LGLYPH_SET_RBEARING (lglyph, advances[j]); + LGLYPH_SET_ASCENT (lglyph, font->ascent); + LGLYPH_SET_DESCENT (lglyph, font->descent); + } + } + + /* Set character codes as indicated in clusters. */ + for (j = 0; j < nchars_in_run - 1; j++) + { + int k, start, end; + wchar_t this_char = *(chars + items[i].iCharPos + j); + + start = clusters[i]; + end = clusters[i+1]; + if (start > end) + { + int temp = start; + start = end; + end = temp; + } + for (k = start; k < end; k++) + { + Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, + done_glyphs + k); + LGLYPH_SET_CHAR (lglyph, this_char); + } + } + /* Last one until end (or beginning) of string. */ + { + int k, start, end; + wchar_t last_char = *(chars + items[i].iCharPos + + nchars_in_run - 1); + start = clusters[nchars_in_run - 1]; + end = nglyphs; + if (start < clusters[nchars_in_run - 2]) + { + end = start + 1; + start = 0; + } + for (k = start; k < end; k++) + { + Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, + done_glyphs + k); + LGLYPH_SET_CHAR (lglyph, last_char); + } + } + } + } + done_glyphs += nglyphs; + done_chars += nchars_in_run; + } + + xfree (items); + SelectObject (context, old_font); + release_frame_dc (f, context); + + if (NILP (lgstring)) + return Qnil; + else + return make_number (done_chars); +} + +/* Uniscribe implementation of encode_char for font backend. + Return a glyph code of FONT for characer C (Unicode code point). + If FONT doesn't have such a glyph, return FONT_INVALID_CODE. */ +static unsigned +uniscribe_encode_char (font, c) + struct font *font; + int c; +{ + wchar_t chars[1]; + WORD indices[1]; + HDC context; + struct frame *f; + HFONT old_font; + DWORD retval; + + /* TODO: surrogates. */ + if (c > 0xFFFF) + return FONT_INVALID_CODE; + + chars[0] = (wchar_t) c; + + /* Use selected frame until API is updated to pass the frame. */ + f = XFRAME (selected_frame); + context = get_frame_dc (f); + old_font = SelectObject (context, + ((W32FontStruct *)(font->font.font))->hfont); + + retval = GetGlyphIndicesW (context, chars, 1, indices, + GGI_MARK_NONEXISTING_GLYPHS); + + SelectObject (context, old_font); + release_frame_dc (f, context); + + if (retval == 1) + return indices[0] == 0xFFFF ? FONT_INVALID_CODE : indices[0]; + else + return FONT_INVALID_CODE; +} + +/* + Shared with w32font: + Lisp_Object uniscribe_get_cache (Lisp_Object frame); + void uniscribe_free_entity (Lisp_Object font_entity); + int uniscribe_has_char (Lisp_Object entity, int c); + int uniscribe_text_extents (struct font *font, unsigned *code, + int nglyphs, struct font_metrics *metrics); + int uniscribe_draw (struct glyph_string *s, int from, int to, + int x, int y, int with_background); + + Unused: + int uniscribe_prepare_face (FRAME_PTR f, struct face *face); + void uniscribe_done_face (FRAME_PTR f, struct face *face); + int uniscribe_get_bitmap (struct font *font, unsigned code, + struct font_bitmap *bitmap, int bits_per_pixel); + void uniscribe_free_bitmap (struct font *font, struct font_bitmap *bitmap); + void * uniscribe_get_outline (struct font *font, unsigned code); + void uniscribe_free_outline (struct font *font, void *outline); + int uniscribe_anchor_point (struct font *font, unsigned code, + int index, int *x, int *y); + int uniscribe_start_for_frame (FRAME_PTR f); + int uniscribe_end_for_frame (FRAME_PTR f); + +*/ + + +/* Callback function for EnumFontFamiliesEx. + Adds the name of opentype fonts to a Lisp list (passed in as the + lParam arg). */ +static int CALLBACK +add_opentype_font_name_to_list (logical_font, physical_font, font_type, + list_object) + ENUMLOGFONTEX *logical_font; + NEWTEXTMETRICEX *physical_font; + DWORD font_type; + LPARAM list_object; +{ + Lisp_Object* list = (Lisp_Object *) list_object; + Lisp_Object family; + + /* Skip vertical fonts (intended only for printing) */ + if (logical_font->elfLogFont.lfFaceName[0] == '@') + return 1; + + /* Skip non opentype fonts. Count old truetype fonts as opentype, + as some of them do contain GPOS and GSUB data that Uniscribe + can make use of. */ + if (!(physical_font->ntmTm.ntmFlags & NTMFLAGS_OPENTYPE) + && font_type != TRUETYPE_FONTTYPE) + return 1; + + family = intern_downcase (logical_font->elfLogFont.lfFaceName, + strlen (logical_font->elfLogFont.lfFaceName)); + if (! memq_no_quit (family, *list)) + *list = Fcons (family, *list); + + return 1; +} + + +/* :otf property handling. + Since the necessary Uniscribe APIs for getting font tag information + are only available in Vista, we need to parse the font data directly + according to the OpenType Specification. */ + +/* Push into DWORD backwards to cope with endianness. */ +#define OTF_TAG(STR) \ + ((STR[3] << 24) | (STR[2] << 16) | (STR[1] << 8) | STR[0]) + +#define OTF_INT16_VAL(TABLE, OFFSET, PTR) \ + do { \ + BYTE temp, data[2]; \ + if (GetFontData (context, TABLE, OFFSET, data, 2) != 2) \ + goto font_table_error; \ + temp = data[0], data[0] = data[1], data[1] = temp; \ + memcpy (PTR, data, 2); \ + } while (0) + +/* Do not reverse the bytes, because we will compare with a OTF_TAG value + that has them reversed already. */ +#define OTF_DWORDTAG_VAL(TABLE, OFFSET, PTR) \ + do { \ + if (GetFontData (context, TABLE, OFFSET, PTR, 4) != 4) \ + goto font_table_error; \ + } while (0) + +#define OTF_TAG_VAL(TABLE, OFFSET, STR) \ + do { \ + if (GetFontData (context, TABLE, OFFSET, STR, 4) != 4) \ + goto font_table_error; \ + STR[4] = '\0'; \ + } while (0) + +static char* NOTHING = " "; + +#define SNAME(VAL) SDATA (STRINGP (VAL) ? VAL : SYMBOL_NAME (VAL)) + +/* Check if font supports the otf script/language/features specified. + OTF_SPEC is in the format + (script lang [(gsub_feature ...)|nil] [(gpos_feature ...)]?) */ +int uniscribe_check_otf (font, otf_spec) + LOGFONT *font; + Lisp_Object otf_spec; +{ + Lisp_Object script, lang, rest; + Lisp_Object features[2]; + DWORD feature_tables[2]; + DWORD script_tag, default_script, lang_tag = 0; + struct frame * f; + HDC context; + HFONT check_font, old_font; + DWORD table; + int i, retval = 0; + + /* Break otf_spec into its components. */ + script = XCAR (otf_spec); + rest = XCDR (otf_spec); + + lang = XCAR (rest); + rest = XCDR (rest); + + features[0] = XCAR (rest); + rest = XCDR (rest); + if (NILP (rest)) + features[1] = Qnil; + else + features[1] = XCAR (rest); + + /* Set up tags we will use in the search. */ + feature_tables[0] = OTF_TAG ("GSUB"); + feature_tables[1] = OTF_TAG ("GPOS"); + default_script = OTF_TAG ("DFLT"); + if (NILP (script)) + script_tag = default_script; + else + script_tag = OTF_TAG (SNAME (script)); + if (!NILP (lang)) + lang_tag = OTF_TAG (SNAME (lang)); + + /* Set up graphics context so we can use the font. */ + f = XFRAME (selected_frame); + context = get_frame_dc (f); + check_font = CreateFontIndirect (font); + old_font = SelectObject (context, check_font); + + /* Scan GSUB and GPOS tables. */ + for (i = 0; i < 2; i++) + { + int j, n_match_features; + unsigned short scriptlist_table, feature_table, n_scripts; + unsigned short script_table, langsys_table, n_langs; + unsigned short feature_index, n_features; + DWORD tbl = feature_tables[i]; + Lisp_Object feature; + + /* Skip if no features requested from this table. */ + if (NILP (features[i])) + continue; + + /* Read GPOS/GSUB header. */ + OTF_INT16_VAL (tbl, 4, &scriptlist_table); + OTF_INT16_VAL (tbl, 6, &feature_table); + OTF_INT16_VAL (tbl, scriptlist_table, &n_scripts); + + /* Find the appropriate script table. */ + script_table = 0; + for (j = 0; j < n_scripts; j++) + { + DWORD script_id; + OTF_DWORDTAG_VAL (tbl, scriptlist_table + 2 + j * 6, &script_id); + if (script_id == script_tag) + { + OTF_INT16_VAL (tbl, scriptlist_table + 6 + j * 6, &script_table); + break; + } + /* If there is a DFLT script defined in the font, use it + if the specified script is not found. */ + else if (script_id == default_script) + OTF_INT16_VAL (tbl, scriptlist_table + 6 + j * 6, &script_table); + } + /* If no specific or default script table was found, then this font + does not support the script. */ + if (!script_table) + goto no_support; + + /* Offset is from beginning of scriptlist_table. */ + script_table += scriptlist_table; + + /* Get default langsys table. */ + OTF_INT16_VAL (tbl, script_table, &langsys_table); + + /* If lang was specified, see if font contains a specific entry. */ + if (!NILP (lang)) + { + OTF_INT16_VAL (tbl, script_table + 2, &n_langs); + + for (j = 0; j < n_langs; j++) + { + DWORD lang_id; + OTF_DWORDTAG_VAL (tbl, script_table + 4 + j * 6, &lang_id); + if (lang_id == lang_tag) + { + OTF_INT16_VAL (tbl, script_table + 8 + j * 6, &langsys_table); + break; + } + } + } + + if (!langsys_table) + goto no_support; + + /* Offset is from beginning of script table. */ + langsys_table += script_table; + + /* Check the features. Features may contain nil according to + documentation in font_prop_validate_otf, so count them. */ + n_match_features = 0; + rest = features[i]; + for (feature = XCAR (rest); CONSP (rest); feature = XCAR (rest)) + { + rest = XCDR (rest); + if (!NILP (feature)) + n_match_features++; + } + + /* If there are no features to check, skip checking. */ + if (!n_match_features) + continue; + + /* First check required feature (if any). */ + OTF_INT16_VAL (tbl, langsys_table + 2, &feature_index); + if (feature_index != 0xFFFF) + { + char feature_id[5]; + OTF_TAG_VAL (tbl, feature_table + 2 + feature_index * 6, feature_id); + OTF_TAG_VAL (tbl, feature_table + 2 + feature_index * 6, feature_id); + /* Assume no duplicates in the font table. This allows us to mark + the features off by simply decrementing a counter. */ + if (!NILP (Fmemq (intern (feature_id), features[i]))) + n_match_features--; + } + /* Now check all the other features. */ + OTF_INT16_VAL (tbl, langsys_table + 4, &n_features); + for (j = 0; j < n_features; j++) + { + char feature_id[5]; + OTF_INT16_VAL (tbl, langsys_table + 6 + j * 2, &feature_index); + OTF_TAG_VAL (tbl, feature_table + 2 + feature_index * 6, feature_id); + /* Assume no duplicates in the font table. This allows us to mark + the features off by simply decrementing a counter. */ + if (!NILP (Fmemq (intern (feature_id), features[i]))) + n_match_features--; + } + + if (n_match_features > 0) + goto no_support; + } + + retval = 1; + + no_support: + font_table_error: + /* restore graphics context. */ + SelectObject (context, old_font); + DeleteObject (check_font); + release_frame_dc (f, context); + + return retval; +} + +static Lisp_Object +otf_features (HDC context, char *table) +{ + Lisp_Object script_list = Qnil; + unsigned short scriptlist_table, n_scripts, feature_table; + DWORD tbl = OTF_TAG (table); + int i, j, k; + + /* Look for scripts in the table. */ + OTF_INT16_VAL (tbl, 4, &scriptlist_table); + OTF_INT16_VAL (tbl, 6, &feature_table); + OTF_INT16_VAL (tbl, scriptlist_table, &n_scripts); + + for (i = 0; i < n_scripts; i++) + { + char script[5], lang[5]; + unsigned short script_table, lang_count, langsys_table, feature_count; + Lisp_Object script_tag, langsys_list, langsys_tag, feature_list; + unsigned short record_offset = scriptlist_table + 2 + i * 6; + OTF_TAG_VAL (tbl, record_offset, script); + OTF_INT16_VAL (tbl, record_offset + 4, &script_table); + + /* Offset is from beginning of script table. */ + script_table += scriptlist_table; + + script_tag = intern (script); + langsys_list = Qnil; + + /* Optional default lang. */ + OTF_INT16_VAL (tbl, script_table, &langsys_table); + if (langsys_table) + { + /* Offset is from beginning of script table. */ + langsys_table += script_table; + + langsys_tag = Qnil; + feature_list = Qnil; + OTF_INT16_VAL (tbl, langsys_table + 4, &feature_count); + for (k = 0; k < feature_count; k++) + { + char feature[5]; + unsigned short index; + OTF_INT16_VAL (tbl, langsys_table + 6 + k * 2, &index); + OTF_TAG_VAL (tbl, feature_table + 2 + index * 6, feature); + feature_list = Fcons (intern (feature), feature_list); + } + langsys_list = Fcons (Fcons (langsys_tag, feature_list), + langsys_list); + } + + /* List of supported languages. */ + OTF_INT16_VAL (tbl, script_table + 2, &lang_count); + + for (j = 0; j < lang_count; j++) + { + record_offset = script_table + 4 + j * 6; + OTF_TAG_VAL (tbl, record_offset, lang); + OTF_INT16_VAL (tbl, record_offset + 4, &langsys_table); + + /* Offset is from beginning of script table. */ + langsys_table += script_table; + + langsys_tag = intern (lang); + feature_list = Qnil; + OTF_INT16_VAL (tbl, langsys_table + 4, &feature_count); + for (k = 0; k < feature_count; k++) + { + char feature[5]; + unsigned short index; + OTF_INT16_VAL (tbl, langsys_table + 6 + k * 2, &index); + OTF_TAG_VAL (tbl, feature_table + 2 + index * 6, feature); + feature_list = Fcons (intern (feature), feature_list); + } + langsys_list = Fcons (Fcons (langsys_tag, feature_list), + langsys_list); + + } + + script_list = Fcons (Fcons (script_tag, langsys_list), script_list); + } + + return script_list; + +font_table_error: + return Qnil; +} + +#undef OTF_INT16_VAL +#undef OTF_TAG_VAL +#undef OTF_TAG + + +struct font_driver uniscribe_font_driver = + { + 0, /* Quniscribe */ + w32font_get_cache, + uniscribe_list, + uniscribe_match, + uniscribe_list_family, + NULL, /* free_entity */ + uniscribe_open, + uniscribe_close, + NULL, /* prepare_face */ + NULL, /* done_face */ + w32font_has_char, + uniscribe_encode_char, + w32font_text_extents, + w32font_draw, + NULL, /* get_bitmap */ + NULL, /* free_bitmap */ + NULL, /* get_outline */ + NULL, /* free_outline */ + NULL, /* anchor_point */ + uniscribe_otf_capability, /* Defined so (font-get FONTOBJ :otf) works. */ + NULL, /* otf_drive - use shape instead. */ + NULL, /* start_for_frame */ + NULL, /* end_for_frame */ + uniscribe_shape + }; + +/* Note that this should be called at every startup, not just when dumping, + as it needs to test for the existence of the Uniscribe library. */ +void +syms_of_w32uniscribe () +{ + HMODULE uniscribe; + + /* Don't init uniscribe when dumping */ + if (!initialized) + return; + + /* Don't register if uniscribe is not available. */ + uniscribe = GetModuleHandle ("usp10"); + if (!uniscribe) + return; + + uniscribe_font_driver.type = Quniscribe; + uniscribe_available = 1; + + register_font_driver (&uniscribe_font_driver, NULL); +} + +#endif /* USE_FONT_BACKEND */