aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEli Zaretskii2015-08-19 18:04:22 +0300
committerEli Zaretskii2015-08-19 18:04:22 +0300
commitae7cfd0baf24fda984ff4c0631bcaa477ea11b7f (patch)
tree9a3dae67498d68e75afbc92b6c7e2586b8ead738
parent7eed7399358faecd719febae4dc720ef2be41155 (diff)
downloademacs-ae7cfd0baf24fda984ff4c0631bcaa477ea11b7f.tar.gz
emacs-ae7cfd0baf24fda984ff4c0631bcaa477ea11b7f.zip
Improve and future-proof OTF fonts support in w32uniscribe.c
* src/w32uniscribe.c (uniscribe_otf_capability): Add commentary about the expected results and why the new Uniscribe APIs are not used in this function. (ScriptGetFontScriptTags_Proc, ScriptGetFontLanguageTags_Proc) (ScriptGetFontFeatureTags_Proc): New function typedefs. (uniscribe_new_apis): New static variable. (uniscribe_check_features): New function, implements OTF features verification while correctly accounting for features in the list after the nil member, if any. (uniscribe_check_otf_1): New function, retrieves the features supported by the font for the requested script and language using the Uniscribe APIs available from Windows Vista onwards. (uniscribe_check_otf): If the new Uniscribe APIs are available, use them in preference to reading the font data directly. Call uniscribe_check_features to verify that the requested features are supported, replacing the original incomplete code. (syms_of_w32uniscribe): Initialize function pointers for the new Uniscribe APIs. (Bug#21260) (otf_features): Scan the script, langsys, and feature arrays back to front, so that the result we return has them in alphabetical order, like ftfont.c does. * src/w32fns.c (syms_of_w32fns) <w32-disable-new-uniscribe-apis>: New variable for debugging w32uniscribe.c code.
-rw-r--r--src/w32fns.c10
-rw-r--r--src/w32uniscribe.c284
2 files changed, 253 insertions, 41 deletions
diff --git a/src/w32fns.c b/src/w32fns.c
index 189a27c62f1..e91097ba20e 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -9242,6 +9242,16 @@ Default is nil.
9242This variable has effect only on NT family of systems, not on Windows 9X. */); 9242This variable has effect only on NT family of systems, not on Windows 9X. */);
9243 w32_use_fallback_wm_chars_method = 0; 9243 w32_use_fallback_wm_chars_method = 0;
9244 9244
9245 DEFVAR_BOOL ("w32-disable-new-uniscribe-apis",
9246 w32_disable_new_uniscribe_apis,
9247 doc: /* Non-nil means don't use new Uniscribe APIs.
9248The new APIs are used to access OTF features supported by fonts.
9249This is intended only for debugging of the new Uniscribe-related code.
9250Default is nil.
9251
9252This variable has effect only on Windows Vista and later. */);
9253 w32_disable_new_uniscribe_apis = 0;
9254
9245#if 0 /* TODO: Port to W32 */ 9255#if 0 /* TODO: Port to W32 */
9246 defsubr (&Sx_change_window_property); 9256 defsubr (&Sx_change_window_property);
9247 defsubr (&Sx_delete_window_property); 9257 defsubr (&Sx_delete_window_property);
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index 73c0410c7b7..b1056bc104e 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -141,7 +141,26 @@ uniscribe_close (struct font *font)
141} 141}
142 142
143/* Return a list describing which scripts/languages FONT supports by 143/* Return a list describing which scripts/languages FONT supports by
144 which GSUB/GPOS features of OpenType tables. */ 144 which GSUB/GPOS features of OpenType tables.
145
146 Implementation note: otf_features called by this function uses
147 GetFontData to access the font tables directly, instead of using
148 ScriptGetFontScriptTags etc. APIs even if those are available. The
149 reason is that font-get, which uses the result of this function,
150 expects a cons cell (GSUB . GPOS) where the features are reported
151 separately for these 2 OTF tables, while the Uniscribe APIs report
152 the features as a single list. There doesn't seem to be a reason
153 for returning the features in 2 separate parts, except for
154 compatibility with libotf; the features are disjoint (each can
155 appear only in one of the 2 slots), and no client of this data
156 discerns between the two slots: the few that request this data all
157 look in both slots. If use of the Uniscribe APIs ever becomes
158 necessary here, and the 2 separate slots are still required, it
159 should be possible to split the feature list the APIs return into 2
160 because each sub-list is alphabetically sorted, so the place where
161 the sorting order breaks is where the GSUB features end and GPOS
162 features begin. But for now, this is not necessary, so we leave
163 the original code in place. */
145static Lisp_Object 164static Lisp_Object
146uniscribe_otf_capability (struct font *font) 165uniscribe_otf_capability (struct font *font)
147{ 166{
@@ -643,7 +662,7 @@ add_opentype_font_name_to_list (ENUMLOGFONTEX *logical_font,
643 662
644/* :otf property handling. 663/* :otf property handling.
645 Since the necessary Uniscribe APIs for getting font tag information 664 Since the necessary Uniscribe APIs for getting font tag information
646 are only available in Vista, we need to parse the font data directly 665 are only available in Vista, we may need to parse the font data directly
647 according to the OpenType Specification. */ 666 according to the OpenType Specification. */
648 667
649/* Push into DWORD backwards to cope with endianness. */ 668/* Push into DWORD backwards to cope with endianness. */
@@ -674,7 +693,171 @@ add_opentype_font_name_to_list (ENUMLOGFONTEX *logical_font,
674 STR[4] = '\0'; \ 693 STR[4] = '\0'; \
675 } while (0) 694 } while (0)
676 695
677#define SNAME(VAL) SDATA (SYMBOL_NAME (VAL)) 696#define SNAME(VAL) SSDATA (SYMBOL_NAME (VAL))
697
698/* Uniscribe APIs available only since Windows Vista. */
699typedef HRESULT (WINAPI *ScriptGetFontScriptTags_Proc)
700 (HDC, SCRIPT_CACHE *, SCRIPT_ANALYSIS *, int, OPENTYPE_TAG *, int *);
701
702typedef HRESULT (WINAPI *ScriptGetFontLanguageTags_Proc)
703 (HDC, SCRIPT_CACHE *, SCRIPT_ANALYSIS *, OPENTYPE_TAG, int, OPENTYPE_TAG *, int *);
704
705typedef HRESULT (WINAPI *ScriptGetFontFeatureTags_Proc)
706 (HDC, SCRIPT_CACHE *, SCRIPT_ANALYSIS *, OPENTYPE_TAG, OPENTYPE_TAG, int, OPENTYPE_TAG *, int *);
707
708ScriptGetFontScriptTags_Proc script_get_font_scripts_fn;
709ScriptGetFontLanguageTags_Proc script_get_font_languages_fn;
710ScriptGetFontFeatureTags_Proc script_get_font_features_fn;
711
712static bool uniscribe_new_apis;
713
714/* Verify that all the required features in FEATURES, each of whose
715 elements is a list or nil, can be found among the N feature tags in
716 FTAGS. Return 'true' if the required features are supported,
717 'false' if not. Each list in FEATURES can include an element of
718 nil, which means all the elements after it must not be in FTAGS. */
719static bool
720uniscribe_check_features (Lisp_Object features[2], OPENTYPE_TAG *ftags, int n)
721{
722 int j;
723
724 for (j = 0; j < 2; j++)
725 {
726 bool negative = false;
727 Lisp_Object rest;
728
729 for (rest = features[j]; CONSP (rest); rest = XCDR (rest))
730 {
731 Lisp_Object feature = XCAR (rest);
732
733 /* The font must NOT have any of the features after nil.
734 See the doc string of 'font-spec', under ':otf'. */
735 if (NILP (feature))
736 negative = true;
737 else
738 {
739 OPENTYPE_TAG feature_tag = OTF_TAG (SNAME (feature));
740 int i;
741
742 for (i = 0; i < n; i++)
743 {
744 if (ftags[i] == feature_tag)
745 {
746 /* Test fails if we find a feature that the font
747 must NOT have. */
748 if (negative)
749 return false;
750 break;
751 }
752 }
753
754 /* Test fails if we do NOT find a feature that the font
755 should have. */
756 if (i >= n && !negative)
757 return false;
758 }
759 }
760 }
761
762 return true;
763}
764
765/* Check if font supports the required OTF script/language/features
766 using the Unsicribe APIs available since Windows Vista. We prefer
767 these APIs as a kind of future-proofing Emacs: they seem to
768 retrieve script tags that the old code (and also libotf) doesn't
769 seem to be able to get, e.g., some fonts that claim support for
770 "dev2" script don't show "deva", but the new APIs do report it. */
771static int
772uniscribe_check_otf_1 (HDC context, Lisp_Object script, Lisp_Object lang,
773 Lisp_Object features[2], int *retval)
774{
775 SCRIPT_CACHE cache = NULL;
776 OPENTYPE_TAG tags[32], script_tag, lang_tag;
777 int max_tags = ARRAYELTS (tags);
778 int ntags, i, ret = 0;
779 HRESULT rslt;
780 Lisp_Object rest;
781
782 *retval = 0;
783
784 rslt = script_get_font_scripts_fn (context, &cache, NULL, max_tags,
785 tags, &ntags);
786 if (FAILED (rslt))
787 {
788 DebPrint (("ScriptGetFontScriptTags failed with 0x%x\n", rslt));
789 ret = -1;
790 goto no_support;
791 }
792 if (NILP (script))
793 script_tag = OTF_TAG ("DFLT");
794 else
795 script_tag = OTF_TAG (SNAME (script));
796 for (i = 0; i < ntags; i++)
797 if (tags[i] == script_tag)
798 break;
799
800 if (i >= ntags)
801 goto no_support;
802
803 if (NILP (lang))
804 lang_tag = OTF_TAG ("dflt");
805 else
806 {
807 rslt = script_get_font_languages_fn (context, &cache, NULL, script_tag,
808 max_tags, tags, &ntags);
809 if (FAILED (rslt))
810 {
811 DebPrint (("ScriptGetFontLanguageTags failed with 0x%x\n", rslt));
812 ret = -1;
813 goto no_support;
814 }
815 if (ntags == 0)
816 lang_tag = OTF_TAG ("dflt");
817 else
818 {
819 lang_tag = OTF_TAG (SNAME (lang));
820 for (i = 0; i < ntags; i++)
821 if (tags[i] == lang_tag)
822 break;
823
824 if (i >= ntags)
825 goto no_support;
826 }
827 }
828
829 if (!NILP (features[0]))
830 {
831 /* Are the 2 feature lists valid? */
832 if (!CONSP (features[0])
833 || (!NILP (features[1]) && !CONSP (features[1])))
834 goto no_support;
835 rslt = script_get_font_features_fn (context, &cache, NULL,
836 script_tag, lang_tag,
837 max_tags, tags, &ntags);
838 if (FAILED (rslt))
839 {
840 DebPrint (("ScriptGetFontFeatureTags failed with 0x%x\n", rslt));
841 ret = -1;
842 goto no_support;
843 }
844
845 /* ScriptGetFontFeatureTags doesn't let us query features
846 separately for GSUB and GPOS, so we check them all together.
847 It doesn't really matter, since the features in GSUB and GPOS
848 are disjoint, i.e. no feature can appear in both tables. */
849 if (!uniscribe_check_features (features, tags, ntags))
850 goto no_support;
851 }
852
853 ret = 1;
854 *retval = 1;
855
856 no_support:
857 if (cache)
858 ScriptFreeCache (&cache);
859 return ret;
860}
678 861
679/* Check if font supports the otf script/language/features specified. 862/* Check if font supports the otf script/language/features specified.
680 OTF_SPEC is in the format 863 OTF_SPEC is in the format
@@ -710,6 +893,18 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
710 else 893 else
711 features[1] = XCAR (rest); 894 features[1] = XCAR (rest);
712 895
896 /* Set up graphics context so we can use the font. */
897 f = XFRAME (selected_frame);
898 context = get_frame_dc (f);
899 check_font = CreateFontIndirect (font);
900 old_font = SelectObject (context, check_font);
901
902 /* If we are on Vista or later, use the new APIs. */
903 if (uniscribe_new_apis
904 && !w32_disable_new_uniscribe_apis
905 && uniscribe_check_otf_1 (context, script, lang, features, &retval) != -1)
906 goto done;
907
713 /* Set up tags we will use in the search. */ 908 /* Set up tags we will use in the search. */
714 feature_tables[0] = OTF_TAG ("GSUB"); 909 feature_tables[0] = OTF_TAG ("GSUB");
715 feature_tables[1] = OTF_TAG ("GPOS"); 910 feature_tables[1] = OTF_TAG ("GPOS");
@@ -721,12 +916,6 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
721 if (!NILP (lang)) 916 if (!NILP (lang))
722 lang_tag = OTF_TAG (SNAME (lang)); 917 lang_tag = OTF_TAG (SNAME (lang));
723 918
724 /* Set up graphics context so we can use the font. */
725 f = XFRAME (selected_frame);
726 context = get_frame_dc (f);
727 check_font = CreateFontIndirect (font);
728 old_font = SelectObject (context, check_font);
729
730 /* Everything else is contained within otf_spec so should get 919 /* Everything else is contained within otf_spec so should get
731 marked along with it. */ 920 marked along with it. */
732 GCPRO1 (otf_spec); 921 GCPRO1 (otf_spec);
@@ -739,6 +928,8 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
739 unsigned short script_table, langsys_table, n_langs; 928 unsigned short script_table, langsys_table, n_langs;
740 unsigned short feature_index, n_features; 929 unsigned short feature_index, n_features;
741 DWORD tbl = feature_tables[i]; 930 DWORD tbl = feature_tables[i];
931 DWORD feature_id, *ftags;
932 Lisp_Object farray[2];
742 933
743 /* Skip if no features requested from this table. */ 934 /* Skip if no features requested from this table. */
744 if (NILP (features[i])) 935 if (NILP (features[i]))
@@ -805,51 +996,49 @@ uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
805 /* Offset is from beginning of script table. */ 996 /* Offset is from beginning of script table. */
806 langsys_table += script_table; 997 langsys_table += script_table;
807 998
808 /* Check the features. Features may contain nil according to
809 documentation in font_prop_validate_otf, so count them. */
810 n_match_features = 0;
811 for (rest = features[i]; CONSP (rest); rest = XCDR (rest))
812 {
813 Lisp_Object feature = XCAR (rest);
814 if (!NILP (feature))
815 n_match_features++;
816 }
817
818 /* If there are no features to check, skip checking. */ 999 /* If there are no features to check, skip checking. */
819 if (!n_match_features) 1000 if (NILP (features[i]))
820 continue; 1001 continue;
1002 if (!CONSP (features[i]))
1003 goto no_support;
1004
1005 n_match_features = 0;
821 1006
822 /* First check required feature (if any). */ 1007 /* First get required feature (if any). */
823 OTF_INT16_VAL (tbl, langsys_table + 2, &feature_index); 1008 OTF_INT16_VAL (tbl, langsys_table + 2, &feature_index);
824 if (feature_index != 0xFFFF) 1009 if (feature_index != 0xFFFF)
1010 n_match_features = 1;
1011 OTF_INT16_VAL (tbl, langsys_table + 4, &n_features);
1012 n_match_features += n_features;
1013 USE_SAFE_ALLOCA;
1014 SAFE_NALLOCA (ftags, 1, n_match_features);
1015 int k = 0;
1016 if (feature_index != 0xFFFF)
825 { 1017 {
826 char feature_id[5]; 1018 OTF_DWORDTAG_VAL (tbl, feature_table + 2 + feature_index * 6,
827 OTF_TAG_VAL (tbl, feature_table + 2 + feature_index * 6, feature_id); 1019 &feature_id);
828 OTF_TAG_VAL (tbl, feature_table + 2 + feature_index * 6, feature_id); 1020 ftags[k++] = feature_id;
829 /* Assume no duplicates in the font table. This allows us to mark
830 the features off by simply decrementing a counter. */
831 if (!NILP (Fmemq (intern (feature_id), features[i])))
832 n_match_features--;
833 } 1021 }
834 /* Now check all the other features. */ 1022 /* Now get all the other features. */
835 OTF_INT16_VAL (tbl, langsys_table + 4, &n_features);
836 for (j = 0; j < n_features; j++) 1023 for (j = 0; j < n_features; j++)
837 { 1024 {
838 char feature_id[5];
839 OTF_INT16_VAL (tbl, langsys_table + 6 + j * 2, &feature_index); 1025 OTF_INT16_VAL (tbl, langsys_table + 6 + j * 2, &feature_index);
840 OTF_TAG_VAL (tbl, feature_table + 2 + feature_index * 6, feature_id); 1026 OTF_DWORDTAG_VAL (tbl, feature_table + 2 + feature_index * 6,
841 /* Assume no duplicates in the font table. This allows us to mark 1027 &feature_id);
842 the features off by simply decrementing a counter. */ 1028 ftags[k++] = feature_id;
843 if (!NILP (Fmemq (intern (feature_id), features[i])))
844 n_match_features--;
845 } 1029 }
846 1030
847 if (n_match_features > 0) 1031 /* Check the features for this table. */
1032 farray[0] = features[i];
1033 farray[1] = Qnil;
1034 if (!uniscribe_check_features (farray, ftags, n_match_features))
848 goto no_support; 1035 goto no_support;
1036 SAFE_FREE ();
849 } 1037 }
850 1038
851 retval = 1; 1039 retval = 1;
852 1040
1041 done:
853 no_support: 1042 no_support:
854 font_table_error: 1043 font_table_error:
855 /* restore graphics context. */ 1044 /* restore graphics context. */
@@ -873,7 +1062,7 @@ otf_features (HDC context, char *table)
873 OTF_INT16_VAL (tbl, 6, &feature_table); 1062 OTF_INT16_VAL (tbl, 6, &feature_table);
874 OTF_INT16_VAL (tbl, scriptlist_table, &n_scripts); 1063 OTF_INT16_VAL (tbl, scriptlist_table, &n_scripts);
875 1064
876 for (i = 0; i < n_scripts; i++) 1065 for (i = n_scripts - 1; i >= 0; i--)
877 { 1066 {
878 char script[5], lang[5]; 1067 char script[5], lang[5];
879 unsigned short script_table, lang_count, langsys_table, feature_count; 1068 unsigned short script_table, lang_count, langsys_table, feature_count;
@@ -898,7 +1087,7 @@ otf_features (HDC context, char *table)
898 langsys_tag = Qnil; 1087 langsys_tag = Qnil;
899 feature_list = Qnil; 1088 feature_list = Qnil;
900 OTF_INT16_VAL (tbl, langsys_table + 4, &feature_count); 1089 OTF_INT16_VAL (tbl, langsys_table + 4, &feature_count);
901 for (k = 0; k < feature_count; k++) 1090 for (k = feature_count - 1; k >= 0; k--)
902 { 1091 {
903 char feature[5]; 1092 char feature[5];
904 unsigned short index; 1093 unsigned short index;
@@ -913,7 +1102,7 @@ otf_features (HDC context, char *table)
913 /* List of supported languages. */ 1102 /* List of supported languages. */
914 OTF_INT16_VAL (tbl, script_table + 2, &lang_count); 1103 OTF_INT16_VAL (tbl, script_table + 2, &lang_count);
915 1104
916 for (j = 0; j < lang_count; j++) 1105 for (j = lang_count - 1; j >= 0; j--)
917 { 1106 {
918 record_offset = script_table + 4 + j * 6; 1107 record_offset = script_table + 4 + j * 6;
919 OTF_TAG_VAL (tbl, record_offset, lang); 1108 OTF_TAG_VAL (tbl, record_offset, lang);
@@ -925,7 +1114,7 @@ otf_features (HDC context, char *table)
925 langsys_tag = intern (lang); 1114 langsys_tag = intern (lang);
926 feature_list = Qnil; 1115 feature_list = Qnil;
927 OTF_INT16_VAL (tbl, langsys_table + 4, &feature_count); 1116 OTF_INT16_VAL (tbl, langsys_table + 4, &feature_count);
928 for (k = 0; k < feature_count; k++) 1117 for (k = feature_count - 1; k >= 0; k--)
929 { 1118 {
930 char feature[5]; 1119 char feature[5];
931 unsigned short index; 1120 unsigned short index;
@@ -1003,4 +1192,17 @@ syms_of_w32uniscribe (void)
1003 uniscribe_available = 1; 1192 uniscribe_available = 1;
1004 1193
1005 register_font_driver (&uniscribe_font_driver, NULL); 1194 register_font_driver (&uniscribe_font_driver, NULL);
1195
1196 script_get_font_scripts_fn = (ScriptGetFontScriptTags_Proc)
1197 GetProcAddress (uniscribe, "ScriptGetFontScriptTags");
1198 script_get_font_languages_fn = (ScriptGetFontLanguageTags_Proc)
1199 GetProcAddress (uniscribe, "ScriptGetFontLanguageTags");
1200 script_get_font_features_fn = (ScriptGetFontFeatureTags_Proc)
1201 GetProcAddress (uniscribe, "ScriptGetFontFeatureTags");
1202 if (script_get_font_scripts_fn
1203 && script_get_font_languages_fn
1204 && script_get_font_features_fn)
1205 uniscribe_new_apis = true;
1206 else
1207 uniscribe_new_apis = false;
1006} 1208}