X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/6d26235315194737c4dc8653cf6d05177a10fa9c..2a2c6ee8d18267c16e3953194c6066d9a22b88a5:/src/w32uniscribe.c
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index 7e6419c4d2..5f91b5022d 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -1,12 +1,12 @@
/* Font backend for the Microsoft W32 Uniscribe API.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2016 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 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, 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
@@ -18,24 +18,22 @@ along with GNU Emacs. If not, see . */
#include
-/* Override API version - Uniscribe is only available as standard since
- Windows 2000, though most users of older systems will have it
+/* 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-existent functions. */
+ Also, MinGW64 w32api headers by default define OPENTYPE_TAG typedef
+ only if _WIN32_WINNT >= 0x0600. We only use the affected APIs if
+ they are available, so there is no chance of calling non-existent
+ functions. */
#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x500
+#define _WIN32_WINNT 0x0600
#include
#include
#include "lisp.h"
#include "w32term.h"
#include "frame.h"
-#include "dispextern.h"
-#include "character.h"
-#include "charset.h"
#include "composite.h"
-#include "fontset.h"
#include "font.h"
#include "w32font.h"
@@ -47,16 +45,12 @@ struct uniscribe_font_info
int uniscribe_available = 0;
-/* Defined in w32font.c, since it is required there as well. */
-extern Lisp_Object Quniscribe;
-extern Lisp_Object Qopentype;
-
/* EnumFontFamiliesEx callback. */
-static int CALLBACK add_opentype_font_name_to_list (ENUMLOGFONTEX *,
- NEWTEXTMETRICEX *,
- DWORD, LPARAM);
+static int CALLBACK ALIGN_STACK add_opentype_font_name_to_list (ENUMLOGFONTEX *,
+ NEWTEXTMETRICEX *,
+ DWORD, LPARAM);
/* Used by uniscribe_otf_capability. */
-static Lisp_Object otf_features (HDC context, char *table);
+static Lisp_Object otf_features (HDC context, const char *table);
static int
memq_no_quit (Lisp_Object elt, Lisp_Object list)
@@ -71,7 +65,7 @@ memq_no_quit (Lisp_Object elt, Lisp_Object list)
static Lisp_Object
uniscribe_list (struct frame *f, Lisp_Object font_spec)
{
- Lisp_Object fonts = w32font_list_internal (f, font_spec, 1);
+ Lisp_Object fonts = w32font_list_internal (f, font_spec, true);
FONT_ADD_LOG ("uniscribe-list", font_spec, fonts);
return fonts;
}
@@ -79,7 +73,7 @@ uniscribe_list (struct frame *f, Lisp_Object font_spec)
static Lisp_Object
uniscribe_match (struct frame *f, Lisp_Object font_spec)
{
- Lisp_Object entity = w32font_match_internal (f, font_spec, 1);
+ Lisp_Object entity = w32font_match_internal (f, font_spec, true);
FONT_ADD_LOG ("uniscribe-match", font_spec, entity);
return entity;
}
@@ -145,7 +139,26 @@ uniscribe_close (struct font *font)
}
/* Return a list describing which scripts/languages FONT supports by
- which GSUB/GPOS features of OpenType tables. */
+ which GSUB/GPOS features of OpenType tables.
+
+ Implementation note: otf_features called by this function uses
+ GetFontData to access the font tables directly, instead of using
+ ScriptGetFontScriptTags etc. APIs even if those are available. The
+ reason is that font-get, which uses the result of this function,
+ expects a cons cell (GSUB . GPOS) where the features are reported
+ separately for these 2 OTF tables, while the Uniscribe APIs report
+ the features as a single list. There doesn't seem to be a reason
+ for returning the features in 2 separate parts, except for
+ compatibility with libotf; the features are disjoint (each can
+ appear only in one of the 2 slots), and no client of this data
+ discerns between the two slots: the few that request this data all
+ look in both slots. If use of the Uniscribe APIs ever becomes
+ necessary here, and the 2 separate slots are still required, it
+ should be possible to split the feature list the APIs return into 2
+ because each sub-list is alphabetically sorted, so the place where
+ the sorting order breaks is where the GSUB features end and GPOS
+ features begin. But for now, this is not necessary, so we leave
+ the original code in place. */
static Lisp_Object
uniscribe_otf_capability (struct font *font)
{
@@ -187,8 +200,9 @@ uniscribe_otf_capability (struct font *font)
static Lisp_Object
uniscribe_shape (Lisp_Object lgstring)
{
- struct font * font;
- struct uniscribe_font_info * uniscribe_font;
+ struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+ struct uniscribe_font_info *uniscribe_font
+ = (struct uniscribe_font_info *) font;
EMACS_UINT nchars;
int nitems, max_items, i, max_glyphs, done_glyphs;
wchar_t *chars;
@@ -203,9 +217,6 @@ uniscribe_shape (Lisp_Object lgstring)
HDC context = NULL;
HFONT old_font = NULL;
- 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_GLYPH_LEN (lgstring);
done_glyphs = 0;
@@ -613,7 +624,7 @@ uniscribe_encode_char (struct font *font, int c)
/* Callback function for EnumFontFamiliesEx.
Adds the name of opentype fonts to a Lisp list (passed in as the
lParam arg). */
-static int CALLBACK
+static int CALLBACK ALIGN_STACK
add_opentype_font_name_to_list (ENUMLOGFONTEX *logical_font,
NEWTEXTMETRICEX *physical_font,
DWORD font_type, LPARAM list_object)
@@ -649,7 +660,7 @@ add_opentype_font_name_to_list (ENUMLOGFONTEX *logical_font,
/* :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
+ are only available in Vista, we may need to parse the font data directly
according to the OpenType Specification. */
/* Push into DWORD backwards to cope with endianness. */
@@ -680,7 +691,170 @@ add_opentype_font_name_to_list (ENUMLOGFONTEX *logical_font,
STR[4] = '\0'; \
} while (0)
-#define SNAME(VAL) SDATA (SYMBOL_NAME (VAL))
+#define SNAME(VAL) SSDATA (SYMBOL_NAME (VAL))
+
+/* Uniscribe APIs available only since Windows Vista. */
+typedef HRESULT (WINAPI *ScriptGetFontScriptTags_Proc)
+ (HDC, SCRIPT_CACHE *, SCRIPT_ANALYSIS *, int, OPENTYPE_TAG *, int *);
+
+typedef HRESULT (WINAPI *ScriptGetFontLanguageTags_Proc)
+ (HDC, SCRIPT_CACHE *, SCRIPT_ANALYSIS *, OPENTYPE_TAG, int, OPENTYPE_TAG *, int *);
+
+typedef HRESULT (WINAPI *ScriptGetFontFeatureTags_Proc)
+ (HDC, SCRIPT_CACHE *, SCRIPT_ANALYSIS *, OPENTYPE_TAG, OPENTYPE_TAG, int, OPENTYPE_TAG *, int *);
+
+ScriptGetFontScriptTags_Proc script_get_font_scripts_fn;
+ScriptGetFontLanguageTags_Proc script_get_font_languages_fn;
+ScriptGetFontFeatureTags_Proc script_get_font_features_fn;
+
+static bool uniscribe_new_apis;
+
+/* Verify that all the required features in FEATURES, each of whose
+ elements is a list or nil, can be found among the N feature tags in
+ FTAGS. Return 'true' if the required features are supported,
+ 'false' if not. Each list in FEATURES can include an element of
+ nil, which means all the elements after it must not be in FTAGS. */
+static bool
+uniscribe_check_features (Lisp_Object features[2], OPENTYPE_TAG *ftags, int n)
+{
+ int j;
+
+ for (j = 0; j < 2; j++)
+ {
+ bool negative = false;
+ Lisp_Object rest;
+
+ for (rest = features[j]; CONSP (rest); rest = XCDR (rest))
+ {
+ Lisp_Object feature = XCAR (rest);
+
+ /* The font must NOT have any of the features after nil.
+ See the doc string of 'font-spec', under ':otf'. */
+ if (NILP (feature))
+ negative = true;
+ else
+ {
+ OPENTYPE_TAG feature_tag = OTF_TAG (SNAME (feature));
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ if (ftags[i] == feature_tag)
+ {
+ /* Test fails if we find a feature that the font
+ must NOT have. */
+ if (negative)
+ return false;
+ break;
+ }
+ }
+
+ /* Test fails if we do NOT find a feature that the font
+ should have. */
+ if (i >= n && !negative)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/* Check if font supports the required OTF script/language/features
+ using the Unsicribe APIs available since Windows Vista. We prefer
+ these APIs as a kind of future-proofing Emacs: they seem to
+ retrieve script tags that the old code (and also libotf) doesn't
+ seem to be able to get, e.g., some fonts that claim support for
+ "dev2" script don't show "deva", but the new APIs do report it. */
+static int
+uniscribe_check_otf_1 (HDC context, Lisp_Object script, Lisp_Object lang,
+ Lisp_Object features[2], int *retval)
+{
+ SCRIPT_CACHE cache = NULL;
+ OPENTYPE_TAG tags[32], script_tag, lang_tag;
+ int max_tags = ARRAYELTS (tags);
+ int ntags, i, ret = 0;
+ HRESULT rslt;
+
+ *retval = 0;
+
+ rslt = script_get_font_scripts_fn (context, &cache, NULL, max_tags,
+ tags, &ntags);
+ if (FAILED (rslt))
+ {
+ DebPrint (("ScriptGetFontScriptTags failed with 0x%x\n", rslt));
+ ret = -1;
+ goto no_support;
+ }
+ if (NILP (script))
+ script_tag = OTF_TAG ("DFLT");
+ else
+ script_tag = OTF_TAG (SNAME (script));
+ for (i = 0; i < ntags; i++)
+ if (tags[i] == script_tag)
+ break;
+
+ if (i >= ntags)
+ goto no_support;
+
+ if (NILP (lang))
+ lang_tag = OTF_TAG ("dflt");
+ else
+ {
+ rslt = script_get_font_languages_fn (context, &cache, NULL, script_tag,
+ max_tags, tags, &ntags);
+ if (FAILED (rslt))
+ {
+ DebPrint (("ScriptGetFontLanguageTags failed with 0x%x\n", rslt));
+ ret = -1;
+ goto no_support;
+ }
+ if (ntags == 0)
+ lang_tag = OTF_TAG ("dflt");
+ else
+ {
+ lang_tag = OTF_TAG (SNAME (lang));
+ for (i = 0; i < ntags; i++)
+ if (tags[i] == lang_tag)
+ break;
+
+ if (i >= ntags)
+ goto no_support;
+ }
+ }
+
+ if (!NILP (features[0]))
+ {
+ /* Are the 2 feature lists valid? */
+ if (!CONSP (features[0])
+ || (!NILP (features[1]) && !CONSP (features[1])))
+ goto no_support;
+ rslt = script_get_font_features_fn (context, &cache, NULL,
+ script_tag, lang_tag,
+ max_tags, tags, &ntags);
+ if (FAILED (rslt))
+ {
+ DebPrint (("ScriptGetFontFeatureTags failed with 0x%x\n", rslt));
+ ret = -1;
+ goto no_support;
+ }
+
+ /* ScriptGetFontFeatureTags doesn't let us query features
+ separately for GSUB and GPOS, so we check them all together.
+ It doesn't really matter, since the features in GSUB and GPOS
+ are disjoint, i.e. no feature can appear in both tables. */
+ if (!uniscribe_check_features (features, tags, ntags))
+ goto no_support;
+ }
+
+ ret = 1;
+ *retval = 1;
+
+ no_support:
+ if (cache)
+ ScriptFreeCache (&cache);
+ return ret;
+}
/* Check if font supports the otf script/language/features specified.
OTF_SPEC is in the format
@@ -696,7 +870,6 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
HDC context;
HFONT check_font, old_font;
int i, retval = 0;
- struct gcpro gcpro1;
/* Check the spec is in the right format. */
if (!CONSP (otf_spec) || XINT (Flength (otf_spec)) < 3)
@@ -716,6 +889,18 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
else
features[1] = XCAR (rest);
+ /* 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);
+
+ /* If we are on Vista or later, use the new APIs. */
+ if (uniscribe_new_apis
+ && !w32_disable_new_uniscribe_apis
+ && uniscribe_check_otf_1 (context, script, lang, features, &retval) != -1)
+ goto done;
+
/* Set up tags we will use in the search. */
feature_tables[0] = OTF_TAG ("GSUB");
feature_tables[1] = OTF_TAG ("GPOS");
@@ -727,16 +912,6 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
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);
-
- /* Everything else is contained within otf_spec so should get
- marked along with it. */
- GCPRO1 (otf_spec);
-
/* Scan GSUB and GPOS tables. */
for (i = 0; i < 2; i++)
{
@@ -745,6 +920,8 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
unsigned short script_table, langsys_table, n_langs;
unsigned short feature_index, n_features;
DWORD tbl = feature_tables[i];
+ DWORD feature_id, *ftags;
+ Lisp_Object farray[2];
/* Skip if no features requested from this table. */
if (NILP (features[i]))
@@ -811,51 +988,49 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
/* 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;
- for (rest = features[i]; CONSP (rest); rest = XCDR (rest))
- {
- Lisp_Object feature = XCAR (rest);
- if (!NILP (feature))
- n_match_features++;
- }
-
/* If there are no features to check, skip checking. */
- if (!n_match_features)
+ if (NILP (features[i]))
continue;
+ if (!CONSP (features[i]))
+ goto no_support;
- /* First check required feature (if any). */
+ n_match_features = 0;
+
+ /* First get required feature (if any). */
OTF_INT16_VAL (tbl, langsys_table + 2, &feature_index);
+ if (feature_index != 0xFFFF)
+ n_match_features = 1;
+ OTF_INT16_VAL (tbl, langsys_table + 4, &n_features);
+ n_match_features += n_features;
+ USE_SAFE_ALLOCA;
+ SAFE_NALLOCA (ftags, 1, n_match_features);
+ int k = 0;
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--;
+ OTF_DWORDTAG_VAL (tbl, feature_table + 2 + feature_index * 6,
+ &feature_id);
+ ftags[k++] = feature_id;
}
- /* Now check all the other features. */
- OTF_INT16_VAL (tbl, langsys_table + 4, &n_features);
+ /* Now get all the other 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--;
+ OTF_DWORDTAG_VAL (tbl, feature_table + 2 + feature_index * 6,
+ &feature_id);
+ ftags[k++] = feature_id;
}
- if (n_match_features > 0)
+ /* Check the features for this table. */
+ farray[0] = features[i];
+ farray[1] = Qnil;
+ if (!uniscribe_check_features (farray, ftags, n_match_features))
goto no_support;
+ SAFE_FREE ();
}
retval = 1;
+ done:
no_support:
font_table_error:
/* restore graphics context. */
@@ -867,7 +1042,7 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
}
static Lisp_Object
-otf_features (HDC context, char *table)
+otf_features (HDC context, const char *table)
{
Lisp_Object script_list = Qnil;
unsigned short scriptlist_table, n_scripts, feature_table;
@@ -879,7 +1054,7 @@ otf_features (HDC context, char *table)
OTF_INT16_VAL (tbl, 6, &feature_table);
OTF_INT16_VAL (tbl, scriptlist_table, &n_scripts);
- for (i = 0; i < n_scripts; i++)
+ for (i = n_scripts - 1; i >= 0; i--)
{
char script[5], lang[5];
unsigned short script_table, lang_count, langsys_table, feature_count;
@@ -904,7 +1079,7 @@ otf_features (HDC context, char *table)
langsys_tag = Qnil;
feature_list = Qnil;
OTF_INT16_VAL (tbl, langsys_table + 4, &feature_count);
- for (k = 0; k < feature_count; k++)
+ for (k = feature_count - 1; k >= 0; k--)
{
char feature[5];
unsigned short index;
@@ -919,7 +1094,7 @@ otf_features (HDC context, char *table)
/* List of supported languages. */
OTF_INT16_VAL (tbl, script_table + 2, &lang_count);
- for (j = 0; j < lang_count; j++)
+ for (j = lang_count - 1; j >= 0; j--)
{
record_offset = script_table + 4 + j * 6;
OTF_TAG_VAL (tbl, record_offset, lang);
@@ -931,7 +1106,7 @@ otf_features (HDC context, char *table)
langsys_tag = intern (lang);
feature_list = Qnil;
OTF_INT16_VAL (tbl, langsys_table + 4, &feature_count);
- for (k = 0; k < feature_count; k++)
+ for (k = feature_count - 1; k >= 0; k--)
{
char feature[5];
unsigned short index;
@@ -991,6 +1166,8 @@ struct font_driver uniscribe_font_driver =
/* 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 (void);
+
void
syms_of_w32uniscribe (void)
{
@@ -1009,4 +1186,17 @@ syms_of_w32uniscribe (void)
uniscribe_available = 1;
register_font_driver (&uniscribe_font_driver, NULL);
+
+ script_get_font_scripts_fn = (ScriptGetFontScriptTags_Proc)
+ GetProcAddress (uniscribe, "ScriptGetFontScriptTags");
+ script_get_font_languages_fn = (ScriptGetFontLanguageTags_Proc)
+ GetProcAddress (uniscribe, "ScriptGetFontLanguageTags");
+ script_get_font_features_fn = (ScriptGetFontFeatureTags_Proc)
+ GetProcAddress (uniscribe, "ScriptGetFontFeatureTags");
+ if (script_get_font_scripts_fn
+ && script_get_font_languages_fn
+ && script_get_font_features_fn)
+ uniscribe_new_apis = true;
+ else
+ uniscribe_new_apis = false;
}