2 * Methods for dealing with opentype font tables
4 * Copyright 2014 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define NONAMELESSUNION
25 #include "dwrite_private.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
30 #define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d')
31 #define MS_HHEA_TAG DWRITE_MAKE_OPENTYPE_TAG('h','h','e','a')
32 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
33 #define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2')
34 #define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t')
35 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
36 #define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
37 #define MS_NAME_TAG DWRITE_MAKE_OPENTYPE_TAG('n','a','m','e')
38 #define MS_GLYF_TAG DWRITE_MAKE_OPENTYPE_TAG('g','l','y','f')
39 #define MS_CFF__TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F',' ')
40 #define MS_CFF2_TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F','2')
41 #define MS_CPAL_TAG DWRITE_MAKE_OPENTYPE_TAG('C','P','A','L')
42 #define MS_COLR_TAG DWRITE_MAKE_OPENTYPE_TAG('C','O','L','R')
43 #define MS_SVG__TAG DWRITE_MAKE_OPENTYPE_TAG('S','V','G',' ')
44 #define MS_SBIX_TAG DWRITE_MAKE_OPENTYPE_TAG('s','b','i','x')
45 #define MS_MAXP_TAG DWRITE_MAKE_OPENTYPE_TAG('m','a','x','p')
46 #define MS_CBLC_TAG DWRITE_MAKE_OPENTYPE_TAG('C','B','L','C')
47 #define MS_CMAP_TAG DWRITE_MAKE_OPENTYPE_TAG('c','m','a','p')
48 #define MS_META_TAG DWRITE_MAKE_OPENTYPE_TAG('m','e','t','a')
51 #define MS_PNG__TAG DWRITE_MAKE_OPENTYPE_TAG('p','n','g',' ')
52 #define MS_JPG__TAG DWRITE_MAKE_OPENTYPE_TAG('j','p','g',' ')
53 #define MS_TIFF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','i','f','f')
55 #define MS_WOFF_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','F')
56 #define MS_WOF2_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','2')
59 #define MS_DLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('d','l','n','g')
60 #define MS_SLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('s','l','n','g')
62 #ifdef WORDS_BIGENDIAN
63 #define GET_BE_WORD(x) (x)
64 #define GET_BE_DWORD(x) (x)
66 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
67 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
70 #define GLYPH_CONTEXT_MAX_LENGTH 64
71 #define SHAPE_MAX_NESTING_LEVEL 6
95 struct cmap_encoding_record
106 struct cmap_encoding_record tables
[1];
109 enum OPENTYPE_CMAP_TABLE_FORMAT
111 OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING
= 4,
112 OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE
= 12
115 enum opentype_cmap_table_platform
117 OPENTYPE_CMAP_TABLE_PLATFORM_WIN
= 3,
120 enum opentype_cmap_table_encoding
122 OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL
= 0,
123 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_BMP
= 1,
124 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_FULL
= 10,
127 /* PANOSE is 10 bytes in size, need to pack the structure properly */
128 #include "pshpack2.h"
145 USHORT lowestRecPPEM
;
146 SHORT direction_hint
;
148 SHORT glyphdata_format
;
151 enum tt_head_macstyle
153 TT_HEAD_MACSTYLE_BOLD
= 1 << 0,
154 TT_HEAD_MACSTYLE_ITALIC
= 1 << 1,
155 TT_HEAD_MACSTYLE_UNDERLINE
= 1 << 2,
156 TT_HEAD_MACSTYLE_OUTLINE
= 1 << 3,
157 TT_HEAD_MACSTYLE_SHADOW
= 1 << 4,
158 TT_HEAD_MACSTYLE_CONDENSED
= 1 << 5,
159 TT_HEAD_MACSTYLE_EXTENDED
= 1 << 6,
166 SHORT underlinePosition
;
167 SHORT underlineThickness
;
179 USHORT usWeightClass
;
182 SHORT ySubscriptXSize
;
183 SHORT ySubscriptYSize
;
184 SHORT ySubscriptXOffset
;
185 SHORT ySubscriptYOffset
;
186 SHORT ySuperscriptXSize
;
187 SHORT ySuperscriptYSize
;
188 SHORT ySuperscriptXOffset
;
189 SHORT ySuperscriptYOffset
;
190 SHORT yStrikeoutSize
;
191 SHORT yStrikeoutPosition
;
194 ULONG ulUnicodeRange1
;
195 ULONG ulUnicodeRange2
;
196 ULONG ulUnicodeRange3
;
197 ULONG ulUnicodeRange4
;
200 USHORT usFirstCharIndex
;
201 USHORT usLastCharIndex
;
202 /* According to the Apple spec, original version didn't have the below fields,
203 * version numbers were taken from the OpenType spec.
205 /* version 0 (TrueType 1.5) */
206 USHORT sTypoAscender
;
207 USHORT sTypoDescender
;
211 /* version 1 (TrueType 1.66) */
212 ULONG ulCodePageRange1
;
213 ULONG ulCodePageRange2
;
214 /* version 2 (OpenType 1.2) */
217 USHORT usDefaultChar
;
229 USHORT advanceWidthMax
;
230 SHORT minLeftSideBearing
;
231 SHORT minRightSideBearing
;
233 SHORT caretSlopeRise
;
237 SHORT metricDataFormat
;
238 USHORT numberOfHMetrics
;
246 DWORD strike_offset
[1];
253 DWORD glyphdata_offsets
[1];
256 struct sbix_glyph_data
281 struct cblc_bitmapsize_table
283 DWORD indexSubTableArrayOffset
;
284 DWORD indexTablesSize
;
285 DWORD numberofIndexSubTables
;
287 sbitLineMetrics hori
;
288 sbitLineMetrics vert
;
289 WORD startGlyphIndex
;
307 struct gasp_range ranges
[1];
310 enum OS2_FSSELECTION
{
311 OS2_FSSELECTION_ITALIC
= 1 << 0,
312 OS2_FSSELECTION_UNDERSCORE
= 1 << 1,
313 OS2_FSSELECTION_NEGATIVE
= 1 << 2,
314 OS2_FSSELECTION_OUTLINED
= 1 << 3,
315 OS2_FSSELECTION_STRIKEOUT
= 1 << 4,
316 OS2_FSSELECTION_BOLD
= 1 << 5,
317 OS2_FSSELECTION_REGULAR
= 1 << 6,
318 OS2_FSSELECTION_USE_TYPO_METRICS
= 1 << 7,
319 OS2_FSSELECTION_WWS
= 1 << 8,
320 OS2_FSSELECTION_OBLIQUE
= 1 << 9
336 TT_NameRecord nameRecord
[1];
366 struct vdmx_vtable entries
[1];
369 struct ot_feature_record
375 struct ot_feature_list
378 struct ot_feature_record features
[1];
383 WORD lookup_order
; /* Reserved */
384 WORD required_feature_index
;
386 WORD feature_index
[1];
389 struct ot_langsys_record
397 WORD default_langsys
;
399 struct ot_langsys_record langsys
[1];
402 struct ot_script_record
408 struct ot_script_list
411 struct ot_script_record scripts
[1];
416 GDEF_CLASS_UNCLASSIFIED
= 0,
418 GDEF_CLASS_LIGATURE
= 2,
420 GDEF_CLASS_COMPONENT
= 4,
421 GDEF_CLASS_MAX
= GDEF_CLASS_COMPONENT
,
429 UINT16 ligcaret_list
;
430 UINT16 markattach_classdef
;
431 UINT16 markglyphsetdef
;
434 struct ot_gdef_classdef_format1
442 struct ot_gdef_class_range
449 struct ot_gdef_classdef_format2
453 struct ot_gdef_class_range ranges
[1];
456 struct gpos_gsub_header
464 enum gsub_gpos_lookup_flags
466 LOOKUP_FLAG_RTL
= 0x1, /* Only used for GPOS cursive attachments. */
468 LOOKUP_FLAG_IGNORE_BASE
= 0x2,
469 LOOKUP_FLAG_IGNORE_LIGATURES
= 0x4,
470 LOOKUP_FLAG_IGNORE_MARKS
= 0x8,
471 LOOKUP_FLAG_IGNORE_MASK
= 0xe,
473 LOOKUP_FLAG_USE_MARK_FILTERING_SET
= 0x10,
474 LOOKUP_FLAG_MARK_ATTACHMENT_TYPE
= 0xff00,
477 enum glyph_prop_flags
479 GLYPH_PROP_BASE
= LOOKUP_FLAG_IGNORE_BASE
,
480 GLYPH_PROP_LIGATURE
= LOOKUP_FLAG_IGNORE_LIGATURES
,
481 GLYPH_PROP_MARK
= LOOKUP_FLAG_IGNORE_MARKS
,
484 enum gpos_lookup_type
486 GPOS_LOOKUP_SINGLE_ADJUSTMENT
= 1,
487 GPOS_LOOKUP_PAIR_ADJUSTMENT
= 2,
488 GPOS_LOOKUP_CURSIVE_ATTACHMENT
= 3,
489 GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT
= 4,
490 GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT
= 5,
491 GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT
= 6,
492 GPOS_LOOKUP_CONTEXTUAL_POSITION
= 7,
493 GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION
= 8,
494 GPOS_LOOKUP_EXTENSION_POSITION
= 9,
497 enum gsub_lookup_type
499 GSUB_LOOKUP_SINGLE_SUBST
= 1,
500 GSUB_LOOKUP_MULTIPLE_SUBST
= 2,
501 GSUB_LOOKUP_ALTERNATE_SUBST
= 3,
502 GSUB_LOOKUP_LIGATURE_SUBST
= 4,
503 GSUB_LOOKUP_CONTEXTUAL_SUBST
= 5,
504 GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST
= 6,
505 GSUB_LOOKUP_EXTENSION_SUBST
= 7,
506 GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST
= 8,
509 enum gpos_value_format
511 GPOS_VALUE_X_PLACEMENT
= 0x1,
512 GPOS_VALUE_Y_PLACEMENT
= 0x2,
513 GPOS_VALUE_X_ADVANCE
= 0x4,
514 GPOS_VALUE_Y_ADVANCE
= 0x8,
515 GPOS_VALUE_X_PLACEMENT_DEVICE
= 0x10,
516 GPOS_VALUE_Y_PLACEMENT_DEVICE
= 0x20,
517 GPOS_VALUE_X_ADVANCE_DEVICE
= 0x40,
518 GPOS_VALUE_Y_ADVANCE_DEVICE
= 0x80,
521 enum OPENTYPE_PLATFORM_ID
523 OPENTYPE_PLATFORM_UNICODE
= 0,
524 OPENTYPE_PLATFORM_MAC
,
525 OPENTYPE_PLATFORM_ISO
,
526 OPENTYPE_PLATFORM_WIN
,
527 OPENTYPE_PLATFORM_CUSTOM
530 struct ot_gsubgpos_extension_format1
534 DWORD extension_offset
;
537 struct ot_gsub_singlesubst_format1
544 struct ot_gsub_singlesubst_format2
549 UINT16 substitutes
[1];
552 struct ot_gsub_multsubst_format1
560 struct ot_gsub_altsubst_format1
568 struct ot_gsub_ligsubst_format1
572 UINT16 lig_set_count
;
576 struct ot_gsub_ligset
586 UINT16 components
[1];
589 struct ot_gsubgpos_context_format1
593 UINT16 ruleset_count
;
597 struct ot_gsubgpos_ruleset
607 WORD lookuplist_index
[1];
610 struct ot_lookup_list
616 struct ot_lookup_table
624 #define GLYPH_NOT_COVERED (~0u)
626 struct ot_coverage_format1
633 struct ot_coverage_range
637 WORD startcoverage_index
;
640 struct ot_coverage_format2
644 struct ot_coverage_range ranges
[1];
647 struct ot_gpos_device_table
655 struct ot_gpos_singlepos_format1
663 struct ot_gpos_singlepos_format2
672 struct ot_gpos_pairvalue
678 struct ot_gpos_pairset
680 WORD pairvalue_count
;
681 struct ot_gpos_pairvalue pairvalues
[1];
684 struct ot_gpos_pairpos_format1
694 struct ot_gpos_pairpos_format2
707 struct ot_gpos_anchor_format1
714 struct ot_gpos_anchor_format2
722 struct ot_gpos_anchor_format3
731 struct ot_gpos_cursive_format1
739 struct ot_gpos_mark_record
745 struct ot_gpos_mark_array
748 struct ot_gpos_mark_record records
[1];
751 struct ot_gpos_base_array
757 struct ot_gpos_mark_to_base_format1
762 WORD mark_class_count
;
767 struct ot_gpos_mark_to_lig_format1
772 WORD mark_class_count
;
777 struct ot_gpos_mark_to_mark_format1
782 WORD mark_class_count
;
791 } GSUB_SingleSubstFormat1
;
798 } GSUB_SingleSubstFormat2
;
802 WORD ExtensionLookupType
;
803 DWORD ExtensionOffset
;
804 } GSUB_ExtensionPosFormat1
;
808 enum TT_NAME_WINDOWS_ENCODING_ID
810 TT_NAME_WINDOWS_ENCODING_SYMBOL
= 0,
811 TT_NAME_WINDOWS_ENCODING_UNICODE_BMP
,
812 TT_NAME_WINDOWS_ENCODING_SJIS
,
813 TT_NAME_WINDOWS_ENCODING_PRC
,
814 TT_NAME_WINDOWS_ENCODING_BIG5
,
815 TT_NAME_WINDOWS_ENCODING_WANSUNG
,
816 TT_NAME_WINDOWS_ENCODING_JOHAB
,
817 TT_NAME_WINDOWS_ENCODING_RESERVED1
,
818 TT_NAME_WINDOWS_ENCODING_RESERVED2
,
819 TT_NAME_WINDOWS_ENCODING_RESERVED3
,
820 TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
823 enum TT_NAME_MAC_ENCODING_ID
825 TT_NAME_MAC_ENCODING_ROMAN
= 0,
826 TT_NAME_MAC_ENCODING_JAPANESE
,
827 TT_NAME_MAC_ENCODING_TRAD_CHINESE
,
828 TT_NAME_MAC_ENCODING_KOREAN
,
829 TT_NAME_MAC_ENCODING_ARABIC
,
830 TT_NAME_MAC_ENCODING_HEBREW
,
831 TT_NAME_MAC_ENCODING_GREEK
,
832 TT_NAME_MAC_ENCODING_RUSSIAN
,
833 TT_NAME_MAC_ENCODING_RSYMBOL
,
834 TT_NAME_MAC_ENCODING_DEVANAGARI
,
835 TT_NAME_MAC_ENCODING_GURMUKHI
,
836 TT_NAME_MAC_ENCODING_GUJARATI
,
837 TT_NAME_MAC_ENCODING_ORIYA
,
838 TT_NAME_MAC_ENCODING_BENGALI
,
839 TT_NAME_MAC_ENCODING_TAMIL
,
840 TT_NAME_MAC_ENCODING_TELUGU
,
841 TT_NAME_MAC_ENCODING_KANNADA
,
842 TT_NAME_MAC_ENCODING_MALAYALAM
,
843 TT_NAME_MAC_ENCODING_SINHALESE
,
844 TT_NAME_MAC_ENCODING_BURMESE
,
845 TT_NAME_MAC_ENCODING_KHMER
,
846 TT_NAME_MAC_ENCODING_THAI
,
847 TT_NAME_MAC_ENCODING_LAOTIAN
,
848 TT_NAME_MAC_ENCODING_GEORGIAN
,
849 TT_NAME_MAC_ENCODING_ARMENIAN
,
850 TT_NAME_MAC_ENCODING_SIMPL_CHINESE
,
851 TT_NAME_MAC_ENCODING_TIBETAN
,
852 TT_NAME_MAC_ENCODING_MONGOLIAN
,
853 TT_NAME_MAC_ENCODING_GEEZ
,
854 TT_NAME_MAC_ENCODING_SLAVIC
,
855 TT_NAME_MAC_ENCODING_VIETNAMESE
,
856 TT_NAME_MAC_ENCODING_SINDHI
,
857 TT_NAME_MAC_ENCODING_UNINTERPRETED
860 enum TT_NAME_MAC_LANGUAGE_ID
862 TT_NAME_MAC_LANGID_ENGLISH
= 0,
863 TT_NAME_MAC_LANGID_FRENCH
,
864 TT_NAME_MAC_LANGID_GERMAN
,
865 TT_NAME_MAC_LANGID_ITALIAN
,
866 TT_NAME_MAC_LANGID_DUTCH
,
867 TT_NAME_MAC_LANGID_SWEDISH
,
868 TT_NAME_MAC_LANGID_SPANISH
,
869 TT_NAME_MAC_LANGID_DANISH
,
870 TT_NAME_MAC_LANGID_PORTUGUESE
,
871 TT_NAME_MAC_LANGID_NORWEGIAN
,
872 TT_NAME_MAC_LANGID_HEBREW
,
873 TT_NAME_MAC_LANGID_JAPANESE
,
874 TT_NAME_MAC_LANGID_ARABIC
,
875 TT_NAME_MAC_LANGID_FINNISH
,
876 TT_NAME_MAC_LANGID_GREEK
,
877 TT_NAME_MAC_LANGID_ICELANDIC
,
878 TT_NAME_MAC_LANGID_MALTESE
,
879 TT_NAME_MAC_LANGID_TURKISH
,
880 TT_NAME_MAC_LANGID_CROATIAN
,
881 TT_NAME_MAC_LANGID_TRAD_CHINESE
,
882 TT_NAME_MAC_LANGID_URDU
,
883 TT_NAME_MAC_LANGID_HINDI
,
884 TT_NAME_MAC_LANGID_THAI
,
885 TT_NAME_MAC_LANGID_KOREAN
,
886 TT_NAME_MAC_LANGID_LITHUANIAN
,
887 TT_NAME_MAC_LANGID_POLISH
,
888 TT_NAME_MAC_LANGID_HUNGARIAN
,
889 TT_NAME_MAC_LANGID_ESTONIAN
,
890 TT_NAME_MAC_LANGID_LATVIAN
,
891 TT_NAME_MAC_LANGID_SAMI
,
892 TT_NAME_MAC_LANGID_FAROESE
,
893 TT_NAME_MAC_LANGID_FARSI
,
894 TT_NAME_MAC_LANGID_RUSSIAN
,
895 TT_NAME_MAC_LANGID_SIMPL_CHINESE
,
896 TT_NAME_MAC_LANGID_FLEMISH
,
897 TT_NAME_MAC_LANGID_GAELIC
,
898 TT_NAME_MAC_LANGID_ALBANIAN
,
899 TT_NAME_MAC_LANGID_ROMANIAN
,
900 TT_NAME_MAC_LANGID_CZECH
,
901 TT_NAME_MAC_LANGID_SLOVAK
,
902 TT_NAME_MAC_LANGID_SLOVENIAN
,
903 TT_NAME_MAC_LANGID_YIDDISH
,
904 TT_NAME_MAC_LANGID_SERBIAN
,
905 TT_NAME_MAC_LANGID_MACEDONIAN
,
906 TT_NAME_MAC_LANGID_BULGARIAN
,
907 TT_NAME_MAC_LANGID_UKRAINIAN
,
908 TT_NAME_MAC_LANGID_BYELORUSSIAN
,
909 TT_NAME_MAC_LANGID_UZBEK
,
910 TT_NAME_MAC_LANGID_KAZAKH
,
911 TT_NAME_MAC_LANGID_AZERB_CYR
,
912 TT_NAME_MAC_LANGID_AZERB_ARABIC
,
913 TT_NAME_MAC_LANGID_ARMENIAN
,
914 TT_NAME_MAC_LANGID_GEORGIAN
,
915 TT_NAME_MAC_LANGID_MOLDAVIAN
,
916 TT_NAME_MAC_LANGID_KIRGHIZ
,
917 TT_NAME_MAC_LANGID_TAJIKI
,
918 TT_NAME_MAC_LANGID_TURKMEN
,
919 TT_NAME_MAC_LANGID_MONGOLIAN
,
920 TT_NAME_MAC_LANGID_MONGOLIAN_CYR
,
921 TT_NAME_MAC_LANGID_PASHTO
,
922 TT_NAME_MAC_LANGID_KURDISH
,
923 TT_NAME_MAC_LANGID_KASHMIRI
,
924 TT_NAME_MAC_LANGID_SINDHI
,
925 TT_NAME_MAC_LANGID_TIBETAN
,
926 TT_NAME_MAC_LANGID_NEPALI
,
927 TT_NAME_MAC_LANGID_SANSKRIT
,
928 TT_NAME_MAC_LANGID_MARATHI
,
929 TT_NAME_MAC_LANGID_BENGALI
,
930 TT_NAME_MAC_LANGID_ASSAMESE
,
931 TT_NAME_MAC_LANGID_GUJARATI
,
932 TT_NAME_MAC_LANGID_PUNJABI
,
933 TT_NAME_MAC_LANGID_ORIYA
,
934 TT_NAME_MAC_LANGID_MALAYALAM
,
935 TT_NAME_MAC_LANGID_KANNADA
,
936 TT_NAME_MAC_LANGID_TAMIL
,
937 TT_NAME_MAC_LANGID_TELUGU
,
938 TT_NAME_MAC_LANGID_SINHALESE
,
939 TT_NAME_MAC_LANGID_BURMESE
,
940 TT_NAME_MAC_LANGID_KHMER
,
941 TT_NAME_MAC_LANGID_LAO
,
942 TT_NAME_MAC_LANGID_VIETNAMESE
,
943 TT_NAME_MAC_LANGID_INDONESIAN
,
944 TT_NAME_MAC_LANGID_TAGALOG
,
945 TT_NAME_MAC_LANGID_MALAY_ROMAN
,
946 TT_NAME_MAC_LANGID_MALAY_ARABIC
,
947 TT_NAME_MAC_LANGID_AMHARIC
,
948 TT_NAME_MAC_LANGID_TIGRINYA
,
949 TT_NAME_MAC_LANGID_GALLA
,
950 TT_NAME_MAC_LANGID_SOMALI
,
951 TT_NAME_MAC_LANGID_SWAHILI
,
952 TT_NAME_MAC_LANGID_KINYARWANDA
,
953 TT_NAME_MAC_LANGID_RUNDI
,
954 TT_NAME_MAC_LANGID_NYANJA
,
955 TT_NAME_MAC_LANGID_MALAGASY
,
956 TT_NAME_MAC_LANGID_ESPERANTO
,
957 TT_NAME_MAC_LANGID_WELSH
= 128,
958 TT_NAME_MAC_LANGID_BASQUE
,
959 TT_NAME_MAC_LANGID_CATALAN
,
960 TT_NAME_MAC_LANGID_LATIN
,
961 TT_NAME_MAC_LANGID_QUECHUA
,
962 TT_NAME_MAC_LANGID_GUARANI
,
963 TT_NAME_MAC_LANGID_AYMARA
,
964 TT_NAME_MAC_LANGID_TATAR
,
965 TT_NAME_MAC_LANGID_UIGHUR
,
966 TT_NAME_MAC_LANGID_DZONGKHA
,
967 TT_NAME_MAC_LANGID_JAVANESE
,
968 TT_NAME_MAC_LANGID_SUNDANESE
,
969 TT_NAME_MAC_LANGID_GALICIAN
,
970 TT_NAME_MAC_LANGID_AFRIKAANS
,
971 TT_NAME_MAC_LANGID_BRETON
,
972 TT_NAME_MAC_LANGID_INUKTITUT
,
973 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC
,
974 TT_NAME_MAC_LANGID_MANX_GAELIC
,
975 TT_NAME_MAC_LANGID_IRISH_GAELIC
,
976 TT_NAME_MAC_LANGID_TONGAN
,
977 TT_NAME_MAC_LANGID_GREEK_POLYTONIC
,
978 TT_NAME_MAC_LANGID_GREENLANDIC
,
979 TT_NAME_MAC_LANGID_AZER_ROMAN
982 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
983 static const char name_mac_langid_to_locale
[][10] = {
1137 enum OPENTYPE_STRING_ID
1139 OPENTYPE_STRING_COPYRIGHT_NOTICE
= 0,
1140 OPENTYPE_STRING_FAMILY_NAME
,
1141 OPENTYPE_STRING_SUBFAMILY_NAME
,
1142 OPENTYPE_STRING_UNIQUE_IDENTIFIER
,
1143 OPENTYPE_STRING_FULL_FONTNAME
,
1144 OPENTYPE_STRING_VERSION_STRING
,
1145 OPENTYPE_STRING_POSTSCRIPT_FONTNAME
,
1146 OPENTYPE_STRING_TRADEMARK
,
1147 OPENTYPE_STRING_MANUFACTURER
,
1148 OPENTYPE_STRING_DESIGNER
,
1149 OPENTYPE_STRING_DESCRIPTION
,
1150 OPENTYPE_STRING_VENDOR_URL
,
1151 OPENTYPE_STRING_DESIGNER_URL
,
1152 OPENTYPE_STRING_LICENSE_DESCRIPTION
,
1153 OPENTYPE_STRING_LICENSE_INFO_URL
,
1154 OPENTYPE_STRING_RESERVED_ID15
,
1155 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
,
1156 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
,
1157 OPENTYPE_STRING_COMPATIBLE_FULLNAME
,
1158 OPENTYPE_STRING_SAMPLE_TEXT
,
1159 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
,
1160 OPENTYPE_STRING_WWS_FAMILY_NAME
,
1161 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
1164 static const UINT16 dwriteid_to_opentypeid
[DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME
+ 1] =
1166 (UINT16
)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
1167 OPENTYPE_STRING_COPYRIGHT_NOTICE
,
1168 OPENTYPE_STRING_VERSION_STRING
,
1169 OPENTYPE_STRING_TRADEMARK
,
1170 OPENTYPE_STRING_MANUFACTURER
,
1171 OPENTYPE_STRING_DESIGNER
,
1172 OPENTYPE_STRING_DESIGNER_URL
,
1173 OPENTYPE_STRING_DESCRIPTION
,
1174 OPENTYPE_STRING_VENDOR_URL
,
1175 OPENTYPE_STRING_LICENSE_DESCRIPTION
,
1176 OPENTYPE_STRING_LICENSE_INFO_URL
,
1177 OPENTYPE_STRING_FAMILY_NAME
,
1178 OPENTYPE_STRING_SUBFAMILY_NAME
,
1179 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
,
1180 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
,
1181 OPENTYPE_STRING_SAMPLE_TEXT
,
1182 OPENTYPE_STRING_FULL_FONTNAME
,
1183 OPENTYPE_STRING_POSTSCRIPT_FONTNAME
,
1184 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
,
1185 OPENTYPE_STRING_WWS_FAMILY_NAME
,
1189 struct cpal_header_0
1192 USHORT num_palette_entries
;
1193 USHORT num_palettes
;
1194 USHORT num_color_records
;
1195 ULONG offset_first_color_record
;
1196 USHORT color_record_indices
[1];
1199 struct cpal_color_record
1211 USHORT num_baseglyph_records
;
1212 ULONG offset_baseglyph_records
;
1213 ULONG offset_layer_records
;
1214 USHORT num_layer_records
;
1217 struct colr_baseglyph_record
1220 USHORT first_layer_index
;
1224 struct colr_layer_record
1227 USHORT palette_index
;
1230 struct meta_data_map
1242 DWORD data_maps_count
;
1243 struct meta_data_map maps
[1];
1246 static const void *table_read_ensure(const struct dwrite_fonttable
*table
, unsigned int offset
, unsigned int size
)
1248 if (size
> table
->size
|| offset
> table
->size
- size
)
1251 return table
->data
+ offset
;
1254 static WORD
table_read_be_word(const struct dwrite_fonttable
*table
, unsigned int offset
)
1256 const WORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1257 return ptr
? GET_BE_WORD(*ptr
) : 0;
1260 static DWORD
table_read_be_dword(const struct dwrite_fonttable
*table
, unsigned int offset
)
1262 const DWORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1263 return ptr
? GET_BE_DWORD(*ptr
) : 0;
1266 static DWORD
table_read_dword(const struct dwrite_fonttable
*table
, unsigned int offset
)
1268 const DWORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1269 return ptr
? *ptr
: 0;
1272 BOOL
is_face_type_supported(DWRITE_FONT_FACE_TYPE type
)
1274 return (type
== DWRITE_FONT_FACE_TYPE_CFF
) ||
1275 (type
== DWRITE_FONT_FACE_TYPE_TRUETYPE
) ||
1276 (type
== DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
) ||
1277 (type
== DWRITE_FONT_FACE_TYPE_RAW_CFF
);
1280 typedef HRESULT (*dwrite_fontfile_analyzer
)(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1281 DWRITE_FONT_FACE_TYPE
*face_type
);
1283 static HRESULT
opentype_ttc_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1284 DWRITE_FONT_FACE_TYPE
*face_type
)
1286 static const DWORD ttctag
= MS_TTCF_TAG
;
1287 const TTC_Header_V1
*header
;
1291 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(header
), &context
);
1295 if (!memcmp(header
->TTCTag
, &ttctag
, sizeof(ttctag
))) {
1296 *font_count
= GET_BE_DWORD(header
->numFonts
);
1297 *file_type
= DWRITE_FONT_FILE_TYPE_OPENTYPE_COLLECTION
;
1298 *face_type
= DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
;
1301 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1303 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1306 static HRESULT
opentype_ttf_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1307 DWRITE_FONT_FACE_TYPE
*face_type
)
1309 const DWORD
*header
;
1313 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1317 if (GET_BE_DWORD(*header
) == 0x10000) {
1319 *file_type
= DWRITE_FONT_FILE_TYPE_TRUETYPE
;
1320 *face_type
= DWRITE_FONT_FACE_TYPE_TRUETYPE
;
1323 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1325 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1328 static HRESULT
opentype_otf_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1329 DWRITE_FONT_FACE_TYPE
*face_type
)
1331 const DWORD
*header
;
1335 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1339 if (GET_BE_DWORD(*header
) == MS_OTTO_TAG
) {
1341 *file_type
= DWRITE_FONT_FILE_TYPE_CFF
;
1342 *face_type
= DWRITE_FONT_FACE_TYPE_CFF
;
1345 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1347 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1350 static HRESULT
opentype_type1_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1351 DWRITE_FONT_FACE_TYPE
*face_type
)
1353 #include "pshpack1.h"
1354 /* Specified in Adobe TechNote #5178 */
1362 #include "poppack.h"
1363 struct type1_header
{
1367 const struct type1_header
*header
;
1371 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1375 /* tag is followed by plain text section */
1376 if (header
->tag
== 0x8001 &&
1377 (!memcmp(header
->data
, "%!PS-AdobeFont", 14) ||
1378 !memcmp(header
->data
, "%!FontType", 10))) {
1380 *file_type
= DWRITE_FONT_FILE_TYPE_TYPE1_PFB
;
1381 *face_type
= DWRITE_FONT_FACE_TYPE_TYPE1
;
1384 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1386 /* let's see if it's a .pfm metrics file */
1387 if (*file_type
== DWRITE_FONT_FILE_TYPE_UNKNOWN
) {
1388 const struct pfm_header
*pfm_header
;
1391 BOOL header_checked
;
1393 hr
= IDWriteFontFileStream_GetFileSize(stream
, &filesize
);
1397 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&pfm_header
, 0, sizeof(*pfm_header
), &context
);
1401 offset
= pfm_header
->dfDevice
;
1402 header_checked
= pfm_header
->dfVersion
== 0x100 && pfm_header
->dfSize
== filesize
;
1403 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1405 /* as a last test check static string in PostScript information section */
1406 if (header_checked
) {
1407 static const char postscript
[] = "PostScript";
1410 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&devtype_name
, offset
, sizeof(postscript
), &context
);
1414 if (!memcmp(devtype_name
, postscript
, sizeof(postscript
))) {
1416 *file_type
= DWRITE_FONT_FILE_TYPE_TYPE1_PFM
;
1417 *face_type
= DWRITE_FONT_FACE_TYPE_TYPE1
;
1420 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1424 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1427 HRESULT
opentype_analyze_font(IDWriteFontFileStream
*stream
, BOOL
*supported
, DWRITE_FONT_FILE_TYPE
*file_type
,
1428 DWRITE_FONT_FACE_TYPE
*face_type
, UINT32
*face_count
)
1430 static dwrite_fontfile_analyzer fontfile_analyzers
[] = {
1431 opentype_ttf_analyzer
,
1432 opentype_otf_analyzer
,
1433 opentype_ttc_analyzer
,
1434 opentype_type1_analyzer
,
1437 dwrite_fontfile_analyzer
*analyzer
= fontfile_analyzers
;
1438 DWRITE_FONT_FACE_TYPE face
;
1444 *file_type
= DWRITE_FONT_FILE_TYPE_UNKNOWN
;
1445 *face_type
= DWRITE_FONT_FACE_TYPE_UNKNOWN
;
1449 hr
= (*analyzer
)(stream
, face_count
, file_type
, face_type
);
1459 *supported
= is_face_type_supported(*face_type
);
1463 HRESULT
opentype_try_get_font_table(const struct file_stream_desc
*stream_desc
, UINT32 tag
, const void **table_data
,
1464 void **table_context
, UINT32
*table_size
, BOOL
*found
)
1466 void *table_directory_context
, *sfnt_context
;
1467 TT_TableRecord
*table_record
= NULL
;
1468 TTC_SFNT_V1
*font_header
= NULL
;
1469 UINT32 table_offset
= 0;
1473 if (found
) *found
= FALSE
;
1474 if (table_size
) *table_size
= 0;
1477 *table_context
= NULL
;
1479 if (stream_desc
->face_type
== DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
) {
1480 const TTC_Header_V1
*ttc_header
;
1482 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&ttc_header
, 0, sizeof(*ttc_header
), &ttc_context
);
1483 if (SUCCEEDED(hr
)) {
1484 if (stream_desc
->face_index
>= GET_BE_DWORD(ttc_header
->numFonts
))
1487 table_offset
= GET_BE_DWORD(ttc_header
->OffsetTable
[stream_desc
->face_index
]);
1488 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&font_header
, table_offset
, sizeof(*font_header
), &sfnt_context
);
1490 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, ttc_context
);
1494 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&font_header
, 0, sizeof(*font_header
), &sfnt_context
);
1499 table_count
= GET_BE_WORD(font_header
->numTables
);
1500 table_offset
+= sizeof(*font_header
);
1502 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, sfnt_context
);
1504 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void **)&table_record
, table_offset
,
1505 table_count
* sizeof(*table_record
), &table_directory_context
);
1509 for (i
= 0; i
< table_count
; i
++) {
1510 if (table_record
->tag
== tag
) {
1511 UINT32 offset
= GET_BE_DWORD(table_record
->offset
);
1512 UINT32 length
= GET_BE_DWORD(table_record
->length
);
1517 *table_size
= length
;
1518 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, table_data
, offset
,
1519 length
, table_context
);
1525 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, table_directory_context
);
1531 static HRESULT
opentype_get_font_table(const struct file_stream_desc
*stream_desc
, UINT32 tag
,
1532 struct dwrite_fonttable
*table
)
1534 return opentype_try_get_font_table(stream_desc
, tag
, (const void **)&table
->data
, &table
->context
, &table
->size
, &table
->exists
);
1541 static UINT16
opentype_cmap_format0_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1543 const UINT8
*glyphs
= cmap
->data
;
1544 return (ch
< 0xff) ? glyphs
[ch
] : 0;
1547 static unsigned int opentype_cmap_format0_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1548 DWRITE_UNICODE_RANGE
*ranges
)
1559 struct cmap_format4_compare_context
1561 const struct dwrite_cmap
*cmap
;
1565 static int cmap_format4_compare_range(const void *a
, const void *b
)
1567 const struct cmap_format4_compare_context
*key
= a
;
1568 const UINT16
*end
= b
;
1571 if (key
->ch
> GET_BE_WORD(*end
))
1574 idx
= end
- key
->cmap
->u
.format4
.ends
;
1575 if (key
->ch
< GET_BE_WORD(key
->cmap
->u
.format4
.starts
[idx
]))
1581 static UINT16
opentype_cmap_format4_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1583 struct cmap_format4_compare_context key
= { .cmap
= cmap
, .ch
= ch
};
1584 unsigned int glyph
, idx
, range_offset
;
1585 const UINT16
*end_found
;
1587 /* Look up range. */
1588 end_found
= bsearch(&key
, cmap
->u
.format4
.ends
, cmap
->u
.format4
.seg_count
, sizeof(*cmap
->u
.format4
.ends
),
1589 cmap_format4_compare_range
);
1593 idx
= end_found
- cmap
->u
.format4
.ends
;
1595 range_offset
= GET_BE_WORD(cmap
->u
.format4
.id_range_offset
[idx
]);
1599 glyph
= ch
+ GET_BE_WORD(cmap
->u
.format4
.id_delta
[idx
]);
1603 unsigned int index
= range_offset
/ 2 + (ch
- GET_BE_WORD(cmap
->u
.format4
.starts
[idx
])) + idx
- cmap
->u
.format4
.seg_count
;
1604 if (index
>= cmap
->u
.format4
.glyph_id_array_len
)
1606 glyph
= GET_BE_WORD(cmap
->u
.format4
.glyph_id_array
[index
]);
1609 glyph
+= GET_BE_WORD(cmap
->u
.format4
.id_delta
[idx
]);
1612 return glyph
& 0xffff;
1615 static unsigned int opentype_cmap_format4_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1616 DWRITE_UNICODE_RANGE
*ranges
)
1620 count
= min(count
, cmap
->u
.format4
.seg_count
);
1622 for (i
= 0; i
< count
; ++i
)
1624 ranges
[i
].first
= GET_BE_WORD(cmap
->u
.format4
.starts
[i
]);
1625 ranges
[i
].last
= GET_BE_WORD(cmap
->u
.format4
.ends
[i
]);
1628 return cmap
->u
.format4
.seg_count
;
1631 static UINT16
opentype_cmap_format6_10_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1633 const UINT16
*glyphs
= cmap
->data
;
1634 if (ch
< cmap
->u
.format6_10
.first
|| ch
> cmap
->u
.format6_10
.last
) return 0;
1635 return glyphs
[ch
- cmap
->u
.format6_10
.first
];
1638 static unsigned int opentype_cmap_format6_10_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1639 DWRITE_UNICODE_RANGE
*ranges
)
1643 ranges
->first
= cmap
->u
.format6_10
.first
;
1644 ranges
->last
= cmap
->u
.format6_10
.last
;
1650 static int cmap_format12_13_compare_group(const void *a
, const void *b
)
1652 const unsigned int *ch
= a
;
1653 const UINT32
*group
= b
;
1655 if (*ch
> GET_BE_DWORD(group
[1]))
1658 if (*ch
< GET_BE_DWORD(group
[0]))
1664 static UINT16
opentype_cmap_format12_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1666 const UINT32
*groups
= cmap
->data
;
1667 const UINT32
*group_found
;
1669 if (!(group_found
= bsearch(&ch
, groups
, cmap
->u
.format12_13
.group_count
, 3 * sizeof(*groups
),
1670 cmap_format12_13_compare_group
)))
1673 return GET_BE_DWORD(group_found
[0]) <= GET_BE_DWORD(group_found
[1]) ?
1674 GET_BE_DWORD(group_found
[2]) + (ch
- GET_BE_DWORD(group_found
[0])) : 0;
1677 static unsigned int opentype_cmap_format12_13_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1678 DWRITE_UNICODE_RANGE
*ranges
)
1680 unsigned int i
, group_count
= cmap
->u
.format12_13
.group_count
;
1681 const UINT32
*groups
= cmap
->data
;
1683 count
= min(count
, group_count
);
1685 for (i
= 0; i
< count
; ++i
)
1687 ranges
[i
].first
= GET_BE_DWORD(groups
[3 * i
]);
1688 ranges
[i
].last
= GET_BE_DWORD(groups
[3 * i
+ 1]);
1694 static UINT16
opentype_cmap_format13_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1696 const UINT32
*groups
= cmap
->data
;
1697 const UINT32
*group_found
;
1699 if (!(group_found
= bsearch(&ch
, groups
, cmap
->u
.format12_13
.group_count
, 3 * sizeof(*groups
),
1700 cmap_format12_13_compare_group
)))
1703 return GET_BE_DWORD(group_found
[2]);
1706 static UINT16
opentype_cmap_dummy_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1711 static unsigned int opentype_cmap_dummy_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1712 DWRITE_UNICODE_RANGE
*ranges
)
1717 UINT16
opentype_cmap_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1721 if (!cmap
->get_glyph
) return 0;
1722 glyph
= cmap
->get_glyph(cmap
, ch
);
1723 if (!glyph
&& cmap
->symbol
&& ch
<= 0xff)
1724 glyph
= cmap
->get_glyph(cmap
, ch
+ 0xf000);
1728 static int cmap_header_compare(const void *a
, const void *b
)
1730 const UINT16
*key
= a
;
1731 const UINT16
*record
= b
;
1734 if (key
[0] < GET_BE_WORD(record
[0])) return -1;
1735 if (key
[0] > GET_BE_WORD(record
[0])) return 1;
1737 if (key
[1] < GET_BE_WORD(record
[1])) return -1;
1738 if (key
[1] > GET_BE_WORD(record
[1])) return 1;
1743 void dwrite_cmap_init(struct dwrite_cmap
*cmap
, IDWriteFontFile
*file
, unsigned int face_index
,
1744 DWRITE_FONT_FACE_TYPE face_type
)
1746 static const UINT16 encodings
[][2] =
1748 { 3, 0 }, /* MS Symbol encoding is preferred. */
1758 const struct cmap_encoding_record
*records
, *found_record
= NULL
;
1759 unsigned int length
, offset
, format
, count
, f
, i
, num_records
;
1760 struct file_stream_desc stream_desc
;
1761 struct dwrite_fonttable table
;
1762 const UINT16
*pair
= NULL
;
1765 if (cmap
->data
) return;
1767 /* For fontface stream is already available and preset. */
1768 if (!cmap
->stream
&& FAILED(hr
= get_filestream_from_file(file
, &cmap
->stream
)))
1770 WARN("Failed to get file stream, hr %#x.\n", hr
);
1774 stream_desc
.stream
= cmap
->stream
;
1775 stream_desc
.face_type
= face_type
;
1776 stream_desc
.face_index
= face_index
;
1778 opentype_get_font_table(&stream_desc
, MS_CMAP_TAG
, &table
);
1781 cmap
->table_context
= table
.context
;
1783 num_records
= table_read_be_word(&table
, 2);
1784 records
= table_read_ensure(&table
, 4, sizeof(*records
) * num_records
);
1786 for (i
= 0; i
< ARRAY_SIZE(encodings
); ++i
)
1788 pair
= encodings
[i
];
1789 if ((found_record
= bsearch(pair
, records
, num_records
, sizeof(*records
), cmap_header_compare
)))
1795 WARN("No suitable cmap table were found.\n");
1799 /* Symbol encoding. */
1800 cmap
->symbol
= pair
[0] == 3 && pair
[1] == 0;
1801 offset
= GET_BE_DWORD(found_record
->offset
);
1803 format
= table_read_be_word(&table
, offset
);
1808 cmap
->data
= table_read_ensure(&table
, offset
+ 6, 256);
1809 cmap
->get_glyph
= opentype_cmap_format0_get_glyph
;
1810 cmap
->get_ranges
= opentype_cmap_format0_get_ranges
;
1813 length
= table_read_be_word(&table
, offset
+ 2);
1814 cmap
->u
.format4
.seg_count
= count
= table_read_be_word(&table
, offset
+ 6) / 2;
1815 cmap
->u
.format4
.ends
= table_read_ensure(&table
, offset
+ 14, count
* 2);
1816 cmap
->u
.format4
.starts
= cmap
->u
.format4
.ends
+ count
+ 1;
1817 cmap
->u
.format4
.id_delta
= cmap
->u
.format4
.starts
+ count
;
1818 cmap
->u
.format4
.id_range_offset
= cmap
->u
.format4
.id_delta
+ count
;
1819 cmap
->u
.format4
.glyph_id_array
= cmap
->data
= cmap
->u
.format4
.id_range_offset
+ count
;
1820 cmap
->u
.format4
.glyph_id_array_len
= (length
- 16 - 8 * count
) / 2;
1821 cmap
->get_glyph
= opentype_cmap_format4_get_glyph
;
1822 cmap
->get_ranges
= opentype_cmap_format4_get_ranges
;
1826 /* Format 10 uses 4 byte fields. */
1827 f
= format
== 6 ? 1 : 2;
1828 cmap
->u
.format6_10
.first
= table_read_be_word(&table
, offset
+ f
* 6);
1829 count
= table_read_be_word(&table
, offset
+ f
* 8);
1830 cmap
->u
.format6_10
.last
= cmap
->u
.format6_10
.first
+ count
;
1831 cmap
->data
= table_read_ensure(&table
, offset
+ f
* 10, count
* 2);
1832 cmap
->get_glyph
= opentype_cmap_format6_10_get_glyph
;
1833 cmap
->get_ranges
= opentype_cmap_format6_10_get_ranges
;
1837 cmap
->u
.format12_13
.group_count
= count
= table_read_be_dword(&table
, offset
+ 12);
1838 cmap
->data
= table_read_ensure(&table
, offset
+ 16, count
* 3 * 4);
1839 cmap
->get_glyph
= format
== 12 ? opentype_cmap_format12_get_glyph
: opentype_cmap_format13_get_glyph
;
1840 cmap
->get_ranges
= opentype_cmap_format12_13_get_ranges
;
1843 WARN("Unhandled subtable format %u.\n", format
);
1850 /* Dummy implementation, returns 0 unconditionally. */
1852 cmap
->get_glyph
= opentype_cmap_dummy_get_glyph
;
1853 cmap
->get_ranges
= opentype_cmap_dummy_get_ranges
;
1857 void dwrite_cmap_release(struct dwrite_cmap
*cmap
)
1861 IDWriteFontFileStream_ReleaseFileFragment(cmap
->stream
, cmap
->table_context
);
1862 IDWriteFontFileStream_Release(cmap
->stream
);
1865 cmap
->stream
= NULL
;
1868 HRESULT
opentype_cmap_get_unicode_ranges(const struct dwrite_cmap
*cmap
, unsigned int max_count
, DWRITE_UNICODE_RANGE
*ranges
,
1869 unsigned int *count
)
1874 *count
= cmap
->get_ranges(cmap
, max_count
, ranges
);
1876 return *count
> max_count
? E_NOT_SUFFICIENT_BUFFER
: S_OK
;
1879 void opentype_get_font_typo_metrics(struct file_stream_desc
*stream_desc
, unsigned int *ascent
, unsigned int *descent
)
1881 struct dwrite_fonttable os2
;
1883 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
1885 *ascent
= *descent
= 0;
1887 if (os2
.size
>= FIELD_OFFSET(struct tt_os2
, sTypoLineGap
))
1889 SHORT value
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, sTypoDescender
));
1890 *ascent
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, sTypoAscender
));
1891 *descent
= value
< 0 ? -value
: 0;
1895 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
1898 void opentype_get_font_metrics(struct file_stream_desc
*stream_desc
, DWRITE_FONT_METRICS1
*metrics
, DWRITE_CARET_METRICS
*caret
)
1900 struct dwrite_fonttable os2
, head
, post
, hhea
;
1902 memset(metrics
, 0, sizeof(*metrics
));
1904 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
1905 opentype_get_font_table(stream_desc
, MS_HEAD_TAG
, &head
);
1906 opentype_get_font_table(stream_desc
, MS_POST_TAG
, &post
);
1907 opentype_get_font_table(stream_desc
, MS_HHEA_TAG
, &hhea
);
1911 metrics
->designUnitsPerEm
= table_read_be_word(&head
, FIELD_OFFSET(struct tt_head
, unitsPerEm
));
1912 metrics
->glyphBoxLeft
= table_read_be_word(&head
, FIELD_OFFSET(struct tt_head
, xMin
));
1913 metrics
->glyphBoxTop
= table_read_be_word(&head
, FIELD_OFFSET(struct tt_head
, yMax
));
1914 metrics
->glyphBoxRight
= table_read_be_word(&head
, FIELD_OFFSET(struct tt_head
, xMax
));
1915 metrics
->glyphBoxBottom
= table_read_be_word(&head
, FIELD_OFFSET(struct tt_head
, yMin
));
1922 caret
->slopeRise
= table_read_be_word(&hhea
, FIELD_OFFSET(struct tt_hhea
, caretSlopeRise
));
1923 caret
->slopeRun
= table_read_be_word(&hhea
, FIELD_OFFSET(struct tt_hhea
, caretSlopeRun
));
1924 caret
->offset
= table_read_be_word(&hhea
, FIELD_OFFSET(struct tt_hhea
, caretOffset
));
1927 memset(caret
, 0, sizeof(*caret
));
1932 USHORT version
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, version
));
1934 metrics
->ascent
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, usWinAscent
));
1935 /* Some fonts have usWinDescent value stored as signed short, which could be wrongly
1936 interpreted as large unsigned value. */
1937 metrics
->descent
= abs((SHORT
)table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, usWinDescent
)));
1939 /* Line gap is estimated using two sets of ascender/descender values and 'hhea' line gap. */
1942 SHORT descender
= (SHORT
)table_read_be_word(&hhea
, FIELD_OFFSET(struct tt_hhea
, descender
));
1945 linegap
= table_read_be_word(&hhea
, FIELD_OFFSET(struct tt_hhea
, ascender
)) + abs(descender
) +
1946 table_read_be_word(&hhea
, FIELD_OFFSET(struct tt_hhea
, linegap
)) - metrics
->ascent
- metrics
->descent
;
1947 metrics
->lineGap
= linegap
> 0 ? linegap
: 0;
1950 metrics
->strikethroughPosition
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, yStrikeoutPosition
));
1951 metrics
->strikethroughThickness
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, yStrikeoutSize
));
1952 metrics
->subscriptPositionX
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, ySubscriptXOffset
));
1953 /* Y offset is stored as positive offset below baseline */
1954 metrics
->subscriptPositionY
= -table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, ySubscriptYOffset
));
1955 metrics
->subscriptSizeX
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, ySubscriptXSize
));
1956 metrics
->subscriptSizeY
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, ySubscriptYSize
));
1957 metrics
->superscriptPositionX
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, ySuperscriptXOffset
));
1958 metrics
->superscriptPositionY
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, ySuperscriptYOffset
));
1959 metrics
->superscriptSizeX
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, ySuperscriptXSize
));
1960 metrics
->superscriptSizeY
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, ySuperscriptYSize
));
1962 /* version 2 fields */
1965 metrics
->capHeight
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, sCapHeight
));
1966 metrics
->xHeight
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, sxHeight
));
1969 if (table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, fsSelection
)) & OS2_FSSELECTION_USE_TYPO_METRICS
)
1971 SHORT descent
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, sTypoDescender
));
1972 metrics
->ascent
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, sTypoAscender
));
1973 metrics
->descent
= descent
< 0 ? -descent
: 0;
1974 metrics
->lineGap
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, sTypoLineGap
));
1975 metrics
->hasTypographicMetrics
= TRUE
;
1980 metrics
->strikethroughPosition
= metrics
->designUnitsPerEm
/ 3;
1983 metrics
->ascent
= table_read_be_word(&hhea
, FIELD_OFFSET(struct tt_hhea
, ascender
));
1984 metrics
->descent
= abs((SHORT
)table_read_be_word(&hhea
, FIELD_OFFSET(struct tt_hhea
, descender
)));
1990 metrics
->underlinePosition
= table_read_be_word(&post
, FIELD_OFFSET(struct tt_post
, underlinePosition
));
1991 metrics
->underlineThickness
= table_read_be_word(&post
, FIELD_OFFSET(struct tt_post
, underlineThickness
));
1994 if (metrics
->underlineThickness
== 0)
1995 metrics
->underlineThickness
= metrics
->designUnitsPerEm
/ 14;
1996 if (metrics
->strikethroughThickness
== 0)
1997 metrics
->strikethroughThickness
= metrics
->underlineThickness
;
1999 /* estimate missing metrics */
2000 if (metrics
->xHeight
== 0)
2001 metrics
->xHeight
= metrics
->designUnitsPerEm
/ 2;
2002 if (metrics
->capHeight
== 0)
2003 metrics
->capHeight
= metrics
->designUnitsPerEm
* 7 / 10;
2006 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2008 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, head
.context
);
2010 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, post
.context
);
2012 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, hhea
.context
);
2015 void opentype_get_font_properties(struct file_stream_desc
*stream_desc
, struct dwrite_font_props
*props
)
2017 struct dwrite_fonttable os2
, head
, colr
, cpal
;
2018 BOOL is_symbol
, is_monospaced
;
2020 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
2021 opentype_get_font_table(stream_desc
, MS_HEAD_TAG
, &head
);
2023 /* default stretch, weight and style to normal */
2024 props
->stretch
= DWRITE_FONT_STRETCH_NORMAL
;
2025 props
->weight
= DWRITE_FONT_WEIGHT_NORMAL
;
2026 props
->style
= DWRITE_FONT_STYLE_NORMAL
;
2027 memset(&props
->panose
, 0, sizeof(props
->panose
));
2028 memset(&props
->fontsig
, 0, sizeof(props
->fontsig
));
2029 memset(&props
->lf
, 0, sizeof(props
->lf
));
2032 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
2035 USHORT version
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, version
));
2036 USHORT fsSelection
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, fsSelection
));
2037 USHORT usWeightClass
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, usWeightClass
));
2038 USHORT usWidthClass
= table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, usWidthClass
));
2041 if (usWidthClass
> DWRITE_FONT_STRETCH_UNDEFINED
&& usWidthClass
<= DWRITE_FONT_STRETCH_ULTRA_EXPANDED
)
2042 props
->stretch
= usWidthClass
;
2044 if (usWeightClass
>= 1 && usWeightClass
<= 9)
2045 usWeightClass
*= 100;
2047 if (usWeightClass
> DWRITE_FONT_WEIGHT_ULTRA_BLACK
)
2048 props
->weight
= DWRITE_FONT_WEIGHT_ULTRA_BLACK
;
2049 else if (usWeightClass
> 0)
2050 props
->weight
= usWeightClass
;
2052 if (version
>= 4 && (fsSelection
& OS2_FSSELECTION_OBLIQUE
))
2053 props
->style
= DWRITE_FONT_STYLE_OBLIQUE
;
2054 else if (fsSelection
& OS2_FSSELECTION_ITALIC
)
2055 props
->style
= DWRITE_FONT_STYLE_ITALIC
;
2056 props
->lf
.lfItalic
= !!(fsSelection
& OS2_FSSELECTION_ITALIC
);
2058 if ((panose
= table_read_ensure(&os2
, FIELD_OFFSET(struct tt_os2
, panose
), sizeof(props
->panose
))))
2059 memcpy(&props
->panose
, panose
, sizeof(props
->panose
));
2062 props
->fontsig
.fsUsb
[0] = table_read_be_dword(&os2
, FIELD_OFFSET(struct tt_os2
, ulUnicodeRange1
));
2063 props
->fontsig
.fsUsb
[1] = table_read_be_dword(&os2
, FIELD_OFFSET(struct tt_os2
, ulUnicodeRange2
));
2064 props
->fontsig
.fsUsb
[2] = table_read_be_dword(&os2
, FIELD_OFFSET(struct tt_os2
, ulUnicodeRange3
));
2065 props
->fontsig
.fsUsb
[3] = table_read_be_dword(&os2
, FIELD_OFFSET(struct tt_os2
, ulUnicodeRange4
));
2069 props
->fontsig
.fsCsb
[0] = table_read_be_dword(&os2
, FIELD_OFFSET(struct tt_os2
, ulCodePageRange1
));
2070 props
->fontsig
.fsCsb
[1] = table_read_be_dword(&os2
, FIELD_OFFSET(struct tt_os2
, ulCodePageRange2
));
2075 USHORT macStyle
= table_read_be_word(&head
, FIELD_OFFSET(struct tt_head
, macStyle
));
2077 if (macStyle
& TT_HEAD_MACSTYLE_CONDENSED
)
2078 props
->stretch
= DWRITE_FONT_STRETCH_CONDENSED
;
2079 else if (macStyle
& TT_HEAD_MACSTYLE_EXTENDED
)
2080 props
->stretch
= DWRITE_FONT_STRETCH_EXPANDED
;
2082 if (macStyle
& TT_HEAD_MACSTYLE_BOLD
)
2083 props
->weight
= DWRITE_FONT_WEIGHT_BOLD
;
2085 if (macStyle
& TT_HEAD_MACSTYLE_ITALIC
) {
2086 props
->style
= DWRITE_FONT_STYLE_ITALIC
;
2087 props
->lf
.lfItalic
= 1;
2091 props
->lf
.lfWeight
= props
->weight
;
2093 /* FONT_IS_SYMBOL */
2094 if (!(is_symbol
= props
->panose
.familyKind
== DWRITE_PANOSE_FAMILY_SYMBOL
))
2096 struct dwrite_fonttable cmap
;
2097 int i
, offset
, num_tables
;
2099 opentype_get_font_table(stream_desc
, MS_CMAP_TAG
, &cmap
);
2103 num_tables
= table_read_be_word(&cmap
, FIELD_OFFSET(struct cmap_header
, num_tables
));
2104 offset
= FIELD_OFFSET(struct cmap_header
, tables
);
2106 for (i
= 0; !is_symbol
&& i
< num_tables
; ++i
)
2108 WORD platform
, encoding
;
2110 platform
= table_read_be_word(&cmap
, offset
+ i
* sizeof(struct cmap_encoding_record
) +
2111 FIELD_OFFSET(struct cmap_encoding_record
, platformID
));
2112 encoding
= table_read_be_word(&cmap
, offset
+ i
* sizeof(struct cmap_encoding_record
) +
2113 FIELD_OFFSET(struct cmap_encoding_record
, encodingID
));
2115 is_symbol
= platform
== OPENTYPE_CMAP_TABLE_PLATFORM_WIN
&&
2116 encoding
== OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL
;
2119 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, cmap
.context
);
2123 props
->flags
|= FONT_IS_SYMBOL
;
2125 /* FONT_IS_MONOSPACED */
2126 if (!(is_monospaced
= props
->panose
.text
.proportion
== DWRITE_PANOSE_PROPORTION_MONOSPACED
))
2128 struct dwrite_fonttable post
;
2130 opentype_get_font_table(stream_desc
, MS_POST_TAG
, &post
);
2134 is_monospaced
= !!table_read_dword(&post
, FIELD_OFFSET(struct tt_post
, fixed_pitch
));
2136 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, post
.context
);
2140 props
->flags
|= FONT_IS_MONOSPACED
;
2142 /* FONT_IS_COLORED */
2143 opentype_get_font_table(stream_desc
, MS_COLR_TAG
, &colr
);
2146 opentype_get_font_table(stream_desc
, MS_CPAL_TAG
, &cpal
);
2149 props
->flags
|= FONT_IS_COLORED
;
2150 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, cpal
.context
);
2153 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, colr
.context
);
2156 TRACE("stretch=%d, weight=%d, style %d\n", props
->stretch
, props
->weight
, props
->style
);
2159 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2161 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, head
.context
);
2164 static UINT
get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform
, USHORT encoding
)
2169 case OPENTYPE_PLATFORM_UNICODE
:
2171 case OPENTYPE_PLATFORM_MAC
:
2174 case TT_NAME_MAC_ENCODING_ROMAN
:
2177 case TT_NAME_MAC_ENCODING_JAPANESE
:
2180 case TT_NAME_MAC_ENCODING_TRAD_CHINESE
:
2183 case TT_NAME_MAC_ENCODING_KOREAN
:
2186 case TT_NAME_MAC_ENCODING_ARABIC
:
2189 case TT_NAME_MAC_ENCODING_HEBREW
:
2192 case TT_NAME_MAC_ENCODING_GREEK
:
2195 case TT_NAME_MAC_ENCODING_RUSSIAN
:
2198 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE
:
2201 case TT_NAME_MAC_ENCODING_THAI
:
2205 FIXME("encoding %u not handled, platform %d.\n", encoding
, platform
);
2209 case OPENTYPE_PLATFORM_WIN
:
2212 case TT_NAME_WINDOWS_ENCODING_SYMBOL
:
2213 case TT_NAME_WINDOWS_ENCODING_UNICODE_BMP
:
2214 case TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
:
2216 case TT_NAME_WINDOWS_ENCODING_SJIS
:
2219 case TT_NAME_WINDOWS_ENCODING_PRC
:
2222 case TT_NAME_WINDOWS_ENCODING_BIG5
:
2225 case TT_NAME_WINDOWS_ENCODING_WANSUNG
:
2228 case TT_NAME_WINDOWS_ENCODING_JOHAB
:
2232 FIXME("encoding %u not handled, platform %d.\n", encoding
, platform
);
2237 FIXME("unknown platform %d\n", platform
);
2243 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform
, USHORT lang_id
, WCHAR
*locale
, USHORT locale_len
)
2245 static const WCHAR enusW
[] = {'e','n','-','U','S',0};
2248 case OPENTYPE_PLATFORM_MAC
:
2250 const char *locale_name
= NULL
;
2252 if (lang_id
> TT_NAME_MAC_LANGID_AZER_ROMAN
)
2253 WARN("invalid mac lang id %d\n", lang_id
);
2254 else if (!name_mac_langid_to_locale
[lang_id
][0])
2255 FIXME("failed to map mac lang id %d to locale name\n", lang_id
);
2257 locale_name
= name_mac_langid_to_locale
[lang_id
];
2260 MultiByteToWideChar(CP_ACP
, 0, name_mac_langid_to_locale
[lang_id
], -1, locale
, locale_len
);
2262 strcpyW(locale
, enusW
);
2265 case OPENTYPE_PLATFORM_WIN
:
2266 if (!LCIDToLocaleName(MAKELCID(lang_id
, SORT_DEFAULT
), locale
, locale_len
, 0)) {
2267 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id
, SORT_DEFAULT
));
2268 strcpyW(locale
, enusW
);
2271 case OPENTYPE_PLATFORM_UNICODE
:
2272 strcpyW(locale
, enusW
);
2275 FIXME("unknown platform %d\n", platform
);
2279 static BOOL
opentype_decode_namerecord(const TT_NAME_V0
*header
, BYTE
*storage_area
, USHORT recid
, IDWriteLocalizedStrings
*strings
)
2281 const TT_NameRecord
*record
= &header
->nameRecord
[recid
];
2282 USHORT lang_id
, length
, offset
, encoding
, platform
;
2285 platform
= GET_BE_WORD(record
->platformID
);
2286 lang_id
= GET_BE_WORD(record
->languageID
);
2287 length
= GET_BE_WORD(record
->length
);
2288 offset
= GET_BE_WORD(record
->offset
);
2289 encoding
= GET_BE_WORD(record
->encodingID
);
2291 if (lang_id
< 0x8000) {
2292 WCHAR locale
[LOCALE_NAME_MAX_LENGTH
];
2296 codepage
= get_name_record_codepage(platform
, encoding
);
2297 get_name_record_locale(platform
, lang_id
, locale
, ARRAY_SIZE(locale
));
2300 DWORD len
= MultiByteToWideChar(codepage
, 0, (LPSTR
)(storage_area
+ offset
), length
, NULL
, 0);
2301 name_string
= heap_alloc(sizeof(WCHAR
) * (len
+1));
2302 MultiByteToWideChar(codepage
, 0, (LPSTR
)(storage_area
+ offset
), length
, name_string
, len
);
2303 name_string
[len
] = 0;
2308 length
/= sizeof(WCHAR
);
2309 name_string
= heap_strdupnW((LPWSTR
)(storage_area
+ offset
), length
);
2310 for (i
= 0; i
< length
; i
++)
2311 name_string
[i
] = GET_BE_WORD(name_string
[i
]);
2314 TRACE("string %s for locale %s found\n", debugstr_w(name_string
), debugstr_w(locale
));
2315 add_localizedstring(strings
, locale
, name_string
);
2316 heap_free(name_string
);
2320 FIXME("handle NAME format 1\n");
2325 static HRESULT
opentype_get_font_strings_from_id(const void *table_data
, enum OPENTYPE_STRING_ID id
, IDWriteLocalizedStrings
**strings
)
2327 int i
, count
, candidate_mac
, candidate_unicode
;
2328 const TT_NAME_V0
*header
;
2329 BYTE
*storage_area
= 0;
2337 hr
= create_localizedstrings(strings
);
2338 if (FAILED(hr
)) return hr
;
2340 header
= table_data
;
2341 format
= GET_BE_WORD(header
->format
);
2348 FIXME("unsupported NAME format %d\n", format
);
2351 storage_area
= (LPBYTE
)table_data
+ GET_BE_WORD(header
->stringOffset
);
2352 count
= GET_BE_WORD(header
->count
);
2355 candidate_unicode
= candidate_mac
= -1;
2356 for (i
= 0; i
< count
; i
++) {
2357 const TT_NameRecord
*record
= &header
->nameRecord
[i
];
2360 if (GET_BE_WORD(record
->nameID
) != id
)
2363 platform
= GET_BE_WORD(record
->platformID
);
2366 /* Skip Unicode or Mac entries for now, fonts tend to duplicate those
2367 strings as WIN platform entries. If font does not have WIN entry for
2368 this id, we will use Mac or Unicode platform entry while assuming
2370 case OPENTYPE_PLATFORM_UNICODE
:
2371 if (candidate_unicode
== -1)
2372 candidate_unicode
= i
;
2374 case OPENTYPE_PLATFORM_MAC
:
2375 if (candidate_mac
== -1)
2378 case OPENTYPE_PLATFORM_WIN
:
2379 if (opentype_decode_namerecord(header
, storage_area
, i
, *strings
))
2383 FIXME("platform %i not supported\n", platform
);
2390 if (candidate_mac
!= -1)
2391 exists
= opentype_decode_namerecord(header
, storage_area
, candidate_mac
, *strings
);
2392 if (!exists
&& candidate_unicode
!= -1)
2393 exists
= opentype_decode_namerecord(header
, storage_area
, candidate_unicode
, *strings
);
2397 IDWriteLocalizedStrings_Release(*strings
);
2403 sort_localizedstrings(*strings
);
2405 return exists
? S_OK
: E_FAIL
;
2408 static WCHAR
*meta_get_lng_name(WCHAR
*str
, WCHAR
**ctx
)
2410 static const WCHAR delimW
[] = {',',' ',0};
2413 if (!str
) str
= *ctx
;
2414 while (*str
&& strchrW(delimW
, *str
)) str
++;
2415 if (!*str
) return NULL
;
2417 while (*str
&& !strchrW(delimW
, *str
)) str
++;
2418 if (*str
) *str
++ = 0;
2424 static HRESULT
opentype_get_font_strings_from_meta(const struct file_stream_desc
*stream_desc
,
2425 DWRITE_INFORMATIONAL_STRING_ID id
, IDWriteLocalizedStrings
**ret
)
2427 static const WCHAR emptyW
[] = { 0 };
2428 const struct meta_data_map
*maps
;
2429 IDWriteLocalizedStrings
*strings
;
2430 struct dwrite_fonttable meta
;
2431 DWORD version
, i
, count
, tag
;
2438 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG
:
2441 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG
:
2445 WARN("Unexpected id %d.\n", id
);
2449 if (FAILED(hr
= create_localizedstrings(&strings
)))
2452 opentype_get_font_table(stream_desc
, MS_META_TAG
, &meta
);
2456 version
= table_read_be_dword(&meta
, 0);
2459 WARN("Unexpected meta table version %d.\n", version
);
2463 count
= table_read_be_dword(&meta
, FIELD_OFFSET(struct meta_header
, data_maps_count
));
2464 if (!(maps
= table_read_ensure(&meta
, FIELD_OFFSET(struct meta_header
, maps
),
2465 count
* sizeof(struct meta_data_map
))))
2468 for (i
= 0; i
< count
; ++i
)
2472 if (maps
[i
].tag
== tag
&& maps
[i
].length
)
2474 DWORD length
= GET_BE_DWORD(maps
[i
].length
), j
;
2476 if ((data
= table_read_ensure(&meta
, GET_BE_DWORD(maps
[i
].offset
), length
)))
2478 WCHAR
*ptrW
= heap_alloc((length
+ 1) * sizeof(WCHAR
)), *ctx
, *token
;
2486 /* Data is stored in comma separated list, ASCII range only. */
2487 for (j
= 0; j
< length
; ++j
)
2491 token
= meta_get_lng_name(ptrW
, &ctx
);
2495 add_localizedstring(strings
, emptyW
, token
);
2496 token
= meta_get_lng_name(NULL
, &ctx
);
2504 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, meta
.context
);
2507 if (IDWriteLocalizedStrings_GetCount(strings
))
2510 IDWriteLocalizedStrings_Release(strings
);
2515 HRESULT
opentype_get_font_info_strings(const struct file_stream_desc
*stream_desc
, DWRITE_INFORMATIONAL_STRING_ID id
,
2516 IDWriteLocalizedStrings
**strings
)
2518 struct dwrite_fonttable name
;
2522 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG
:
2523 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG
:
2524 opentype_get_font_strings_from_meta(stream_desc
, id
, strings
);
2527 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2528 opentype_get_font_strings_from_id(name
.data
, dwriteid_to_opentypeid
[id
], strings
);
2530 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2536 /* FamilyName locating order is WWS Family Name -> Preferred Family Name -> Family Name. If font claims to
2537 have 'Preferred Family Name' in WWS format, then WWS name is not used. */
2538 HRESULT
opentype_get_font_familyname(struct file_stream_desc
*stream_desc
, IDWriteLocalizedStrings
**names
)
2540 struct dwrite_fonttable os2
, name
;
2541 const void *name_table
;
2545 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
2546 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2548 name_table
= (const void *)name
.data
;
2552 /* If Preferred Family doesn't conform to WWS model try WWS name. */
2553 fsselection
= os2
.data
? table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, fsSelection
)) : 0;
2554 if (os2
.data
&& !(fsselection
& OS2_FSSELECTION_WWS
))
2555 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_WWS_FAMILY_NAME
, names
);
2560 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
, names
);
2562 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_FAMILY_NAME
, names
);
2565 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2567 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2572 /* FaceName locating order is WWS Face Name -> Preferred Face Name -> Face Name. If font claims to
2573 have 'Preferred Face Name' in WWS format, then WWS name is not used. */
2574 HRESULT
opentype_get_font_facename(struct file_stream_desc
*stream_desc
, WCHAR
*lfname
, IDWriteLocalizedStrings
**names
)
2576 struct dwrite_fonttable os2
, name
;
2577 IDWriteLocalizedStrings
*lfnames
;
2578 const void *name_table
;
2582 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
2583 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2585 name_table
= name
.data
;
2589 /* if Preferred Family doesn't conform to WWS model try WWS name */
2590 fsselection
= os2
.data
? table_read_be_word(&os2
, FIELD_OFFSET(struct tt_os2
, fsSelection
)) : 0;
2591 if (os2
.data
&& !(fsselection
& OS2_FSSELECTION_WWS
))
2592 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_WWS_SUBFAMILY_NAME
, names
);
2597 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
, names
);
2599 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_SUBFAMILY_NAME
, names
);
2601 /* User locale is preferred, with fallback to en-us. */
2603 if (SUCCEEDED(opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_FAMILY_NAME
, &lfnames
))) {
2604 static const WCHAR enusW
[] = {'e','n','-','u','s',0};
2605 WCHAR localeW
[LOCALE_NAME_MAX_LENGTH
];
2610 if (GetSystemDefaultLocaleName(localeW
, ARRAY_SIZE(localeW
)))
2611 IDWriteLocalizedStrings_FindLocaleName(lfnames
, localeW
, &index
, &exists
);
2614 IDWriteLocalizedStrings_FindLocaleName(lfnames
, enusW
, &index
, &exists
);
2620 IDWriteLocalizedStrings_GetStringLength(lfnames
, index
, &length
);
2621 nameW
= heap_alloc((length
+ 1) * sizeof(WCHAR
));
2624 IDWriteLocalizedStrings_GetString(lfnames
, index
, nameW
, length
+ 1);
2625 lstrcpynW(lfname
, nameW
, LF_FACESIZE
);
2630 IDWriteLocalizedStrings_Release(lfnames
);
2634 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2636 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2641 static const struct ot_langsys
*opentype_get_langsys(const struct ot_gsubgpos_table
*table
, unsigned int script_index
,
2642 unsigned int language_index
, unsigned int *feature_count
)
2644 unsigned int table_offset
, langsys_offset
;
2645 const struct ot_langsys
*langsys
= NULL
;
2649 if (!table
->table
.data
|| script_index
== ~0u)
2652 /* ScriptTable offset. */
2653 table_offset
= table_read_be_word(&table
->table
, table
->script_list
+ FIELD_OFFSET(struct ot_script_list
, scripts
) +
2654 script_index
* sizeof(struct ot_script_record
) + FIELD_OFFSET(struct ot_script_record
, script
));
2658 if (language_index
== ~0u)
2659 langsys_offset
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
);
2661 langsys_offset
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
+
2662 FIELD_OFFSET(struct ot_script
, langsys
) + language_index
* sizeof(struct ot_langsys_record
) +
2663 FIELD_OFFSET(struct ot_langsys_record
, langsys
));
2664 langsys_offset
+= table
->script_list
+ table_offset
;
2666 *feature_count
= table_read_be_word(&table
->table
, langsys_offset
+ FIELD_OFFSET(struct ot_langsys
, feature_count
));
2668 langsys
= table_read_ensure(&table
->table
, langsys_offset
, FIELD_OFFSET(struct ot_langsys
, feature_index
[*feature_count
]));
2675 void opentype_get_typographic_features(struct ot_gsubgpos_table
*table
, unsigned int script_index
,
2676 unsigned int language_index
, struct tag_array
*t
)
2678 unsigned int i
, total_feature_count
, script_feature_count
;
2679 const struct ot_feature_list
*feature_list
;
2680 const struct ot_langsys
*langsys
= NULL
;
2682 langsys
= opentype_get_langsys(table
, script_index
, language_index
, &script_feature_count
);
2684 total_feature_count
= table_read_be_word(&table
->table
, table
->feature_list
);
2685 if (!total_feature_count
)
2688 feature_list
= table_read_ensure(&table
->table
, table
->feature_list
,
2689 FIELD_OFFSET(struct ot_feature_list
, features
[total_feature_count
]));
2693 for (i
= 0; i
< script_feature_count
; ++i
)
2695 unsigned int feature_index
= GET_BE_WORD(langsys
->feature_index
[i
]);
2696 if (feature_index
>= total_feature_count
)
2699 if (!dwrite_array_reserve((void **)&t
->tags
, &t
->capacity
, t
->count
+ 1, sizeof(*t
->tags
)))
2702 t
->tags
[t
->count
++] = feature_list
->features
[feature_index
].tag
;
2706 static unsigned int find_vdmx_group(const struct vdmx_header
*hdr
)
2709 const struct vdmx_ratio
*ratios
= (struct vdmx_ratio
*)(hdr
+ 1);
2710 BYTE dev_x_ratio
= 1, dev_y_ratio
= 1;
2711 unsigned int group_offset
= 0;
2713 num_ratios
= GET_BE_WORD(hdr
->num_ratios
);
2715 for (i
= 0; i
< num_ratios
; i
++) {
2717 if (!ratios
[i
].bCharSet
) continue;
2719 if ((ratios
[i
].xRatio
== 0 && ratios
[i
].yStartRatio
== 0 &&
2720 ratios
[i
].yEndRatio
== 0) ||
2721 (ratios
[i
].xRatio
== dev_x_ratio
&& ratios
[i
].yStartRatio
<= dev_y_ratio
&&
2722 ratios
[i
].yEndRatio
>= dev_y_ratio
))
2724 group_offset
= GET_BE_WORD(*((WORD
*)(ratios
+ num_ratios
) + i
));
2729 return group_offset
;
2732 BOOL
opentype_get_vdmx_size(const struct dwrite_fonttable
*vdmx
, INT emsize
, UINT16
*ascent
, UINT16
*descent
)
2734 unsigned int num_ratios
, num_recs
, group_offset
, i
;
2735 const struct vdmx_header
*header
;
2736 const struct vdmx_group
*group
;
2741 num_ratios
= table_read_be_word(vdmx
, FIELD_OFFSET(struct vdmx_header
, num_ratios
));
2742 num_recs
= table_read_be_word(vdmx
, FIELD_OFFSET(struct vdmx_header
, num_recs
));
2744 header
= table_read_ensure(vdmx
, 0, sizeof(*header
) + num_ratios
* sizeof(struct vdmx_ratio
) +
2745 num_recs
* sizeof(*group
));
2750 group_offset
= find_vdmx_group(header
);
2754 num_recs
= table_read_be_word(vdmx
, group_offset
);
2755 group
= table_read_ensure(vdmx
, group_offset
, FIELD_OFFSET(struct vdmx_group
, entries
[num_recs
]));
2760 if (emsize
< group
->startsz
|| emsize
>= group
->endsz
)
2763 for (i
= 0; i
< num_recs
; ++i
)
2765 WORD ppem
= GET_BE_WORD(group
->entries
[i
].yPelHeight
);
2766 if (ppem
> emsize
) {
2767 FIXME("interpolate %d\n", emsize
);
2771 if (ppem
== emsize
) {
2772 *ascent
= (SHORT
)GET_BE_WORD(group
->entries
[i
].yMax
);
2773 *descent
= -(SHORT
)GET_BE_WORD(group
->entries
[i
].yMin
);
2781 unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable
*gasp
, float emsize
)
2783 unsigned int version
, num_ranges
, i
;
2784 const struct gasp_header
*table
;
2790 num_ranges
= table_read_be_word(gasp
, FIELD_OFFSET(struct gasp_header
, num_ranges
));
2792 table
= table_read_ensure(gasp
, 0, FIELD_OFFSET(struct gasp_header
, ranges
[num_ranges
]));
2796 version
= GET_BE_WORD(table
->version
);
2799 ERR("Unsupported gasp table format version %u.\n", version
);
2803 for (i
= 0; i
< num_ranges
; ++i
)
2805 flags
= GET_BE_WORD(table
->ranges
[i
].flags
);
2806 if (emsize
<= GET_BE_WORD(table
->ranges
[i
].max_ppem
)) break;
2813 unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable
*cpal
)
2815 return table_read_be_word(cpal
, FIELD_OFFSET(struct cpal_header_0
, num_palettes
));
2818 unsigned int opentype_get_cpal_paletteentrycount(const struct dwrite_fonttable
*cpal
)
2820 return table_read_be_word(cpal
, FIELD_OFFSET(struct cpal_header_0
, num_palette_entries
));
2823 HRESULT
opentype_get_cpal_entries(const struct dwrite_fonttable
*cpal
, unsigned int palette
,
2824 unsigned int first_entry_index
, unsigned int entry_count
, DWRITE_COLOR_F
*entries
)
2826 unsigned int num_palettes
, num_palette_entries
, i
;
2827 const struct cpal_color_record
*records
;
2828 const struct cpal_header_0
*header
;
2830 header
= table_read_ensure(cpal
, 0, sizeof(*header
));
2832 if (!cpal
->exists
|| !header
)
2833 return DWRITE_E_NOCOLOR
;
2835 num_palettes
= GET_BE_WORD(header
->num_palettes
);
2836 if (palette
>= num_palettes
)
2837 return DWRITE_E_NOCOLOR
;
2839 header
= table_read_ensure(cpal
, 0, FIELD_OFFSET(struct cpal_header_0
, color_record_indices
[palette
]));
2841 return DWRITE_E_NOCOLOR
;
2843 num_palette_entries
= GET_BE_WORD(header
->num_palette_entries
);
2844 if (first_entry_index
+ entry_count
> num_palette_entries
)
2845 return E_INVALIDARG
;
2847 records
= table_read_ensure(cpal
, GET_BE_DWORD(header
->offset_first_color_record
),
2848 sizeof(*records
) * GET_BE_WORD(header
->num_color_records
));
2850 return DWRITE_E_NOCOLOR
;
2852 first_entry_index
+= GET_BE_WORD(header
->color_record_indices
[palette
]);
2854 for (i
= 0; i
< entry_count
; i
++) {
2855 entries
[i
].u1
.r
= records
[first_entry_index
+ i
].red
/ 255.0f
;
2856 entries
[i
].u2
.g
= records
[first_entry_index
+ i
].green
/ 255.0f
;
2857 entries
[i
].u3
.b
= records
[first_entry_index
+ i
].blue
/ 255.0f
;
2858 entries
[i
].u4
.a
= records
[first_entry_index
+ i
].alpha
/ 255.0f
;
2864 static int colr_compare_gid(const void *g
, const void *r
)
2866 const struct colr_baseglyph_record
*record
= r
;
2867 UINT16 glyph
= *(UINT16
*)g
, GID
= GET_BE_WORD(record
->glyph
);
2872 else if (glyph
< GID
)
2878 HRESULT
opentype_get_colr_glyph(const struct dwrite_fonttable
*colr
, UINT16 glyph
, struct dwrite_colorglyph
*ret
)
2880 unsigned int num_baseglyph_records
, offset_baseglyph_records
;
2881 const struct colr_baseglyph_record
*record
;
2882 const struct colr_layer_record
*layer
;
2883 const struct colr_header
*header
;
2885 memset(ret
, 0, sizeof(*ret
));
2887 ret
->palette_index
= 0xffff;
2889 header
= table_read_ensure(colr
, 0, sizeof(*header
));
2893 num_baseglyph_records
= GET_BE_WORD(header
->num_baseglyph_records
);
2894 offset_baseglyph_records
= GET_BE_DWORD(header
->offset_baseglyph_records
);
2895 if (!table_read_ensure(colr
, offset_baseglyph_records
, num_baseglyph_records
* sizeof(*record
)))
2900 record
= bsearch(&glyph
, colr
->data
+ offset_baseglyph_records
, num_baseglyph_records
,
2901 sizeof(*record
), colr_compare_gid
);
2905 ret
->first_layer
= GET_BE_WORD(record
->first_layer_index
);
2906 ret
->num_layers
= GET_BE_WORD(record
->num_layers
);
2908 if ((layer
= table_read_ensure(colr
, GET_BE_DWORD(header
->offset_layer_records
),
2909 (ret
->first_layer
+ ret
->layer
) * sizeof(*layer
))))
2911 layer
+= ret
->first_layer
+ ret
->layer
;
2912 ret
->glyph
= GET_BE_WORD(layer
->glyph
);
2913 ret
->palette_index
= GET_BE_WORD(layer
->palette_index
);
2919 void opentype_colr_next_glyph(const struct dwrite_fonttable
*colr
, struct dwrite_colorglyph
*glyph
)
2921 const struct colr_layer_record
*layer
;
2922 const struct colr_header
*header
;
2924 /* iterated all the way through */
2925 if (glyph
->layer
== glyph
->num_layers
)
2928 if (!(header
= table_read_ensure(colr
, 0, sizeof(*header
))))
2933 if ((layer
= table_read_ensure(colr
, GET_BE_DWORD(header
->offset_layer_records
),
2934 (glyph
->first_layer
+ glyph
->layer
) * sizeof(*layer
))))
2936 layer
+= glyph
->first_layer
+ glyph
->layer
;
2937 glyph
->glyph
= GET_BE_WORD(layer
->glyph
);
2938 glyph
->palette_index
= GET_BE_WORD(layer
->palette_index
);
2942 static BOOL
opentype_has_font_table(IDWriteFontFace5
*fontface
, UINT32 tag
)
2944 BOOL exists
= FALSE
;
2950 hr
= IDWriteFontFace5_TryGetFontTable(fontface
, tag
, &data
, &size
, &context
, &exists
);
2955 IDWriteFontFace5_ReleaseFontTable(fontface
, context
);
2960 static unsigned int opentype_get_sbix_formats(IDWriteFontFace5
*fontface
)
2962 unsigned int num_strikes
, num_glyphs
, i
, j
, ret
= 0;
2963 const struct sbix_header
*sbix_header
;
2964 struct dwrite_fonttable table
;
2966 memset(&table
, 0, sizeof(table
));
2967 table
.exists
= TRUE
;
2969 if (!get_fontface_table(fontface
, MS_MAXP_TAG
, &table
))
2972 num_glyphs
= table_read_be_word(&table
, FIELD_OFFSET(struct maxp
, num_glyphs
));
2974 IDWriteFontFace5_ReleaseFontTable(fontface
, table
.context
);
2976 memset(&table
, 0, sizeof(table
));
2977 table
.exists
= TRUE
;
2979 if (!get_fontface_table(fontface
, MS_SBIX_TAG
, &table
))
2982 num_strikes
= table_read_be_dword(&table
, FIELD_OFFSET(struct sbix_header
, num_strikes
));
2983 sbix_header
= table_read_ensure(&table
, 0, FIELD_OFFSET(struct sbix_header
, strike_offset
[num_strikes
]));
2987 for (i
= 0; i
< num_strikes
; ++i
)
2989 unsigned int strike_offset
= GET_BE_DWORD(sbix_header
->strike_offset
[i
]);
2990 const struct sbix_strike
*strike
= table_read_ensure(&table
, strike_offset
,
2991 FIELD_OFFSET(struct sbix_strike
, glyphdata_offsets
[num_glyphs
+ 1]));
2996 for (j
= 0; j
< num_glyphs
; j
++)
2998 unsigned int offset
= GET_BE_DWORD(strike
->glyphdata_offsets
[j
]);
2999 unsigned int next_offset
= GET_BE_DWORD(strike
->glyphdata_offsets
[j
+ 1]);
3000 const struct sbix_glyph_data
*glyph_data
;
3002 if (offset
== next_offset
)
3005 glyph_data
= table_read_ensure(&table
, strike_offset
+ offset
, sizeof(*glyph_data
));
3009 switch (glyph_data
->graphic_type
)
3012 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PNG
;
3015 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_JPEG
;
3018 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_TIFF
;
3021 FIXME("unexpected bitmap format %s\n", debugstr_tag(GET_BE_DWORD(glyph_data
->graphic_type
)));
3027 IDWriteFontFace5_ReleaseFontTable(fontface
, table
.context
);
3032 static unsigned int opentype_get_cblc_formats(IDWriteFontFace5
*fontface
)
3034 const unsigned int format_mask
= DWRITE_GLYPH_IMAGE_FORMATS_PNG
|
3035 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8
;
3036 const struct cblc_bitmapsize_table
*sizes
;
3037 struct dwrite_fonttable cblc
= { 0 };
3038 unsigned int num_sizes
, i
, ret
= 0;
3039 const struct cblc_header
*header
;
3042 if (!get_fontface_table(fontface
, MS_CBLC_TAG
, &cblc
))
3045 num_sizes
= table_read_be_dword(&cblc
, FIELD_OFFSET(struct cblc_header
, num_sizes
));
3046 sizes
= table_read_ensure(&cblc
, sizeof(*header
), num_sizes
* sizeof(*sizes
));
3050 for (i
= 0; i
< num_sizes
; ++i
)
3052 BYTE bpp
= sizes
[i
].bit_depth
;
3054 if ((ret
& format_mask
) == format_mask
)
3057 if (bpp
== 1 || bpp
== 2 || bpp
== 4 || bpp
== 8)
3058 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PNG
;
3060 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8
;
3064 IDWriteFontFace5_ReleaseFontTable(fontface
, cblc
.context
);
3069 UINT32
opentype_get_glyph_image_formats(IDWriteFontFace5
*fontface
)
3071 UINT32 ret
= DWRITE_GLYPH_IMAGE_FORMATS_NONE
;
3073 if (opentype_has_font_table(fontface
, MS_GLYF_TAG
))
3074 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
;
3076 if (opentype_has_font_table(fontface
, MS_CFF__TAG
) ||
3077 opentype_has_font_table(fontface
, MS_CFF2_TAG
))
3078 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_CFF
;
3080 if (opentype_has_font_table(fontface
, MS_COLR_TAG
))
3081 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_COLR
;
3083 if (opentype_has_font_table(fontface
, MS_SVG__TAG
))
3084 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_SVG
;
3086 if (opentype_has_font_table(fontface
, MS_SBIX_TAG
))
3087 ret
|= opentype_get_sbix_formats(fontface
);
3089 if (opentype_has_font_table(fontface
, MS_CBLC_TAG
))
3090 ret
|= opentype_get_cblc_formats(fontface
);
3095 DWRITE_CONTAINER_TYPE
opentype_analyze_container_type(void const *data
, UINT32 data_size
)
3099 if (data_size
< sizeof(DWORD
))
3100 return DWRITE_CONTAINER_TYPE_UNKNOWN
;
3102 /* Both WOFF and WOFF2 start with 4 bytes signature. */
3103 signature
= *(DWORD
*)data
;
3108 return DWRITE_CONTAINER_TYPE_WOFF
;
3110 return DWRITE_CONTAINER_TYPE_WOFF2
;
3112 return DWRITE_CONTAINER_TYPE_UNKNOWN
;
3116 void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache
*cache
)
3118 cache
->font
->grab_font_table(cache
->context
, MS_GSUB_TAG
, &cache
->gsub
.table
.data
, &cache
->gsub
.table
.size
,
3119 &cache
->gsub
.table
.context
);
3121 if (cache
->gsub
.table
.data
)
3123 cache
->gsub
.script_list
= table_read_be_word(&cache
->gsub
.table
, FIELD_OFFSET(struct gpos_gsub_header
, script_list
));
3124 cache
->gsub
.feature_list
= table_read_be_word(&cache
->gsub
.table
, FIELD_OFFSET(struct gpos_gsub_header
, feature_list
));
3125 cache
->gsub
.lookup_list
= table_read_be_word(&cache
->gsub
.table
, FIELD_OFFSET(struct gpos_gsub_header
, lookup_list
));
3128 cache
->font
->grab_font_table(cache
->context
, MS_GPOS_TAG
, &cache
->gpos
.table
.data
, &cache
->gpos
.table
.size
,
3129 &cache
->gpos
.table
.context
);
3131 if (cache
->gpos
.table
.data
)
3133 cache
->gpos
.script_list
= table_read_be_word(&cache
->gpos
.table
,
3134 FIELD_OFFSET(struct gpos_gsub_header
, script_list
));
3135 cache
->gpos
.feature_list
= table_read_be_word(&cache
->gpos
.table
,
3136 FIELD_OFFSET(struct gpos_gsub_header
, feature_list
));
3137 cache
->gpos
.lookup_list
= table_read_be_word(&cache
->gpos
.table
,
3138 FIELD_OFFSET(struct gpos_gsub_header
, lookup_list
));
3141 cache
->font
->grab_font_table(cache
->context
, MS_GDEF_TAG
, &cache
->gdef
.table
.data
, &cache
->gdef
.table
.size
,
3142 &cache
->gdef
.table
.context
);
3144 if (cache
->gdef
.table
.data
)
3146 unsigned int version
= table_read_be_dword(&cache
->gdef
.table
, 0);
3148 cache
->gdef
.classdef
= table_read_be_word(&cache
->gdef
.table
, FIELD_OFFSET(struct gdef_header
, classdef
));
3149 cache
->gdef
.markattachclassdef
= table_read_be_word(&cache
->gdef
.table
,
3150 FIELD_OFFSET(struct gdef_header
, markattach_classdef
));
3151 if (version
>= 0x00010002)
3152 cache
->gdef
.markglyphsetdef
= table_read_be_word(&cache
->gdef
.table
,
3153 FIELD_OFFSET(struct gdef_header
, markglyphsetdef
));
3157 unsigned int opentype_layout_find_script(const struct scriptshaping_cache
*cache
, unsigned int kind
, DWORD script
,
3158 unsigned int *script_index
)
3160 const struct ot_gsubgpos_table
*table
= kind
== MS_GSUB_TAG
? &cache
->gsub
: &cache
->gpos
;
3161 UINT16 script_count
;
3164 *script_index
= ~0u;
3166 script_count
= table_read_be_word(&table
->table
, table
->script_list
);
3170 for (i
= 0; i
< script_count
; i
++)
3172 unsigned int tag
= table_read_dword(&table
->table
, table
->script_list
+ FIELD_OFFSET(struct ot_script_list
, scripts
) +
3173 i
* sizeof(struct ot_script_record
));
3187 unsigned int opentype_layout_find_language(const struct scriptshaping_cache
*cache
, unsigned int kind
, DWORD language
,
3188 unsigned int script_index
, unsigned int *language_index
)
3190 const struct ot_gsubgpos_table
*table
= kind
== MS_GSUB_TAG
? &cache
->gsub
: &cache
->gpos
;
3191 UINT16 table_offset
, lang_count
;
3194 *language_index
= ~0u;
3196 table_offset
= table_read_be_word(&table
->table
, table
->script_list
+ FIELD_OFFSET(struct ot_script_list
, scripts
) +
3197 script_index
* sizeof(struct ot_script_record
) + FIELD_OFFSET(struct ot_script_record
, script
));
3201 lang_count
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
+
3202 FIELD_OFFSET(struct ot_script
, langsys_count
));
3203 for (i
= 0; i
< lang_count
; i
++)
3205 unsigned int tag
= table_read_dword(&table
->table
, table
->script_list
+ table_offset
+
3206 FIELD_OFFSET(struct ot_script
, langsys
) + i
* sizeof(struct ot_langsys_record
));
3208 if (tag
== language
)
3210 *language_index
= i
;
3215 /* Try 'defaultLangSys' if it's set. */
3216 if (table_read_be_word(&table
->table
, table
->script_list
+ table_offset
))
3222 static int gdef_class_compare_format2(const void *g
, const void *r
)
3224 const struct ot_gdef_class_range
*range
= r
;
3225 UINT16 glyph
= *(UINT16
*)g
;
3227 if (glyph
< GET_BE_WORD(range
->start_glyph
))
3229 else if (glyph
> GET_BE_WORD(range
->end_glyph
))
3235 static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable
*table
,
3236 unsigned int offset
, UINT16 glyph
)
3238 WORD format
= table_read_be_word(table
, offset
), count
;
3239 unsigned int glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3243 const struct ot_gdef_classdef_format1
*format1
;
3245 count
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gdef_classdef_format1
, glyph_count
));
3246 format1
= table_read_ensure(table
, offset
, FIELD_OFFSET(struct ot_gdef_classdef_format1
, classes
[count
]));
3249 WORD start_glyph
= GET_BE_WORD(format1
->start_glyph
);
3250 if (glyph
>= start_glyph
&& (glyph
- start_glyph
) < count
)
3252 glyph_class
= GET_BE_WORD(format1
->classes
[glyph
- start_glyph
]);
3253 if (glyph_class
> GDEF_CLASS_MAX
)
3254 glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3258 else if (format
== 2)
3260 const struct ot_gdef_classdef_format2
*format2
;
3262 count
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gdef_classdef_format2
, range_count
));
3263 format2
= table_read_ensure(table
, offset
, FIELD_OFFSET(struct ot_gdef_classdef_format2
, ranges
[count
]));
3266 const struct ot_gdef_class_range
*range
= bsearch(&glyph
, format2
->ranges
, count
,
3267 sizeof(struct ot_gdef_class_range
), gdef_class_compare_format2
);
3268 glyph_class
= range
&& glyph
<= GET_BE_WORD(range
->end_glyph
) ?
3269 GET_BE_WORD(range
->glyph_class
) : GDEF_CLASS_UNCLASSIFIED
;
3270 if (glyph_class
> GDEF_CLASS_MAX
)
3271 glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3275 WARN("Unknown GDEF format %u.\n", format
);
3280 static unsigned int opentype_set_glyph_props(struct scriptshaping_context
*context
, unsigned int idx
)
3282 struct scriptshaping_cache
*cache
= context
->cache
;
3283 unsigned int glyph_class
= 0, props
;
3285 if (cache
->gdef
.classdef
)
3287 glyph_class
= opentype_layout_get_glyph_class(&cache
->gdef
.table
, cache
->gdef
.classdef
,
3288 context
->u
.buffer
.glyphs
[idx
]);
3291 switch (glyph_class
)
3293 case GDEF_CLASS_BASE
:
3294 props
= GLYPH_PROP_BASE
;
3296 case GDEF_CLASS_LIGATURE
:
3297 props
= GLYPH_PROP_LIGATURE
;
3299 case GDEF_CLASS_MARK
:
3300 props
= GLYPH_PROP_MARK
;
3301 if (cache
->gdef
.markattachclassdef
)
3303 glyph_class
= opentype_layout_get_glyph_class(&cache
->gdef
.table
, cache
->gdef
.markattachclassdef
,
3304 context
->u
.buffer
.glyphs
[idx
]);
3305 props
|= glyph_class
<< 8;
3312 context
->glyph_infos
[idx
].props
= props
;
3317 static void opentype_set_subst_glyph_props(struct scriptshaping_context
*context
, unsigned int idx
)
3319 unsigned int glyph_props
= opentype_set_glyph_props(context
, idx
) & LOOKUP_FLAG_IGNORE_MASK
;
3320 context
->u
.subst
.glyph_props
[idx
].isDiacritic
= !!(glyph_props
== GLYPH_PROP_MARK
);
3321 context
->u
.subst
.glyph_props
[idx
].isZeroWidthSpace
= !!(glyph_props
== GLYPH_PROP_MARK
);
3324 struct coverage_compare_format1_context
3327 const UINT16
*table_base
;
3328 unsigned int *coverage_index
;
3331 static int coverage_compare_format1(const void *left
, const void *right
)
3333 const struct coverage_compare_format1_context
*context
= left
;
3334 UINT16 glyph
= GET_BE_WORD(*(UINT16
*)right
);
3337 ret
= context
->glyph
- glyph
;
3339 *context
->coverage_index
= (UINT16
*)right
- context
->table_base
;
3344 static int coverage_compare_format2(const void *g
, const void *r
)
3346 const struct ot_coverage_range
*range
= r
;
3347 UINT16 glyph
= *(UINT16
*)g
;
3349 if (glyph
< GET_BE_WORD(range
->start_glyph
))
3351 else if (glyph
> GET_BE_WORD(range
->end_glyph
))
3357 static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable
*table
, unsigned int coverage
,
3360 WORD format
= table_read_be_word(table
, coverage
), count
;
3362 count
= table_read_be_word(table
, coverage
+ 2);
3366 const struct ot_coverage_format1
*format1
= table_read_ensure(table
, coverage
,
3367 FIELD_OFFSET(struct ot_coverage_format1
, glyphs
[count
]));
3368 struct coverage_compare_format1_context context
;
3369 unsigned int coverage_index
= GLYPH_NOT_COVERED
;
3373 context
.glyph
= glyph
;
3374 context
.table_base
= format1
->glyphs
;
3375 context
.coverage_index
= &coverage_index
;
3377 bsearch(&context
, format1
->glyphs
, count
, sizeof(glyph
), coverage_compare_format1
);
3380 return coverage_index
;
3382 else if (format
== 2)
3384 const struct ot_coverage_format2
*format2
= table_read_ensure(table
, coverage
,
3385 FIELD_OFFSET(struct ot_coverage_format2
, ranges
[count
]));
3388 const struct ot_coverage_range
*range
= bsearch(&glyph
, format2
->ranges
, count
,
3389 sizeof(struct ot_coverage_range
), coverage_compare_format2
);
3390 return range
&& glyph
<= GET_BE_WORD(range
->end_glyph
) ?
3391 GET_BE_WORD(range
->startcoverage_index
) + glyph
- GET_BE_WORD(range
->start_glyph
) :
3396 WARN("Unknown coverage format %u.\n", format
);
3401 static inline unsigned int dwrite_popcount(unsigned int x
)
3403 #ifdef HAVE___BUILTIN_POPCOUNT
3404 return __builtin_popcount(x
);
3406 x
-= x
>> 1 & 0x55555555;
3407 x
= (x
& 0x33333333) + (x
>> 2 & 0x33333333);
3408 return ((x
+ (x
>> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
3412 static float opentype_scale_gpos_be_value(WORD value
, float emsize
, UINT16 upem
)
3414 return (short)GET_BE_WORD(value
) * emsize
/ upem
;
3417 static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context
*context
, unsigned int offset
)
3419 const struct dwrite_fonttable
*table
= &context
->table
->table
;
3420 unsigned int start_size
, end_size
, format
, value_word
;
3421 unsigned int index
, ppem
, mask
;
3427 start_size
= table_read_be_word(table
, offset
);
3428 end_size
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gpos_device_table
, end_size
));
3430 ppem
= context
->emsize
;
3431 if (ppem
< start_size
|| ppem
> end_size
)
3434 format
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gpos_device_table
, format
));
3436 if (format
< 1 || format
> 3)
3439 index
= ppem
- start_size
;
3441 value_word
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gpos_device_table
, values
[index
>> (4 - format
)]));
3442 mask
= 0xffff >> (16 - (1 << format
));
3444 value
= (value_word
>> ((index
% (4 - format
)) * (1 << format
))) & mask
;
3446 if ((unsigned int)value
>= ((mask
+ 1) >> 1))
3452 static void opentype_layout_apply_gpos_value(struct scriptshaping_context
*context
, unsigned int table_offset
,
3453 WORD value_format
, const WORD
*values
, unsigned int glyph
)
3455 const struct scriptshaping_cache
*cache
= context
->cache
;
3456 DWRITE_GLYPH_OFFSET
*offset
= &context
->offsets
[glyph
];
3457 float *advance
= &context
->advances
[glyph
];
3462 if (value_format
& GPOS_VALUE_X_PLACEMENT
)
3464 offset
->advanceOffset
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3467 if (value_format
& GPOS_VALUE_Y_PLACEMENT
)
3469 offset
->ascenderOffset
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3472 if (value_format
& GPOS_VALUE_X_ADVANCE
)
3474 *advance
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3477 if (value_format
& GPOS_VALUE_Y_ADVANCE
)
3481 if (value_format
& GPOS_VALUE_X_PLACEMENT_DEVICE
)
3483 offset
->advanceOffset
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3486 if (value_format
& GPOS_VALUE_Y_PLACEMENT_DEVICE
)
3488 offset
->ascenderOffset
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3491 if (value_format
& GPOS_VALUE_X_ADVANCE_DEVICE
)
3493 *advance
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3496 if (value_format
& GPOS_VALUE_Y_ADVANCE_DEVICE
)
3502 static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context
*context
,
3503 unsigned int lookup_offset
, unsigned int subtable
)
3505 unsigned int subtable_offset
= table_read_be_word(&context
->table
->table
, lookup_offset
+
3506 FIELD_OFFSET(struct ot_lookup_table
, subtable
[subtable
]));
3508 return lookup_offset
+ subtable_offset
;
3513 unsigned int offset
;
3514 unsigned int subtable_count
;
3520 /* First two to fit matching callback result. */
3526 struct match_context
;
3529 const struct match_context
*mc
;
3530 unsigned int subtable_offset
;
3533 typedef BOOL (*p_match_func
)(UINT16 glyph
, UINT16 glyph_data
, const struct match_data
*match_data
);
3535 struct match_context
3537 struct scriptshaping_context
*context
;
3538 unsigned int backtrack_offset
;
3539 unsigned int input_offset
;
3540 unsigned int lookahead_offset
;
3541 p_match_func match_func
;
3542 const struct lookup
*lookup
;
3545 struct glyph_iterator
3547 struct scriptshaping_context
*context
;
3552 p_match_func match_func
;
3553 const UINT16
*glyph_data
;
3554 const struct match_data
*match_data
;
3557 static void glyph_iterator_init(struct scriptshaping_context
*context
, unsigned int flags
, unsigned int pos
,
3558 unsigned int len
, struct glyph_iterator
*iter
)
3560 iter
->context
= context
;
3561 iter
->flags
= flags
;
3565 iter
->match_func
= NULL
;
3566 iter
->match_data
= NULL
;
3567 iter
->glyph_data
= NULL
;
3570 struct ot_gdef_mark_glyph_sets
3577 static BOOL
opentype_match_glyph_func(UINT16 glyph
, UINT16 glyph_data
, const struct match_data
*data
)
3579 return glyph
== glyph_data
;
3582 static BOOL
opentype_match_class_func(UINT16 glyph
, UINT16 glyph_data
, const struct match_data
*data
)
3584 const struct match_context
*mc
= data
->mc
;
3585 UINT16 glyph_class
= opentype_layout_get_glyph_class(&mc
->context
->table
->table
, data
->subtable_offset
, glyph
);
3586 return glyph_class
== glyph_data
;
3589 static BOOL
opentype_match_coverage_func(UINT16 glyph
, UINT16 glyph_data
, const struct match_data
*data
)
3591 const struct match_context
*mc
= data
->mc
;
3592 return opentype_layout_is_glyph_covered(&mc
->context
->table
->table
, data
->subtable_offset
+ glyph_data
, glyph
)
3593 != GLYPH_NOT_COVERED
;
3596 static BOOL
opentype_layout_mark_set_covers(const struct scriptshaping_cache
*cache
, unsigned int set_index
,
3599 unsigned int format
, offset
= cache
->gdef
.markglyphsetdef
, coverage_offset
, set_count
;
3604 format
= table_read_be_word(&cache
->gdef
.table
, offset
);
3608 set_count
= table_read_be_word(&cache
->gdef
.table
, offset
+ 2);
3609 if (!set_count
|| set_index
>= set_count
)
3612 coverage_offset
= table_read_be_dword(&cache
->gdef
.table
, offset
+ 2 + set_index
* sizeof(coverage_offset
));
3613 return opentype_layout_is_glyph_covered(&cache
->gdef
.table
, offset
+ coverage_offset
, glyph
) != GLYPH_NOT_COVERED
;
3616 WARN("Unexpected MarkGlyphSets format %#x.\n", format
);
3621 static BOOL
lookup_is_glyph_match(const struct scriptshaping_context
*context
, unsigned int idx
, unsigned int match_props
)
3623 unsigned int glyph_props
= context
->glyph_infos
[idx
].props
;
3624 UINT16 glyph
= context
->u
.buffer
.glyphs
[idx
];
3626 if (glyph_props
& match_props
& LOOKUP_FLAG_IGNORE_MASK
)
3629 if (!(glyph_props
& GLYPH_PROP_MARK
))
3632 if (match_props
& LOOKUP_FLAG_USE_MARK_FILTERING_SET
)
3633 return opentype_layout_mark_set_covers(context
->cache
, match_props
>> 16, glyph
);
3635 if (match_props
& LOOKUP_FLAG_MARK_ATTACHMENT_TYPE
)
3636 return (match_props
& LOOKUP_FLAG_MARK_ATTACHMENT_TYPE
) == (glyph_props
& LOOKUP_FLAG_MARK_ATTACHMENT_TYPE
);
3641 static enum iterator_match
glyph_iterator_may_skip(const struct glyph_iterator
*iter
)
3643 if (!lookup_is_glyph_match(iter
->context
, iter
->pos
, iter
->flags
))
3649 static enum iterator_match
glyph_iterator_may_match(const struct glyph_iterator
*iter
)
3651 if (!(iter
->mask
& iter
->context
->glyph_infos
[iter
->pos
].mask
))
3654 /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that
3655 in all matching functions. */
3656 if (iter
->match_func
)
3657 return !!iter
->match_func(iter
->context
->u
.buffer
.glyphs
[iter
->pos
], GET_BE_WORD(*iter
->glyph_data
), iter
->match_data
);
3662 static BOOL
glyph_iterator_next(struct glyph_iterator
*iter
)
3664 enum iterator_match skip
, match
;
3666 while (iter
->pos
+ iter
->len
< iter
->context
->glyph_count
)
3670 skip
= glyph_iterator_may_skip(iter
);
3671 if (skip
== ITER_YES
)
3674 match
= glyph_iterator_may_match(iter
);
3675 if (match
== ITER_YES
|| (match
== ITER_MAYBE
&& skip
== ITER_NO
))
3678 if (iter
->glyph_data
)
3683 if (skip
== ITER_NO
)
3690 static BOOL
glyph_iterator_prev(struct glyph_iterator
*iter
)
3692 enum iterator_match skip
, match
;
3694 while (iter
->pos
> iter
->len
- 1)
3698 skip
= glyph_iterator_may_skip(iter
);
3699 if (skip
== ITER_YES
)
3702 match
= glyph_iterator_may_match(iter
);
3703 if (match
== ITER_YES
|| (match
== ITER_MAYBE
&& skip
== ITER_NO
))
3706 if (iter
->glyph_data
)
3711 if (skip
== ITER_NO
)
3720 unsigned short index
;
3721 unsigned short type
;
3722 unsigned short flags
;
3723 unsigned short subtable_count
;
3726 unsigned int offset
;
3729 static BOOL
opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context
*context
,
3730 const struct lookup
*lookup
, unsigned int subtable_offset
)
3732 const struct dwrite_fonttable
*table
= &context
->table
->table
;
3733 UINT16 format
, value_format
, value_len
, coverage
, glyph
;
3735 unsigned int coverage_index
;
3737 format
= table_read_be_word(table
, subtable_offset
);
3739 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_singlepos_format1
, coverage
));
3740 value_format
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_singlepos_format1
, value_format
));
3741 value_len
= dwrite_popcount(value_format
);
3743 glyph
= context
->u
.pos
.glyphs
[context
->cur
];
3747 const struct ot_gpos_singlepos_format1
*format1
= table_read_ensure(table
, subtable_offset
,
3748 FIELD_OFFSET(struct ot_gpos_singlepos_format1
, value
[value_len
]));
3750 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
3751 if (coverage_index
== GLYPH_NOT_COVERED
)
3754 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format
, format1
->value
, context
->cur
);
3756 else if (format
== 2)
3758 WORD value_count
= table_read_be_word(table
, subtable_offset
+
3759 FIELD_OFFSET(struct ot_gpos_singlepos_format2
, value_count
));
3760 const struct ot_gpos_singlepos_format2
*format2
= table_read_ensure(table
, subtable_offset
,
3761 FIELD_OFFSET(struct ot_gpos_singlepos_format2
, values
) + value_count
* value_len
* sizeof(WORD
));
3763 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
3764 if (coverage_index
== GLYPH_NOT_COVERED
|| coverage_index
>= value_count
)
3767 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format
, &format2
->values
[coverage_index
* value_len
],
3772 WARN("Unknown single adjustment format %u.\n", format
);
3781 static int gpos_pair_adjustment_compare_format1(const void *g
, const void *r
)
3783 const struct ot_gpos_pairvalue
*pairvalue
= r
;
3784 UINT16 second_glyph
= GET_BE_WORD(pairvalue
->second_glyph
);
3785 return *(UINT16
*)g
- second_glyph
;
3788 static BOOL
opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context
*context
,
3789 const struct lookup
*lookup
, unsigned int subtable_offset
)
3791 const struct dwrite_fonttable
*table
= &context
->table
->table
;
3792 unsigned int first_glyph
, second_glyph
;
3793 struct glyph_iterator iter_pair
;
3794 WORD format
, coverage
;
3796 WORD value_format1
, value_format2
, value_len1
, value_len2
;
3797 unsigned int coverage_index
;
3799 glyph_iterator_init(context
, lookup
->flags
, context
->cur
, 1, &iter_pair
);
3800 if (!glyph_iterator_next(&iter_pair
))
3803 if (context
->is_rtl
)
3805 first_glyph
= iter_pair
.pos
;
3806 second_glyph
= context
->cur
;
3810 first_glyph
= context
->cur
;
3811 second_glyph
= iter_pair
.pos
;
3814 format
= table_read_be_word(table
, subtable_offset
);
3816 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_pairpos_format1
, coverage
));
3820 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, context
->u
.pos
.glyphs
[first_glyph
]);
3821 if (coverage_index
== GLYPH_NOT_COVERED
)
3826 const struct ot_gpos_pairpos_format1
*format1
;
3827 WORD pairset_count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_pairpos_format1
,
3829 unsigned int pairvalue_len
, pairset_offset
;
3830 const struct ot_gpos_pairset
*pairset
;
3831 const WORD
*pairvalue
;
3832 WORD pairvalue_count
;
3834 if (!pairset_count
|| coverage_index
>= pairset_count
)
3837 format1
= table_read_ensure(table
, subtable_offset
, FIELD_OFFSET(struct ot_gpos_pairpos_format1
, pairsets
[pairset_count
]));
3841 /* Ordered paired values. */
3842 pairvalue_count
= table_read_be_word(table
, subtable_offset
+ GET_BE_WORD(format1
->pairsets
[coverage_index
]));
3843 if (!pairvalue_count
)
3846 /* Structure length is variable, but does not change across the subtable. */
3847 value_format1
= GET_BE_WORD(format1
->value_format1
) & 0xff;
3848 value_format2
= GET_BE_WORD(format1
->value_format2
) & 0xff;
3850 value_len1
= dwrite_popcount(value_format1
);
3851 value_len2
= dwrite_popcount(value_format2
);
3852 pairvalue_len
= FIELD_OFFSET(struct ot_gpos_pairvalue
, data
) + value_len1
* sizeof(WORD
) +
3853 value_len2
* sizeof(WORD
);
3855 pairset_offset
= subtable_offset
+ GET_BE_WORD(format1
->pairsets
[coverage_index
]);
3856 pairset
= table_read_ensure(table
, subtable_offset
+ pairset_offset
, pairvalue_len
* pairvalue_count
);
3860 pairvalue
= bsearch(&context
->u
.pos
.glyphs
[second_glyph
], pairset
->pairvalues
, pairvalue_count
,
3861 pairvalue_len
, gpos_pair_adjustment_compare_format1
);
3865 pairvalue
+= 1; /* Skip SecondGlyph. */
3866 opentype_layout_apply_gpos_value(context
, pairset_offset
, value_format1
, pairvalue
, first_glyph
);
3867 opentype_layout_apply_gpos_value(context
, pairset_offset
, value_format2
, pairvalue
+ value_len1
,
3870 context
->cur
= iter_pair
.pos
;
3874 else if (format
== 2)
3876 const struct ot_gpos_pairpos_format2
*format2
;
3877 WORD class1_count
, class2_count
;
3878 unsigned int class1
, class2
;
3879 const WCHAR
*values
;
3881 value_format1
= table_read_be_word(table
, subtable_offset
+
3882 FIELD_OFFSET(struct ot_gpos_pairpos_format2
, value_format1
)) & 0xff;
3883 value_format2
= table_read_be_word(table
, subtable_offset
+
3884 FIELD_OFFSET(struct ot_gpos_pairpos_format2
, value_format2
)) & 0xff;
3886 class1_count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_pairpos_format2
, class1_count
));
3887 class2_count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_pairpos_format2
, class2_count
));
3889 value_len1
= dwrite_popcount(value_format1
);
3890 value_len2
= dwrite_popcount(value_format2
);
3892 format2
= table_read_ensure(table
, subtable_offset
, FIELD_OFFSET(struct ot_gpos_pairpos_format2
,
3893 values
[class1_count
* class2_count
* (value_len1
+ value_len2
)]));
3897 class1
= opentype_layout_get_glyph_class(table
, subtable_offset
+ GET_BE_WORD(format2
->class_def1
),
3898 context
->u
.pos
.glyphs
[first_glyph
]);
3899 class2
= opentype_layout_get_glyph_class(table
, subtable_offset
+ GET_BE_WORD(format2
->class_def2
),
3900 context
->u
.pos
.glyphs
[second_glyph
]);
3902 if (!(class1
< class1_count
&& class2
< class2_count
))
3905 values
= &format2
->values
[(class1
* class2_count
+ class2
) * (value_len1
+ value_len2
)];
3906 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format1
, values
, first_glyph
);
3907 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format2
, values
+ value_len1
,
3910 context
->cur
= iter_pair
.pos
;
3916 WARN("Unknown pair adjustment format %u.\n", format
);
3923 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context
*context
, unsigned int anchor_offset
,
3924 unsigned int glyph_index
, float *x
, float *y
)
3926 const struct scriptshaping_cache
*cache
= context
->cache
;
3927 const struct dwrite_fonttable
*table
= &context
->table
->table
;
3929 WORD format
= table_read_be_word(table
, anchor_offset
);
3935 const struct ot_gpos_anchor_format1
*format1
= table_read_ensure(table
, anchor_offset
, sizeof(*format1
));
3939 *x
= opentype_scale_gpos_be_value(format1
->x_coord
, context
->emsize
, cache
->upem
);
3940 *y
= opentype_scale_gpos_be_value(format1
->y_coord
, context
->emsize
, cache
->upem
);
3943 else if (format
== 2)
3945 const struct ot_gpos_anchor_format2
*format2
= table_read_ensure(table
, anchor_offset
, sizeof(*format2
));
3949 if (context
->measuring_mode
!= DWRITE_MEASURING_MODE_NATURAL
)
3950 FIXME("Use outline anchor point for glyph %u.\n", context
->u
.pos
.glyphs
[glyph_index
]);
3952 *x
= opentype_scale_gpos_be_value(format2
->x_coord
, context
->emsize
, cache
->upem
);
3953 *y
= opentype_scale_gpos_be_value(format2
->y_coord
, context
->emsize
, cache
->upem
);
3956 else if (format
== 3)
3958 const struct ot_gpos_anchor_format3
*format3
= table_read_ensure(table
, anchor_offset
, sizeof(*format3
));
3962 *x
= opentype_scale_gpos_be_value(format3
->x_coord
, context
->emsize
, cache
->upem
);
3963 *y
= opentype_scale_gpos_be_value(format3
->y_coord
, context
->emsize
, cache
->upem
);
3965 if (context
->measuring_mode
!= DWRITE_MEASURING_MODE_NATURAL
)
3967 if (format3
->x_dev_offset
)
3968 *x
+= opentype_layout_gpos_get_dev_value(context
, anchor_offset
+ GET_BE_WORD(format3
->x_dev_offset
));
3969 if (format3
->y_dev_offset
)
3970 *y
+= opentype_layout_gpos_get_dev_value(context
, anchor_offset
+ GET_BE_WORD(format3
->y_dev_offset
));
3975 WARN("Unknown anchor format %u.\n", format
);
3978 static BOOL
opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context
*context
,
3979 const struct lookup
*lookup
, unsigned int subtable_offset
)
3981 const struct dwrite_fonttable
*table
= &context
->table
->table
;
3982 UINT16 format
, glyph
;
3984 format
= table_read_be_word(table
, subtable_offset
);
3985 glyph
= context
->u
.pos
.glyphs
[context
->cur
];
3989 WORD coverage_offset
= table_read_be_word(table
, subtable_offset
+
3990 FIELD_OFFSET(struct ot_gpos_cursive_format1
, coverage
));
3991 unsigned int glyph_index
, entry_count
, entry_anchor
, exit_anchor
;
3992 float entry_x
, entry_y
, exit_x
, exit_y
, delta
;
3993 struct glyph_iterator prev_iter
;
3995 if (!coverage_offset
)
3998 entry_count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_cursive_format1
, count
));
4000 glyph_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage_offset
, glyph
);
4001 if (glyph_index
== GLYPH_NOT_COVERED
|| glyph_index
>= entry_count
)
4004 entry_anchor
= table_read_be_word(table
, subtable_offset
+
4005 FIELD_OFFSET(struct ot_gpos_cursive_format1
, anchors
[glyph_index
* 2]));
4009 glyph_iterator_init(context
, lookup
->flags
, context
->cur
, 1, &prev_iter
);
4010 if (!glyph_iterator_prev(&prev_iter
))
4013 glyph_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage_offset
,
4014 context
->u
.pos
.glyphs
[prev_iter
.pos
]);
4015 if (glyph_index
== GLYPH_NOT_COVERED
|| glyph_index
>= entry_count
)
4018 exit_anchor
= table_read_be_word(table
, subtable_offset
+
4019 FIELD_OFFSET(struct ot_gpos_cursive_format1
, anchors
[glyph_index
* 2 + 1]));
4023 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ exit_anchor
, prev_iter
.pos
, &exit_x
, &exit_y
);
4024 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ entry_anchor
, context
->cur
, &entry_x
, &entry_y
);
4026 if (context
->is_rtl
)
4028 delta
= exit_x
+ context
->offsets
[prev_iter
.pos
].advanceOffset
;
4029 context
->advances
[prev_iter
.pos
] -= delta
;
4030 context
->advances
[context
->cur
] = entry_x
+ context
->offsets
[context
->cur
].advanceOffset
;
4031 context
->offsets
[prev_iter
.pos
].advanceOffset
-= delta
;
4035 delta
= entry_x
+ context
->offsets
[context
->cur
].advanceOffset
;
4036 context
->advances
[prev_iter
.pos
] = exit_x
+ context
->offsets
[prev_iter
.pos
].advanceOffset
;
4037 context
->advances
[context
->cur
] -= delta
;
4038 context
->offsets
[context
->cur
].advanceOffset
-= delta
;
4041 if (lookup
->flags
& LOOKUP_FLAG_RTL
)
4042 context
->offsets
[prev_iter
.pos
].ascenderOffset
= entry_y
- exit_y
;
4044 context
->offsets
[context
->cur
].ascenderOffset
= exit_y
- entry_y
;
4050 WARN("Unknown cursive attachment format %u.\n", format
);
4057 static BOOL
opentype_layout_apply_mark_array(struct scriptshaping_context
*context
, unsigned int subtable_offset
,
4058 unsigned int mark_array
, unsigned int mark_index
, unsigned int glyph_index
, unsigned int anchors_matrix
,
4059 unsigned int class_count
, unsigned int glyph_pos
)
4061 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4062 unsigned int mark_class
, mark_count
, glyph_count
;
4063 const struct ot_gpos_mark_record
*record
;
4064 float mark_x
, mark_y
, base_x
, base_y
;
4065 const UINT16
*anchors
;
4067 mark_count
= table_read_be_word(table
, subtable_offset
+ mark_array
);
4068 if (mark_index
>= mark_count
) return FALSE
;
4070 if (!(record
= table_read_ensure(table
, subtable_offset
+ mark_array
+
4071 FIELD_OFFSET(struct ot_gpos_mark_array
, records
[mark_index
]), sizeof(*record
))))
4076 mark_class
= GET_BE_WORD(record
->mark_class
);
4077 if (mark_class
>= class_count
) return FALSE
;
4079 glyph_count
= table_read_be_word(table
, subtable_offset
+ anchors_matrix
);
4080 if (glyph_index
>= glyph_count
) return FALSE
;
4082 /* Anchors data is stored as two dimensional array [glyph_count][class_count], starting with row count field. */
4083 anchors
= table_read_ensure(table
, subtable_offset
+ anchors_matrix
+ 2, glyph_count
* class_count
* sizeof(*anchors
));
4084 if (!anchors
) return FALSE
;
4086 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ mark_array
+ GET_BE_WORD(record
->mark_anchor
),
4087 context
->cur
, &mark_x
, &mark_y
);
4088 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ anchors_matrix
+
4089 GET_BE_WORD(anchors
[glyph_index
* class_count
+ mark_class
]), glyph_pos
, &base_x
, &base_y
);
4091 if (context
->is_rtl
)
4092 context
->offsets
[context
->cur
].advanceOffset
= mark_x
- base_x
;
4094 context
->offsets
[context
->cur
].advanceOffset
= -context
->advances
[glyph_pos
] + base_x
- mark_x
;
4096 context
->offsets
[context
->cur
].ascenderOffset
= base_y
- mark_y
;
4102 static BOOL
opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context
*context
,
4103 const struct lookup
*lookup
, unsigned int subtable_offset
)
4105 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4108 format
= table_read_be_word(table
, subtable_offset
);
4112 const struct ot_gpos_mark_to_base_format1
*format1
;
4113 unsigned int base_index
, mark_index
;
4114 struct glyph_iterator base_iter
;
4116 if (!(format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
)))) return FALSE
;
4118 mark_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->mark_coverage
),
4119 context
->u
.pos
.glyphs
[context
->cur
]);
4120 if (mark_index
== GLYPH_NOT_COVERED
) return FALSE
;
4122 /* Look back for first base glyph. */
4123 glyph_iterator_init(context
, LOOKUP_FLAG_IGNORE_MARKS
, context
->cur
, 1, &base_iter
);
4124 if (!glyph_iterator_prev(&base_iter
))
4127 base_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->base_coverage
),
4128 context
->u
.pos
.glyphs
[base_iter
.pos
]);
4129 if (base_index
== GLYPH_NOT_COVERED
) return FALSE
;
4131 return opentype_layout_apply_mark_array(context
, subtable_offset
, GET_BE_WORD(format1
->mark_array
), mark_index
,
4132 base_index
, GET_BE_WORD(format1
->base_array
), GET_BE_WORD(format1
->mark_class_count
), base_iter
.pos
);
4136 WARN("Unknown mark-to-base format %u.\n", format
);
4143 static const UINT16
* table_read_array_be_word(const struct dwrite_fonttable
*table
, unsigned int offset
,
4144 unsigned int index
, UINT16
*data
)
4146 unsigned int count
= table_read_be_word(table
, offset
);
4147 const UINT16
*array
;
4149 if (index
!= ~0u && index
>= count
) return NULL
;
4150 if (!(array
= table_read_ensure(table
, offset
+ 2, count
* sizeof(*array
)))) return FALSE
;
4151 *data
= index
== ~0u ? count
: GET_BE_WORD(array
[index
]);
4155 static BOOL
opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context
*context
,
4156 const struct lookup
*lookup
, unsigned int subtable_offset
)
4158 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4161 format
= table_read_be_word(table
, subtable_offset
);
4165 unsigned int mark_index
, lig_index
, comp_index
, class_count
, comp_count
;
4166 const struct ot_gpos_mark_to_lig_format1
*format1
;
4167 struct glyph_iterator lig_iter
;
4168 unsigned int lig_array
;
4171 if (!(format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
)))) return FALSE
;
4173 mark_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->mark_coverage
),
4174 context
->u
.pos
.glyphs
[context
->cur
]);
4175 if (mark_index
== GLYPH_NOT_COVERED
) return FALSE
;
4177 glyph_iterator_init(context
, LOOKUP_FLAG_IGNORE_MARKS
, context
->cur
, 1, &lig_iter
);
4178 if (!glyph_iterator_prev(&lig_iter
))
4181 lig_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->lig_coverage
),
4182 context
->u
.pos
.glyphs
[lig_iter
.pos
]);
4183 if (lig_index
== GLYPH_NOT_COVERED
) return FALSE
;
4185 class_count
= GET_BE_WORD(format1
->mark_class_count
);
4187 lig_array
= GET_BE_WORD(format1
->lig_array
);
4189 if (!table_read_array_be_word(table
, subtable_offset
+ lig_array
, lig_index
, &lig_attach
)) return FALSE
;
4191 comp_count
= table_read_be_word(table
, subtable_offset
+ lig_array
+ lig_attach
);
4192 if (!comp_count
) return FALSE
;
4194 comp_index
= context
->u
.buffer
.glyph_props
[lig_iter
.pos
].components
-
4195 context
->u
.buffer
.glyph_props
[context
->cur
].lig_component
- 1;
4196 if (comp_index
>= comp_count
) return FALSE
;
4198 return opentype_layout_apply_mark_array(context
, subtable_offset
, GET_BE_WORD(format1
->mark_array
), mark_index
,
4199 comp_index
, lig_array
+ lig_attach
, class_count
, lig_iter
.pos
);
4202 WARN("Unknown mark-to-ligature format %u.\n", format
);
4207 static BOOL
opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context
*context
,
4208 const struct lookup
*lookup
, unsigned int subtable_offset
)
4210 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4213 format
= table_read_be_word(table
, subtable_offset
);
4217 const struct ot_gpos_mark_to_mark_format1
*format1
;
4218 unsigned int mark1_index
, mark2_index
;
4219 struct glyph_iterator mark_iter
;
4221 if (!(format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
)))) return FALSE
;
4223 mark1_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->mark1_coverage
),
4224 context
->u
.pos
.glyphs
[context
->cur
]);
4225 if (mark1_index
== GLYPH_NOT_COVERED
) return FALSE
;
4227 glyph_iterator_init(context
, lookup
->flags
& ~LOOKUP_FLAG_IGNORE_MASK
, context
->cur
, 1, &mark_iter
);
4228 if (!glyph_iterator_prev(&mark_iter
))
4231 if (!context
->u
.pos
.glyph_props
[mark_iter
.pos
].isDiacritic
)
4234 mark2_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->mark2_coverage
),
4235 context
->u
.pos
.glyphs
[mark_iter
.pos
]);
4236 if (mark2_index
== GLYPH_NOT_COVERED
) return FALSE
;
4238 return opentype_layout_apply_mark_array(context
, subtable_offset
, GET_BE_WORD(format1
->mark1_array
), mark1_index
,
4239 mark2_index
, GET_BE_WORD(format1
->mark2_array
), GET_BE_WORD(format1
->mark_class_count
), mark_iter
.pos
);
4243 WARN("Unknown mark-to-mark format %u.\n", format
);
4250 static unsigned int opentype_layout_adjust_extension_subtable(struct scriptshaping_context
*context
,
4251 unsigned int *subtable_offset
, const struct lookup
*lookup
)
4253 const struct ot_gsubgpos_extension_format1
*format1
;
4255 if ((context
->table
== &context
->cache
->gsub
&& lookup
->type
!= GSUB_LOOKUP_EXTENSION_SUBST
) ||
4256 (context
->table
== &context
->cache
->gpos
&& lookup
->type
!= GPOS_LOOKUP_EXTENSION_POSITION
))
4258 return lookup
->type
;
4261 if (!(format1
= table_read_ensure(&context
->table
->table
, *subtable_offset
, sizeof(*format1
))))
4264 if (GET_BE_WORD(format1
->format
) != 1)
4266 WARN("Unexpected extension table format %#x.\n", format1
->format
);
4270 *subtable_offset
= *subtable_offset
+ GET_BE_DWORD(format1
->extension_offset
);
4272 return GET_BE_WORD(format1
->lookup_type
);
4275 static BOOL
opentype_layout_apply_context(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4276 unsigned int subtable_offset
);
4277 static BOOL
opentype_layout_apply_chain_context(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4278 unsigned int subtable_offset
);
4280 static BOOL
opentype_layout_apply_gpos_lookup(struct scriptshaping_context
*context
, const struct lookup
*lookup
)
4282 unsigned int i
, lookup_type
;
4285 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
4287 unsigned int subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, i
);
4289 lookup_type
= opentype_layout_adjust_extension_subtable(context
, &subtable_offset
, lookup
);
4291 switch (lookup_type
)
4293 case GPOS_LOOKUP_SINGLE_ADJUSTMENT
:
4294 ret
= opentype_layout_apply_gpos_single_adjustment(context
, lookup
, subtable_offset
);
4296 case GPOS_LOOKUP_PAIR_ADJUSTMENT
:
4297 ret
= opentype_layout_apply_gpos_pair_adjustment(context
, lookup
, subtable_offset
);
4299 case GPOS_LOOKUP_CURSIVE_ATTACHMENT
:
4300 ret
= opentype_layout_apply_gpos_cursive_attachment(context
, lookup
, subtable_offset
);
4302 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT
:
4303 ret
= opentype_layout_apply_gpos_mark_to_base_attachment(context
, lookup
, subtable_offset
);
4305 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT
:
4306 ret
= opentype_layout_apply_gpos_mark_to_lig_attachment(context
, lookup
, subtable_offset
);
4308 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT
:
4309 ret
= opentype_layout_apply_gpos_mark_to_mark_attachment(context
, lookup
, subtable_offset
);
4311 case GPOS_LOOKUP_CONTEXTUAL_POSITION
:
4312 ret
= opentype_layout_apply_context(context
, lookup
, subtable_offset
);
4314 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION
:
4315 ret
= opentype_layout_apply_chain_context(context
, lookup
, subtable_offset
);
4317 case GPOS_LOOKUP_EXTENSION_POSITION
:
4318 WARN("Recursive extension lookup.\n");
4321 WARN("Unknown lookup type %u.\n", lookup_type
);
4333 struct lookup
*lookups
;
4338 static int lookups_sorting_compare(const void *a
, const void *b
)
4340 const struct lookup
*left
= (const struct lookup
*)a
;
4341 const struct lookup
*right
= (const struct lookup
*)b
;
4342 return left
->index
< right
->index
? -1 : left
->index
> right
->index
? 1 : 0;
4345 static BOOL
opentype_layout_init_lookup(const struct ot_gsubgpos_table
*table
, unsigned short lookup_index
, unsigned int mask
,
4346 struct lookup
*lookup
)
4348 unsigned short subtable_count
, lookup_type
, flags
, mark_filtering_set
;
4349 const struct ot_lookup_table
*lookup_table
;
4350 unsigned int offset
;
4352 if (!(offset
= table_read_be_word(&table
->table
, table
->lookup_list
+
4353 FIELD_OFFSET(struct ot_lookup_list
, lookup
[lookup_index
]))))
4358 offset
+= table
->lookup_list
;
4360 if (!(lookup_table
= table_read_ensure(&table
->table
, offset
, sizeof(*lookup_table
))))
4363 if (!(subtable_count
= GET_BE_WORD(lookup_table
->subtable_count
)))
4366 lookup_type
= GET_BE_WORD(lookup_table
->lookup_type
);
4367 flags
= GET_BE_WORD(lookup_table
->flags
);
4369 if (flags
& LOOKUP_FLAG_USE_MARK_FILTERING_SET
)
4371 mark_filtering_set
= table_read_be_word(&table
->table
, offset
+
4372 FIELD_OFFSET(struct ot_lookup_table
, subtable
[subtable_count
]));
4373 flags
|= mark_filtering_set
<< 16;
4376 lookup
->index
= lookup_index
;
4377 lookup
->type
= lookup_type
;
4378 lookup
->flags
= flags
;
4379 lookup
->subtable_count
= subtable_count
;
4380 lookup
->mask
= mask
;
4381 lookup
->offset
= offset
;
4386 static void opentype_layout_add_lookups(const struct ot_feature_list
*feature_list
, UINT16 total_lookup_count
,
4387 const struct ot_gsubgpos_table
*table
, struct shaping_feature
*feature
, struct lookups
*lookups
)
4389 UINT16 feature_offset
, lookup_count
;
4392 /* Feature wasn't found */
4393 if (feature
->index
== 0xffff)
4396 feature_offset
= GET_BE_WORD(feature_list
->features
[feature
->index
].offset
);
4398 lookup_count
= table_read_be_word(&table
->table
, table
->feature_list
+ feature_offset
+
4399 FIELD_OFFSET(struct ot_feature
, lookup_count
));
4403 if (!dwrite_array_reserve((void **)&lookups
->lookups
, &lookups
->capacity
, lookups
->count
+ lookup_count
,
4404 sizeof(*lookups
->lookups
)))
4409 for (i
= 0; i
< lookup_count
; ++i
)
4411 UINT16 lookup_index
= table_read_be_word(&table
->table
, table
->feature_list
+ feature_offset
+
4412 FIELD_OFFSET(struct ot_feature
, lookuplist_index
[i
]));
4414 if (lookup_index
>= total_lookup_count
)
4417 if (opentype_layout_init_lookup(table
, lookup_index
, feature
->mask
, &lookups
->lookups
[lookups
->count
]))
4422 static void opentype_layout_collect_lookups(struct scriptshaping_context
*context
, unsigned int script_index
,
4423 unsigned int language_index
, const struct shaping_features
*features
, const struct ot_gsubgpos_table
*table
,
4424 struct lookups
*lookups
)
4426 unsigned int last_num_lookups
= 0, stage
, script_feature_count
= 0;
4427 UINT16 total_feature_count
, total_lookup_count
;
4428 struct shaping_feature required_feature
= { 0 };
4429 const struct ot_feature_list
*feature_list
;
4430 const struct ot_langsys
*langsys
= NULL
;
4431 struct shaping_feature
*feature
;
4432 unsigned int i
, j
, next_bit
;
4433 unsigned int global_bit_shift
= 1;
4434 unsigned int global_bit_mask
= 2;
4435 UINT16 feature_index
;
4437 if (!table
->table
.data
)
4440 if (script_index
!= ~0u)
4442 unsigned int table_offset
, langsys_offset
;
4444 /* ScriptTable offset. */
4445 table_offset
= table_read_be_word(&table
->table
, table
->script_list
+ FIELD_OFFSET(struct ot_script_list
, scripts
) +
4446 script_index
* sizeof(struct ot_script_record
) + FIELD_OFFSET(struct ot_script_record
, script
));
4450 if (language_index
== ~0u)
4451 langsys_offset
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
);
4453 langsys_offset
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
+
4454 FIELD_OFFSET(struct ot_script
, langsys
) + language_index
* sizeof(struct ot_langsys_record
) +
4455 FIELD_OFFSET(struct ot_langsys_record
, langsys
));
4456 langsys_offset
+= table
->script_list
+ table_offset
;
4458 script_feature_count
= table_read_be_word(&table
->table
, langsys_offset
+ FIELD_OFFSET(struct ot_langsys
, feature_count
));
4459 if (script_feature_count
)
4460 langsys
= table_read_ensure(&table
->table
, langsys_offset
,
4461 FIELD_OFFSET(struct ot_langsys
, feature_index
[script_feature_count
]));
4463 script_feature_count
= 0;
4466 total_feature_count
= table_read_be_word(&table
->table
, table
->feature_list
);
4467 if (!total_feature_count
)
4470 total_lookup_count
= table_read_be_word(&table
->table
, table
->lookup_list
);
4471 if (!total_lookup_count
)
4474 feature_list
= table_read_ensure(&table
->table
, table
->feature_list
,
4475 FIELD_OFFSET(struct ot_feature_list
, features
[total_feature_count
]));
4479 /* Required feature. */
4480 required_feature
.index
= langsys
? GET_BE_WORD(langsys
->required_feature_index
) : 0xffff;
4481 if (required_feature
.index
< total_feature_count
)
4482 required_feature
.tag
= feature_list
->features
[required_feature
.index
].tag
;
4483 required_feature
.mask
= global_bit_mask
;
4485 context
->global_mask
= global_bit_mask
;
4486 next_bit
= global_bit_shift
+ 1;
4487 for (i
= 0; i
< features
->count
; ++i
)
4489 unsigned int bits_needed
;
4492 feature
= &features
->features
[i
];
4494 feature
->index
= 0xffff;
4496 if ((feature
->flags
& FEATURE_GLOBAL
) && feature
->max_value
== 1)
4499 BitScanReverse(&bits_needed
, min(feature
->max_value
, 256));
4501 if (!feature
->max_value
|| next_bit
+ bits_needed
> 8 * sizeof (feature
->mask
))
4504 if (required_feature
.tag
== feature
->tag
)
4505 required_feature
.stage
= feature
->stage
;
4507 for (j
= 0; j
< script_feature_count
; ++j
)
4509 feature_index
= GET_BE_WORD(langsys
->feature_index
[j
]);
4510 if (feature_index
>= total_feature_count
)
4512 if ((found
= feature_list
->features
[feature_index
].tag
== feature
->tag
))
4514 feature
->index
= feature_index
;
4519 if (!found
&& (features
->features
[i
].flags
& FEATURE_GLOBAL_SEARCH
))
4521 for (j
= 0; j
< total_feature_count
; ++j
)
4523 if ((found
= (feature_list
->features
[j
].tag
== feature
->tag
)))
4534 if (feature
->flags
& FEATURE_GLOBAL
&& feature
->max_value
== 1)
4536 feature
->shift
= global_bit_shift
;
4537 feature
->mask
= global_bit_mask
;
4541 feature
->shift
= next_bit
;
4542 feature
->mask
= (1 << (next_bit
+ bits_needed
)) - (1 << next_bit
);
4543 next_bit
+= bits_needed
;
4544 context
->global_mask
|= (feature
->default_value
<< feature
->shift
) & feature
->mask
;
4548 for (stage
= 0; stage
<= features
->stage
; ++stage
)
4550 if (required_feature
.index
!= 0xffff && required_feature
.stage
== stage
)
4551 opentype_layout_add_lookups(feature_list
, total_lookup_count
, table
, &required_feature
, lookups
);
4553 for (i
= 0; i
< features
->count
; ++i
)
4555 if (features
->features
[i
].stage
== stage
)
4556 opentype_layout_add_lookups(feature_list
, total_lookup_count
, table
, &features
->features
[i
], lookups
);
4559 /* Sort and merge lookups for current stage. */
4560 if (last_num_lookups
< lookups
->count
)
4562 qsort(lookups
->lookups
+ last_num_lookups
, lookups
->count
- last_num_lookups
, sizeof(*lookups
->lookups
),
4563 lookups_sorting_compare
);
4565 j
= last_num_lookups
;
4566 for (i
= j
+ 1; i
< lookups
->count
; ++i
)
4568 if (lookups
->lookups
[i
].index
!= lookups
->lookups
[j
].index
)
4570 lookups
->lookups
[++j
] = lookups
->lookups
[i
];
4574 lookups
->lookups
[j
].mask
|= lookups
->lookups
[i
].mask
;
4577 lookups
->count
= j
+ 1;
4580 last_num_lookups
= lookups
->count
;
4584 static int feature_search_compare(const void *a
, const void* b
)
4586 unsigned int tag
= *(unsigned int *)a
;
4587 const struct shaping_feature
*feature
= b
;
4589 return tag
< feature
->tag
? -1 : tag
> feature
->tag
? 1 : 0;
4592 static unsigned int shaping_features_get_mask(const struct shaping_features
*features
, unsigned int tag
, unsigned int *shift
)
4594 struct shaping_feature
*feature
;
4596 feature
= bsearch(&tag
, features
->features
, features
->count
, sizeof(*features
->features
), feature_search_compare
);
4598 if (!feature
|| feature
->index
== 0xffff)
4601 if (shift
) *shift
= feature
->shift
;
4602 return feature
->mask
;
4605 static void opentype_layout_get_glyph_range_for_text(struct scriptshaping_context
*context
, unsigned int start_char
,
4606 unsigned int end_char
, unsigned int *start_glyph
, unsigned int *end_glyph
)
4608 *start_glyph
= context
->u
.buffer
.clustermap
[start_char
];
4609 if (end_char
>= context
->length
- 1)
4610 *end_glyph
= context
->glyph_count
- 1;
4612 *end_glyph
= context
->u
.buffer
.clustermap
[end_char
+ 1] - 1;
4615 static void opentype_layout_set_glyph_masks(struct scriptshaping_context
*context
, const struct shaping_features
*features
)
4617 const DWRITE_TYPOGRAPHIC_FEATURES
**user_features
= context
->user_features
.features
;
4618 unsigned int f
, r
, g
, start_char
, mask
, shift
, value
;
4620 for (g
= 0; g
< context
->glyph_count
; ++g
)
4621 context
->glyph_infos
[g
].mask
= context
->global_mask
;
4623 /* FIXME: set shaper masks */
4625 for (r
= 0, start_char
= 0; r
< context
->user_features
.range_count
; ++r
)
4627 unsigned int start_glyph
, end_glyph
;
4629 if (start_char
>= context
->length
)
4632 opentype_layout_get_glyph_range_for_text(context
, start_char
, start_char
+ context
->user_features
.range_lengths
[r
],
4633 &start_glyph
, &end_glyph
);
4634 start_char
+= context
->user_features
.range_lengths
[r
];
4636 if (start_glyph
> end_glyph
|| end_glyph
>= context
->glyph_count
)
4639 for (f
= 0; f
< user_features
[r
]->featureCount
; ++f
)
4641 mask
= shaping_features_get_mask(features
, user_features
[r
]->features
[f
].nameTag
, &shift
);
4645 value
= (user_features
[r
]->features
[f
].parameter
<< shift
) & mask
;
4647 for (g
= start_glyph
; g
<= end_glyph
; ++g
)
4648 context
->glyph_infos
[g
].mask
= (context
->glyph_infos
[g
].mask
& ~mask
) | value
;
4653 static void opentype_layout_apply_gpos_context_lookup(struct scriptshaping_context
*context
, unsigned int lookup_index
)
4655 struct lookup lookup
= { 0 };
4656 /* Feature mask is intentionally zero, it's not used outside of main loop. */
4657 if (opentype_layout_init_lookup(context
->table
, lookup_index
, 0, &lookup
))
4658 opentype_layout_apply_gpos_lookup(context
, &lookup
);
4661 void opentype_layout_apply_gpos_features(struct scriptshaping_context
*context
, unsigned int script_index
,
4662 unsigned int language_index
, const struct shaping_features
*features
)
4664 struct lookups lookups
= { 0 };
4668 context
->nesting_level_left
= SHAPE_MAX_NESTING_LEVEL
;
4669 context
->u
.buffer
.apply_context_lookup
= opentype_layout_apply_gpos_context_lookup
;
4670 opentype_layout_collect_lookups(context
, script_index
, language_index
, features
, &context
->cache
->gpos
, &lookups
);
4672 for (i
= 0; i
< context
->glyph_count
; ++i
)
4673 opentype_set_glyph_props(context
, i
);
4674 opentype_layout_set_glyph_masks(context
, features
);
4676 for (i
= 0; i
< lookups
.count
; ++i
)
4678 const struct lookup
*lookup
= &lookups
.lookups
[i
];
4681 context
->lookup_mask
= lookup
->mask
;
4683 while (context
->cur
< context
->glyph_count
)
4687 if ((context
->glyph_infos
[context
->cur
].mask
& lookup
->mask
) &&
4688 lookup_is_glyph_match(context
, context
->cur
, lookup
->flags
))
4690 ret
= opentype_layout_apply_gpos_lookup(context
, lookup
);
4698 heap_free(lookups
.lookups
);
4701 static void opentype_layout_replace_glyph(struct scriptshaping_context
*context
, UINT16 glyph
)
4703 UINT16 orig_glyph
= context
->u
.subst
.glyphs
[context
->cur
];
4704 if (glyph
!= orig_glyph
)
4706 context
->u
.subst
.glyphs
[context
->cur
] = glyph
;
4707 opentype_set_subst_glyph_props(context
, context
->cur
);
4711 static BOOL
opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4712 unsigned int subtable_offset
)
4714 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4715 UINT16 format
, coverage
, orig_glyph
, glyph
;
4716 unsigned int coverage_index
;
4718 orig_glyph
= glyph
= context
->u
.subst
.glyphs
[context
->cur
];
4720 format
= table_read_be_word(table
, subtable_offset
);
4722 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_singlesubst_format1
, coverage
));
4726 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
4727 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
4729 glyph
= orig_glyph
+ table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_singlesubst_format1
, delta
));
4731 else if (format
== 2)
4733 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
4734 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
4736 if (!table_read_array_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_singlesubst_format2
, count
),
4737 coverage_index
, &glyph
))
4744 WARN("Unknown single substitution format %u.\n", format
);
4748 opentype_layout_replace_glyph(context
, glyph
);
4754 static BOOL
opentype_layout_gsub_ensure_buffer(struct scriptshaping_context
*context
, unsigned int count
)
4756 DWRITE_SHAPING_GLYPH_PROPERTIES
*glyph_props
;
4757 struct shaping_glyph_info
*glyph_infos
;
4758 unsigned int new_capacity
;
4762 if (context
->u
.subst
.capacity
>= count
)
4765 new_capacity
= context
->u
.subst
.capacity
* 2;
4767 if ((glyphs
= heap_realloc(context
->u
.subst
.glyphs
, new_capacity
* sizeof(*glyphs
))))
4768 context
->u
.subst
.glyphs
= glyphs
;
4769 if ((glyph_props
= heap_realloc(context
->u
.subst
.glyph_props
, new_capacity
* sizeof(*glyph_props
))))
4770 context
->u
.subst
.glyph_props
= glyph_props
;
4771 if ((glyph_infos
= heap_realloc(context
->glyph_infos
, new_capacity
* sizeof(*glyph_infos
))))
4772 context
->glyph_infos
= glyph_infos
;
4774 if ((ret
= (glyphs
&& glyph_props
&& glyph_infos
)))
4775 context
->u
.subst
.capacity
= new_capacity
;
4780 static BOOL
opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4781 unsigned int subtable_offset
)
4783 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4784 UINT16 format
, coverage
, glyph
, glyph_count
;
4785 unsigned int i
, idx
, coverage_index
;
4786 const UINT16
*glyphs
;
4789 glyph
= context
->u
.subst
.glyphs
[idx
];
4791 format
= table_read_be_word(table
, subtable_offset
);
4793 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_multsubst_format1
, coverage
));
4799 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
4800 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
4802 if (!table_read_array_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_multsubst_format1
, seq_count
),
4803 coverage_index
, &seq_offset
))
4808 if (!(glyphs
= table_read_array_be_word(table
, subtable_offset
+ seq_offset
, ~0u, &glyph_count
))) return FALSE
;
4810 if (glyph_count
== 1)
4812 /* Equivalent of single substitution. */
4813 opentype_layout_replace_glyph(context
, GET_BE_WORD(glyphs
[0]));
4816 else if (glyph_count
== 0)
4822 unsigned int shift_len
, src_idx
, dest_idx
, mask
;
4824 /* Current glyph is also replaced. */
4827 if (!(opentype_layout_gsub_ensure_buffer(context
, context
->glyph_count
+ glyph_count
)))
4830 shift_len
= context
->cur
+ 1 < context
->glyph_count
? context
->glyph_count
- context
->cur
- 1 : 0;
4834 src_idx
= context
->cur
+ 1;
4835 dest_idx
= src_idx
+ glyph_count
;
4837 memmove(&context
->u
.subst
.glyphs
[dest_idx
], &context
->u
.subst
.glyphs
[src_idx
],
4838 shift_len
* sizeof(*context
->u
.subst
.glyphs
));
4839 memmove(&context
->u
.subst
.glyph_props
[dest_idx
], &context
->u
.subst
.glyph_props
[src_idx
],
4840 shift_len
* sizeof(*context
->u
.subst
.glyph_props
));
4841 memmove(&context
->glyph_infos
[dest_idx
], &context
->glyph_infos
[src_idx
],
4842 shift_len
* sizeof(*context
->glyph_infos
));
4845 mask
= context
->glyph_infos
[context
->cur
].mask
;
4846 for (i
= 0, idx
= context
->cur
; i
<= glyph_count
; ++i
)
4848 glyph
= GET_BE_WORD(glyphs
[i
]);
4849 context
->u
.subst
.glyphs
[idx
+ i
] = glyph
;
4852 context
->u
.subst
.glyph_props
[idx
+ i
].isClusterStart
= 0;
4853 context
->u
.buffer
.glyph_props
[idx
+ i
].components
= 0;
4854 context
->glyph_infos
[idx
+ i
].start_text_idx
= 0;
4856 opentype_set_subst_glyph_props(context
, idx
+ i
);
4857 /* Inherit feature mask from original matched glyph. */
4858 context
->glyph_infos
[idx
+ i
].mask
= mask
;
4861 context
->cur
+= glyph_count
+ 1;
4862 context
->glyph_count
+= glyph_count
;
4867 WARN("Unknown multiple substitution format %u.\n", format
);
4874 static BOOL
opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4875 unsigned int subtable_offset
)
4877 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4878 unsigned int idx
, coverage_index
;
4879 UINT16 format
, coverage
, glyph
;
4882 glyph
= context
->u
.subst
.glyphs
[idx
];
4884 format
= table_read_be_word(table
, subtable_offset
);
4888 const struct ot_gsub_altsubst_format1
*format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
));
4889 unsigned int shift
, alt_index
;
4892 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_altsubst_format1
, coverage
));
4894 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
4895 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
4897 if (!table_read_array_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_altsubst_format1
, count
),
4898 coverage_index
, &set_offset
))
4901 /* Argument is 1-based. */
4902 BitScanForward(&shift
, context
->lookup_mask
);
4903 alt_index
= (context
->lookup_mask
& context
->glyph_infos
[idx
].mask
) >> shift
;
4904 if (!alt_index
) return FALSE
;
4906 if (!table_read_array_be_word(table
, subtable_offset
+ set_offset
, alt_index
- 1, &glyph
)) return FALSE
;
4910 WARN("Unexpected alternate substitution format %d.\n", format
);
4914 opentype_layout_replace_glyph(context
, glyph
);
4920 static BOOL
opentype_layout_context_match_input(const struct match_context
*mc
, unsigned int count
, const UINT16
*input
,
4921 unsigned int *end_offset
, unsigned int *match_positions
)
4923 struct match_data match_data
= { .mc
= mc
, .subtable_offset
= mc
->input_offset
};
4924 struct scriptshaping_context
*context
= mc
->context
;
4925 struct glyph_iterator iter
;
4928 if (count
> GLYPH_CONTEXT_MAX_LENGTH
)
4931 match_positions
[0] = context
->cur
;
4933 glyph_iterator_init(context
, mc
->lookup
->flags
, context
->cur
, count
- 1, &iter
);
4934 iter
.mask
= context
->lookup_mask
;
4935 iter
.match_func
= mc
->match_func
;
4936 iter
.match_data
= &match_data
;
4937 iter
.glyph_data
= input
;
4939 for (i
= 1; i
< count
; ++i
)
4941 if (!glyph_iterator_next(&iter
))
4944 match_positions
[i
] = iter
.pos
;
4947 *end_offset
= iter
.pos
- context
->cur
+ 1;
4952 static void opentype_layout_unsafe_to_break(struct scriptshaping_context
*context
, unsigned int idx
)
4954 if (context
->u
.buffer
.glyph_props
[idx
].isClusterStart
)
4955 context
->u
.buffer
.text_props
[context
->glyph_infos
[idx
].start_text_idx
].canBreakShapingAfter
= 0;
4958 static void opentype_layout_delete_glyph(struct scriptshaping_context
*context
, unsigned int idx
)
4960 unsigned int shift_len
;
4962 shift_len
= context
->glyph_count
- context
->cur
- 1;
4966 memmove(&context
->u
.buffer
.glyphs
[idx
], &context
->u
.buffer
.glyphs
[idx
+ 1],
4967 shift_len
* sizeof(*context
->u
.buffer
.glyphs
));
4968 memmove(&context
->u
.buffer
.glyph_props
[idx
], &context
->u
.buffer
.glyph_props
[idx
+ 1],
4969 shift_len
* sizeof(*context
->u
.buffer
.glyph_props
));
4970 memmove(&context
->glyph_infos
[idx
], &context
->glyph_infos
[idx
+ 1], shift_len
* sizeof(*context
->glyph_infos
));
4973 context
->glyph_count
--;
4976 static BOOL
opentype_layout_apply_ligature(struct scriptshaping_context
*context
, unsigned int offset
,
4977 const struct lookup
*lookup
)
4979 struct match_context mc
= { .context
= context
, .lookup
= lookup
, .match_func
= opentype_match_glyph_func
};
4980 const struct dwrite_fonttable
*gsub
= &context
->table
->table
;
4981 unsigned int match_positions
[GLYPH_CONTEXT_MAX_LENGTH
];
4982 unsigned int i
, j
, comp_count
, match_length
= 0;
4983 const struct ot_gsub_lig
*lig
;
4986 comp_count
= table_read_be_word(gsub
, offset
+ FIELD_OFFSET(struct ot_gsub_lig
, comp_count
));
4991 lig
= table_read_ensure(gsub
, offset
, FIELD_OFFSET(struct ot_gsub_lig
, components
[comp_count
-1]));
4995 lig_glyph
= GET_BE_WORD(lig
->lig_glyph
);
4997 if (comp_count
== 1)
4999 opentype_layout_replace_glyph(context
, lig_glyph
);
5004 if (!opentype_layout_context_match_input(&mc
, comp_count
, lig
->components
, &match_length
, match_positions
))
5007 opentype_layout_replace_glyph(context
, lig_glyph
);
5008 context
->u
.buffer
.glyph_props
[context
->cur
].components
= comp_count
;
5010 /* Positioning against a ligature implies keeping track of ligature component
5011 glyph should be attached to. Update per-glyph property for interleaving glyphs,
5012 0 means attaching to last component, n - attaching to n-th glyph before last. */
5013 for (i
= 1; i
< comp_count
; ++i
)
5015 j
= match_positions
[i
- 1] + 1;
5016 while (j
< match_positions
[i
])
5018 context
->u
.buffer
.glyph_props
[j
++].lig_component
= comp_count
- i
;
5020 opentype_layout_unsafe_to_break(context
, i
);
5021 context
->u
.buffer
.glyph_props
[i
].isClusterStart
= 0;
5022 context
->glyph_infos
[i
].start_text_idx
= 0;
5025 /* Delete ligated glyphs, backwards to preserve index. */
5026 for (i
= 1; i
< comp_count
; ++i
)
5028 opentype_layout_delete_glyph(context
, match_positions
[comp_count
- i
]);
5031 /* Skip whole matched sequence, accounting for deleted glyphs. */
5032 context
->cur
+= match_length
- (comp_count
- 1);
5037 static BOOL
opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
5038 unsigned int subtable_offset
)
5040 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5041 UINT16 format
, coverage
, glyph
, lig_set_offset
;
5042 unsigned int coverage_index
;
5044 glyph
= context
->u
.subst
.glyphs
[context
->cur
];
5046 format
= table_read_be_word(table
, subtable_offset
);
5050 const struct ot_gsub_ligsubst_format1
*format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
));
5052 const UINT16
*offsets
;
5055 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_ligsubst_format1
, coverage
));
5057 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5058 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
5060 if (!table_read_array_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_ligsubst_format1
, lig_set_count
),
5061 coverage_index
, &lig_set_offset
))
5064 if (!(offsets
= table_read_array_be_word(table
, subtable_offset
+ lig_set_offset
, ~0u, &lig_count
)))
5067 /* First applicable ligature is used. */
5068 for (i
= 0; i
< lig_count
; ++i
)
5070 if (opentype_layout_apply_ligature(context
, subtable_offset
+ lig_set_offset
+ GET_BE_WORD(offsets
[i
]), lookup
))
5075 WARN("Unexpected ligature substitution format %d.\n", format
);
5080 static BOOL
opentype_layout_context_match_backtrack(const struct match_context
*mc
, unsigned int count
,
5081 const UINT16
*backtrack
, unsigned int *match_start
)
5083 struct match_data match_data
= { .mc
= mc
, .subtable_offset
= mc
->backtrack_offset
};
5084 struct scriptshaping_context
*context
= mc
->context
;
5085 struct glyph_iterator iter
;
5088 glyph_iterator_init(context
, mc
->lookup
->flags
, context
->cur
, count
, &iter
);
5089 iter
.match_func
= mc
->match_func
;
5090 iter
.match_data
= &match_data
;
5091 iter
.glyph_data
= backtrack
;
5093 for (i
= 0; i
< count
; ++i
)
5095 if (!glyph_iterator_prev(&iter
))
5099 *match_start
= iter
.pos
;
5104 static BOOL
opentype_layout_context_match_lookahead(const struct match_context
*mc
, unsigned int count
,
5105 const UINT16
*lookahead
, unsigned int offset
, unsigned int *end_index
)
5107 struct match_data match_data
= { .mc
= mc
, .subtable_offset
= mc
->lookahead_offset
};
5108 struct scriptshaping_context
*context
= mc
->context
;
5109 struct glyph_iterator iter
;
5112 glyph_iterator_init(context
, mc
->lookup
->flags
, context
->cur
+ offset
- 1, count
, &iter
);
5113 iter
.match_func
= mc
->match_func
;
5114 iter
.match_data
= &match_data
;
5115 iter
.glyph_data
= lookahead
;
5117 for (i
= 0; i
< count
; ++i
)
5119 if (!glyph_iterator_next(&iter
))
5123 *end_index
= iter
.pos
;
5128 static BOOL
opentype_layout_context_apply_lookup(struct scriptshaping_context
*context
, unsigned int count
,
5129 unsigned int *match_positions
, unsigned int lookup_count
, const UINT16
*lookup_records
, unsigned int match_length
)
5134 if (!context
->nesting_level_left
)
5137 end
= context
->cur
+ match_length
;
5139 for (i
= 0; i
< lookup_count
; ++i
)
5141 unsigned int idx
= GET_BE_WORD(lookup_records
[i
]);
5142 unsigned int orig_len
, lookup_index
, next
;
5147 context
->cur
= match_positions
[idx
];
5149 orig_len
= context
->glyph_count
;
5151 lookup_index
= GET_BE_WORD(lookup_records
[i
+1]);
5153 --context
->nesting_level_left
;
5154 context
->u
.buffer
.apply_context_lookup(context
, lookup_index
);
5155 ++context
->nesting_level_left
;
5157 delta
= context
->glyph_count
- orig_len
;
5162 if (end
<= (int)match_positions
[idx
])
5164 end
= match_positions
[idx
];
5172 if (delta
+ count
> GLYPH_CONTEXT_MAX_LENGTH
)
5177 delta
= max(delta
, (int)next
- (int)count
);
5181 memmove(match_positions
+ next
+ delta
, match_positions
+ next
,
5182 (count
- next
) * sizeof (*match_positions
));
5186 for (j
= idx
+ 1; j
< next
; j
++)
5187 match_positions
[j
] = match_positions
[j
- 1] + 1;
5189 for (; next
< count
; next
++)
5190 match_positions
[next
] += delta
;
5198 static BOOL
opentype_layout_apply_chain_context_match(unsigned int backtrack_count
, const UINT16
*backtrack
,
5199 unsigned int input_count
, const UINT16
*input
, unsigned int lookahead_count
, const UINT16
*lookahead
,
5200 unsigned int lookup_count
, const UINT16
*lookup_records
, const struct match_context
*mc
)
5202 unsigned int start_index
= 0, match_length
= 0, end_index
= 0;
5203 unsigned int match_positions
[GLYPH_CONTEXT_MAX_LENGTH
];
5205 return opentype_layout_context_match_input(mc
, input_count
, input
, &match_length
, match_positions
) &&
5206 opentype_layout_context_match_backtrack(mc
, backtrack_count
, backtrack
, &start_index
) &&
5207 opentype_layout_context_match_lookahead(mc
, lookahead_count
, lookahead
, input_count
, &end_index
) &&
5208 opentype_layout_context_apply_lookup(mc
->context
, input_count
, match_positions
, lookup_count
, lookup_records
, match_length
);
5211 static BOOL
opentype_layout_apply_chain_rule_set(const struct match_context
*mc
, unsigned int offset
)
5213 unsigned int backtrack_count
, input_count
, lookahead_count
, lookup_count
;
5214 const struct dwrite_fonttable
*table
= &mc
->context
->table
->table
;
5215 const UINT16
*backtrack
, *lookahead
, *input
, *lookup_records
;
5216 const struct ot_gsubgpos_ruleset
*ruleset
;
5217 unsigned int i
, count
;
5219 count
= table_read_be_word(table
, offset
);
5220 ruleset
= table_read_ensure(table
, offset
, count
* sizeof(ruleset
->offsets
));
5222 for (i
= 0; i
< count
; ++i
)
5224 unsigned int rule_offset
= offset
+ GET_BE_WORD(ruleset
->offsets
[i
]);
5226 backtrack_count
= table_read_be_word(table
, rule_offset
);
5228 backtrack
= table_read_ensure(table
, rule_offset
, backtrack_count
* sizeof(*backtrack
));
5229 rule_offset
+= backtrack_count
* sizeof(*backtrack
);
5231 if (!(input_count
= table_read_be_word(table
, rule_offset
)))
5235 input
= table_read_ensure(table
, rule_offset
, (input_count
- 1) * sizeof(*input
));
5236 rule_offset
+= (input_count
- 1) * sizeof(*input
);
5238 lookahead_count
= table_read_be_word(table
, rule_offset
);
5240 lookahead
= table_read_ensure(table
, rule_offset
, lookahead_count
* sizeof(*lookahead
));
5241 rule_offset
+= lookahead_count
* sizeof(*lookahead
);
5243 lookup_count
= table_read_be_word(table
, rule_offset
);
5245 lookup_records
= table_read_ensure(table
, rule_offset
, lookup_count
* 2 * sizeof(*lookup_records
));
5247 /* First applicable rule is used. */
5248 if (opentype_layout_apply_chain_context_match(backtrack_count
, backtrack
, input_count
, input
, lookahead_count
,
5249 lookahead
, lookup_count
, lookup_records
, mc
))
5258 static BOOL
opentype_layout_apply_context_match(unsigned int input_count
, const UINT16
*input
, unsigned int lookup_count
,
5259 const UINT16
*lookup_records
, const struct match_context
*mc
)
5261 unsigned int match_positions
[GLYPH_CONTEXT_MAX_LENGTH
];
5262 unsigned int match_length
= 0;
5264 return opentype_layout_context_match_input(mc
, input_count
, input
, &match_length
, match_positions
) &&
5265 opentype_layout_context_apply_lookup(mc
->context
, input_count
, match_positions
, lookup_count
,
5266 lookup_records
, match_length
);
5269 static BOOL
opentype_layout_apply_rule_set(const struct match_context
*mc
, unsigned int offset
)
5271 unsigned int input_count
, lookup_count
;
5272 const struct dwrite_fonttable
*table
= &mc
->context
->table
->table
;
5273 const UINT16
*input
, *lookup_records
;
5274 const struct ot_gsubgpos_ruleset
*ruleset
;
5275 unsigned int i
, count
;
5277 count
= table_read_be_word(table
, offset
);
5278 ruleset
= table_read_ensure(table
, offset
, count
* sizeof(ruleset
->offsets
));
5280 for (i
= 0; i
< count
; ++i
)
5282 unsigned int rule_offset
= offset
+ GET_BE_WORD(ruleset
->offsets
[i
]);
5284 if (!(input_count
= table_read_be_word(table
, rule_offset
)))
5288 if (!(lookup_count
= table_read_be_word(table
, rule_offset
)))
5292 if (!(input
= table_read_ensure(table
, rule_offset
, (input_count
- 1) * sizeof(*input
))))
5294 rule_offset
+= (input_count
- 1) * sizeof(*input
);
5296 if (!(lookup_records
= table_read_ensure(table
, rule_offset
, lookup_count
* 2 * sizeof(*lookup_records
))))
5299 /* First applicable rule is used. */
5300 if (opentype_layout_apply_context_match(input_count
, input
, lookup_count
, lookup_records
, mc
))
5307 static BOOL
opentype_layout_apply_context(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
5308 unsigned int subtable_offset
)
5310 struct match_context mc
= { .context
= context
, .lookup
= lookup
};
5311 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5312 unsigned int coverage_index
= GLYPH_NOT_COVERED
, count
, offset
;
5313 UINT16 glyph
, format
, coverage
;
5316 glyph
= context
->u
.subst
.glyphs
[context
->cur
];
5318 format
= table_read_be_word(table
, subtable_offset
);
5322 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
, coverage
));
5324 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5325 if (coverage_index
== GLYPH_NOT_COVERED
)
5328 count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
, ruleset_count
));
5329 if (coverage_index
>= count
)
5332 offset
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
,
5333 rulesets
[coverage_index
]));
5334 offset
+= subtable_offset
;
5336 mc
.match_func
= opentype_match_glyph_func
;
5338 ret
= opentype_layout_apply_rule_set(&mc
, offset
);
5340 else if (format
== 2)
5342 unsigned int input_classdef
, rule_set_idx
;
5344 offset
= subtable_offset
+ 2 /* format */;
5346 coverage
= table_read_be_word(table
, offset
);
5349 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5350 if (coverage_index
== GLYPH_NOT_COVERED
)
5353 input_classdef
= table_read_be_word(table
, offset
) + subtable_offset
;
5356 count
= table_read_be_word(table
, offset
);
5359 rule_set_idx
= opentype_layout_get_glyph_class(table
, input_classdef
, glyph
);
5360 if (rule_set_idx
>= count
)
5363 offset
= table_read_be_word(table
, offset
+ rule_set_idx
* 2);
5364 offset
+= subtable_offset
;
5366 mc
.input_offset
= input_classdef
;
5367 mc
.match_func
= opentype_match_class_func
;
5369 ret
= opentype_layout_apply_rule_set(&mc
, offset
);
5371 else if (format
== 3)
5373 unsigned int input_count
, lookup_count
;
5374 const UINT16
*input
, *lookup_records
;
5376 offset
= subtable_offset
+ 2 /* format */;
5378 input_count
= table_read_be_word(table
, offset
);
5384 lookup_count
= table_read_be_word(table
, offset
);
5387 if (!(input
= table_read_ensure(table
, offset
, sizeof(*input
) * input_count
)))
5389 offset
+= sizeof(*input
) * input_count
;
5391 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(input
[0]), glyph
);
5392 if (coverage_index
== GLYPH_NOT_COVERED
)
5395 lookup_records
= table_read_ensure(table
, offset
, lookup_count
* 2 * sizeof(*lookup_records
));
5397 mc
.input_offset
= subtable_offset
;
5398 mc
.match_func
= opentype_match_coverage_func
;
5400 ret
= opentype_layout_apply_context_match(input_count
, input
+ 1, lookup_count
, lookup_records
, &mc
);
5403 WARN("Unknown contextual substitution format %u.\n", format
);
5408 static BOOL
opentype_layout_apply_chain_context(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
5409 unsigned int subtable_offset
)
5411 struct match_context mc
= { .context
= context
, .lookup
= lookup
};
5412 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5413 unsigned int coverage_index
= GLYPH_NOT_COVERED
, count
, offset
;
5414 UINT16 glyph
, format
, coverage
;
5417 glyph
= context
->u
.subst
.glyphs
[context
->cur
];
5419 format
= table_read_be_word(table
, subtable_offset
);
5423 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
, coverage
));
5425 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5426 if (coverage_index
== GLYPH_NOT_COVERED
)
5429 count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
, ruleset_count
));
5430 if (coverage_index
>= count
)
5433 offset
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
,
5434 rulesets
[coverage_index
]));
5435 offset
+= subtable_offset
;
5437 mc
.match_func
= opentype_match_glyph_func
;
5439 ret
= opentype_layout_apply_chain_rule_set(&mc
, offset
);
5441 else if (format
== 2)
5443 unsigned int backtrack_classdef
, input_classdef
, lookahead_classdef
, rule_set_idx
;
5445 offset
= subtable_offset
+ 2 /* format */;
5447 coverage
= table_read_be_word(table
, offset
);
5450 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5451 if (coverage_index
== GLYPH_NOT_COVERED
)
5454 backtrack_classdef
= table_read_be_word(table
, offset
) + subtable_offset
;
5457 input_classdef
= table_read_be_word(table
, offset
) + subtable_offset
;
5460 lookahead_classdef
= table_read_be_word(table
, offset
) + subtable_offset
;
5463 count
= table_read_be_word(table
, offset
);
5466 rule_set_idx
= opentype_layout_get_glyph_class(table
, input_classdef
, glyph
);
5467 if (rule_set_idx
>= count
)
5470 offset
= table_read_be_word(table
, offset
+ rule_set_idx
* 2);
5471 offset
+= subtable_offset
;
5473 mc
.backtrack_offset
= backtrack_classdef
;
5474 mc
.input_offset
= input_classdef
;
5475 mc
.lookahead_offset
= lookahead_classdef
;
5476 mc
.match_func
= opentype_match_class_func
;
5478 ret
= opentype_layout_apply_chain_rule_set(&mc
, offset
);
5480 else if (format
== 3)
5482 unsigned int backtrack_count
, input_count
, lookahead_count
, lookup_count
;
5483 const UINT16
*backtrack
, *lookahead
, *input
, *lookup_records
;
5485 offset
= subtable_offset
+ 2 /* format */;
5487 backtrack_count
= table_read_be_word(table
, offset
);
5489 backtrack
= table_read_ensure(table
, offset
, backtrack_count
* sizeof(*backtrack
));
5490 offset
+= backtrack_count
* sizeof(*backtrack
);
5492 input_count
= table_read_be_word(table
, offset
);
5494 input
= table_read_ensure(table
, offset
, input_count
* sizeof(*input
));
5495 offset
+= input_count
* sizeof(*input
);
5497 lookahead_count
= table_read_be_word(table
, offset
);
5499 lookahead
= table_read_ensure(table
, offset
, lookahead_count
* sizeof(*lookahead
));
5500 offset
+= lookahead_count
* sizeof(*lookahead
);
5502 lookup_count
= table_read_be_word(table
, offset
);
5504 lookup_records
= table_read_ensure(table
, offset
, lookup_count
* 2 * sizeof(*lookup_records
));
5507 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(input
[0]), glyph
);
5509 if (coverage_index
== GLYPH_NOT_COVERED
)
5512 mc
.backtrack_offset
= subtable_offset
;
5513 mc
.input_offset
= subtable_offset
;
5514 mc
.lookahead_offset
= subtable_offset
;
5515 mc
.match_func
= opentype_match_coverage_func
;
5517 ret
= opentype_layout_apply_chain_context_match(backtrack_count
, backtrack
, input_count
, input
+ 1, lookahead_count
,
5518 lookahead
, lookup_count
, lookup_records
, &mc
);
5521 WARN("Unknown chaining contextual substitution format %u.\n", format
);
5526 static BOOL
opentype_layout_apply_gsub_reverse_chain_context_substitution(struct scriptshaping_context
*context
,
5527 const struct lookup
*lookup
, unsigned int subtable_offset
)
5529 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5530 unsigned int offset
= subtable_offset
;
5531 UINT16 glyph
, format
;
5533 if (context
->nesting_level_left
!= SHAPE_MAX_NESTING_LEVEL
)
5536 glyph
= context
->u
.subst
.glyphs
[context
->cur
];
5538 format
= table_read_be_word(table
, offset
);
5543 struct match_context mc
= { .context
= context
, .lookup
= lookup
};
5544 unsigned int start_index
= 0, end_index
= 0, backtrack_count
, lookahead_count
;
5545 unsigned int coverage
, coverage_index
;
5546 const UINT16
*backtrack
, *lookahead
;
5548 coverage
= table_read_be_word(table
, offset
);
5551 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5552 if (coverage_index
== GLYPH_NOT_COVERED
)
5555 backtrack_count
= table_read_be_word(table
, offset
);
5558 backtrack
= table_read_ensure(table
, offset
, sizeof(*backtrack
) * backtrack_count
);
5559 offset
+= sizeof(*backtrack
) * backtrack_count
;
5561 lookahead_count
= table_read_be_word(table
, offset
);
5564 lookahead
= table_read_ensure(table
, offset
, sizeof(*lookahead
) * lookahead_count
);
5565 offset
+= sizeof(*lookahead
) * lookahead_count
;
5567 mc
.match_func
= opentype_match_coverage_func
;
5568 mc
.backtrack_offset
= subtable_offset
;
5569 mc
.lookahead_offset
= subtable_offset
;
5571 if (opentype_layout_context_match_backtrack(&mc
, backtrack_count
, backtrack
, &start_index
) &&
5572 opentype_layout_context_match_lookahead(&mc
, lookahead_count
, lookahead
, 1, &end_index
))
5574 unsigned int glyph_count
= table_read_be_word(table
, offset
);
5575 if (coverage_index
>= glyph_count
)
5579 glyph
= table_read_be_word(table
, offset
+ coverage_index
* sizeof(glyph
));
5580 opentype_layout_replace_glyph(context
, glyph
);
5586 WARN("Unknown reverse chaining contextual substitution format %u.\n", format
);
5591 static BOOL
opentype_layout_apply_gsub_lookup(struct scriptshaping_context
*context
, const struct lookup
*lookup
)
5593 unsigned int i
, lookup_type
;
5596 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
5598 unsigned int subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, i
);
5600 lookup_type
= opentype_layout_adjust_extension_subtable(context
, &subtable_offset
, lookup
);
5602 switch (lookup_type
)
5604 case GSUB_LOOKUP_SINGLE_SUBST
:
5605 ret
= opentype_layout_apply_gsub_single_substitution(context
, lookup
, subtable_offset
);
5607 case GSUB_LOOKUP_MULTIPLE_SUBST
:
5608 ret
= opentype_layout_apply_gsub_mult_substitution(context
, lookup
, subtable_offset
);
5610 case GSUB_LOOKUP_ALTERNATE_SUBST
:
5611 ret
= opentype_layout_apply_gsub_alt_substitution(context
, lookup
, subtable_offset
);
5613 case GSUB_LOOKUP_LIGATURE_SUBST
:
5614 ret
= opentype_layout_apply_gsub_lig_substitution(context
, lookup
, subtable_offset
);
5616 case GSUB_LOOKUP_CONTEXTUAL_SUBST
:
5617 ret
= opentype_layout_apply_context(context
, lookup
, subtable_offset
);
5619 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST
:
5620 ret
= opentype_layout_apply_chain_context(context
, lookup
, subtable_offset
);
5622 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST
:
5623 ret
= opentype_layout_apply_gsub_reverse_chain_context_substitution(context
, lookup
, subtable_offset
);
5625 case GSUB_LOOKUP_EXTENSION_SUBST
:
5626 WARN("Invalid lookup type for extension substitution %#x.\n", lookup_type
);
5629 WARN("Unknown lookup type %u.\n", lookup_type
);
5639 static unsigned int unicode_get_mirrored_char(unsigned int codepoint
)
5641 extern const WCHAR wine_mirror_map
[] DECLSPEC_HIDDEN
;
5643 /* TODO: check if mirroring for higher planes makes sense at all */
5644 if (codepoint
> 0xffff) return codepoint
;
5645 mirror
= get_table_entry(wine_mirror_map
, codepoint
);
5646 return mirror
? mirror
: codepoint
;
5650 * 034F # Mn COMBINING GRAPHEME JOINER
5651 * 061C # Cf ARABIC LETTER MARK
5652 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5653 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5654 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5655 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5657 static unsigned int opentype_is_default_ignorable(unsigned int codepoint
)
5659 return codepoint
== 0x34f || codepoint
== 0x61c || codepoint
== 0xfeff ||
5660 (codepoint
>= 0x180b && codepoint
<= 0x180e) || (codepoint
>= 0x200b && codepoint
<= 0x200f);
5663 static unsigned int opentype_is_diacritic(unsigned int codepoint
)
5665 WCHAR ch
= codepoint
;
5667 /* Ignore higher planes for now. */
5668 if (codepoint
> 0xffff) return 0;
5669 GetStringTypeW(CT_CTYPE3
, &ch
, 1, &type
);
5670 return !!(type
& C3_DIACRITIC
);
5673 static void opentype_get_nominal_glyphs(struct scriptshaping_context
*context
, const struct shaping_features
*features
)
5675 unsigned int rtlm_mask
= shaping_features_get_mask(features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), NULL
);
5676 const struct shaping_font_ops
*font
= context
->cache
->font
;
5677 unsigned int i
, g
, c
, codepoint
, cluster_start_idx
= 0;
5678 UINT16
*clustermap
= context
->u
.subst
.clustermap
;
5679 const WCHAR
*text
= context
->text
;
5682 memset(context
->u
.subst
.glyph_props
, 0, context
->u
.subst
.max_glyph_count
* sizeof(*context
->u
.subst
.glyph_props
));
5683 memset(context
->u
.buffer
.text_props
, 0, context
->length
* sizeof(*context
->u
.buffer
.text_props
));
5685 for (i
= 0; i
< context
->length
; ++i
)
5687 g
= context
->glyph_count
;
5689 if ((bmp
= !(IS_HIGH_SURROGATE(text
[i
]) && (i
< context
->length
- 1) && IS_LOW_SURROGATE(text
[i
+ 1]))))
5691 codepoint
= text
[i
];
5695 codepoint
= 0x10000 + ((text
[i
] - 0xd800) << 10) + (text
[i
+ 1] - 0xdc00);
5698 if (context
->is_rtl
)
5700 c
= unicode_get_mirrored_char(codepoint
);
5701 if (c
!= codepoint
&& font
->has_glyph(context
->cache
->context
, c
))
5704 context
->glyph_infos
[i
].mask
|= rtlm_mask
;
5707 /* TODO: should this check for glyph availability? */
5708 if (*context
->u
.subst
.digits
&& codepoint
>= '0' && codepoint
<= '9')
5709 codepoint
= context
->u
.subst
.digits
[codepoint
- '0'];
5711 context
->u
.buffer
.glyphs
[g
] = font
->get_glyph(context
->cache
->context
, codepoint
);
5712 context
->u
.buffer
.glyph_props
[g
].justification
= SCRIPT_JUSTIFY_CHARACTER
;
5713 opentype_set_subst_glyph_props(context
, g
);
5715 /* Group diacritics with preceding base. Glyph class is ignored here. */
5716 if (!g
|| !opentype_is_diacritic(codepoint
))
5718 context
->u
.buffer
.glyph_props
[g
].isClusterStart
= 1;
5719 context
->glyph_infos
[g
].start_text_idx
= i
;
5720 cluster_start_idx
= g
;
5723 if (opentype_is_default_ignorable(codepoint
))
5724 context
->u
.buffer
.glyph_props
[g
].isZeroWidthSpace
= 1;
5725 context
->u
.buffer
.glyph_props
[g
].components
= 1;
5726 context
->glyph_count
++;
5728 /* Set initial cluster map here, it's used for setting user features masks. */
5729 clustermap
[i
] = cluster_start_idx
;
5731 context
->u
.buffer
.text_props
[i
].canBreakShapingAfter
= 1;
5734 clustermap
[i
+ 1] = cluster_start_idx
;
5735 context
->u
.buffer
.text_props
[i
+ 1].canBreakShapingAfter
= 1;
5741 static BOOL
opentype_is_gsub_lookup_reversed(const struct scriptshaping_context
*context
, const struct lookup
*lookup
)
5743 unsigned int subtable_offset
, lookup_type
= lookup
->type
;
5745 if (lookup
->type
== GSUB_LOOKUP_EXTENSION_SUBST
)
5747 subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, 0);
5748 /* Assumes format 1. */
5749 lookup_type
= table_read_be_word(&context
->table
->table
, subtable_offset
+ 2);
5751 return lookup_type
== GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST
;
5754 static void opentype_layout_apply_gsub_context_lookup(struct scriptshaping_context
*context
, unsigned int lookup_index
)
5756 struct lookup lookup
= { 0 };
5757 /* Feature mask is intentionally zero, it's not used outside of main loop. */
5758 if (opentype_layout_init_lookup(context
->table
, lookup_index
, 0, &lookup
))
5759 opentype_layout_apply_gsub_lookup(context
, &lookup
);
5762 void opentype_layout_apply_gsub_features(struct scriptshaping_context
*context
, unsigned int script_index
,
5763 unsigned int language_index
, const struct shaping_features
*features
)
5765 struct lookups lookups
= { 0 };
5766 unsigned int i
, j
, start_idx
;
5769 context
->nesting_level_left
= SHAPE_MAX_NESTING_LEVEL
;
5770 context
->u
.buffer
.apply_context_lookup
= opentype_layout_apply_gsub_context_lookup
;
5771 opentype_layout_collect_lookups(context
, script_index
, language_index
, features
, context
->table
, &lookups
);
5773 opentype_get_nominal_glyphs(context
, features
);
5774 opentype_layout_set_glyph_masks(context
, features
);
5776 for (i
= 0; i
< lookups
.count
; ++i
)
5778 const struct lookup
*lookup
= &lookups
.lookups
[i
];
5780 context
->lookup_mask
= lookup
->mask
;
5782 if (!opentype_is_gsub_lookup_reversed(context
, lookup
))
5785 while (context
->cur
< context
->glyph_count
)
5789 if ((context
->glyph_infos
[context
->cur
].mask
& lookup
->mask
) &&
5790 lookup_is_glyph_match(context
, context
->cur
, lookup
->flags
))
5792 ret
= opentype_layout_apply_gsub_lookup(context
, lookup
);
5801 context
->cur
= context
->glyph_count
- 1;
5805 if ((context
->glyph_infos
[context
->cur
].mask
& lookup
->mask
) &&
5806 lookup_is_glyph_match(context
, context
->cur
, lookup
->flags
))
5808 opentype_layout_apply_gsub_lookup(context
, lookup
);
5811 if (context
->cur
== 0) break;
5817 /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding
5818 text span to start_idx. */
5820 for (i
= 1; i
< context
->glyph_count
; ++i
)
5822 if (context
->u
.buffer
.glyph_props
[i
].isClusterStart
)
5824 unsigned int start_text
, end_text
;
5826 start_text
= context
->glyph_infos
[start_idx
].start_text_idx
;
5827 end_text
= context
->glyph_infos
[i
].start_text_idx
;
5829 for (j
= start_text
; j
< end_text
; ++j
)
5830 context
->u
.buffer
.clustermap
[j
] = start_idx
;
5836 /* Fill the tail. */
5837 for (j
= context
->glyph_infos
[start_idx
].start_text_idx
; j
< context
->length
; ++j
)
5838 context
->u
.buffer
.clustermap
[j
] = start_idx
;
5840 heap_free(lookups
.lookups
);
5843 static BOOL
opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context
*context
, UINT16 glyph
,
5844 unsigned int subtable_offset
, unsigned int coverage
, unsigned int format
)
5846 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5847 const UINT16
*offsets
;
5850 if (format
== 1 || format
== 2)
5852 if (opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
) != GLYPH_NOT_COVERED
)
5855 else if (format
== 3)
5857 count
= table_read_be_word(table
, subtable_offset
+ 2);
5858 if (!count
|| !(offsets
= table_read_ensure(table
, subtable_offset
+ 6, count
* sizeof(*offsets
))))
5861 if (opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(offsets
[0]), glyph
) != GLYPH_NOT_COVERED
)
5868 static BOOL
opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context
*context
, UINT16 glyph
,
5869 unsigned int subtable_offset
, unsigned int coverage
, unsigned int format
)
5871 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5872 unsigned int count
, backtrack_count
;
5873 const UINT16
*offsets
;
5875 if (format
== 1 || format
== 2)
5877 if (opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
) != GLYPH_NOT_COVERED
)
5880 else if (format
== 3)
5882 backtrack_count
= table_read_be_word(table
, subtable_offset
+ 2);
5884 count
= table_read_be_word(table
, subtable_offset
+ 4 + backtrack_count
* sizeof(*offsets
));
5886 if (!count
|| !(offsets
= table_read_ensure(table
, subtable_offset
+ 6 + backtrack_count
* sizeof(*offsets
),
5887 count
* sizeof(*offsets
))))
5890 if (opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(offsets
[0]), glyph
) != GLYPH_NOT_COVERED
)
5897 static BOOL
opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context
*context
, UINT16 glyph
,
5898 const struct lookup
*lookup
)
5900 const struct dwrite_fonttable
*gsub
= &context
->table
->table
;
5901 static const unsigned short gsub_formats
[] =
5904 1, /* SingleSubst */
5905 1, /* MultipleSubst */
5906 1, /* AlternateSubst */
5907 1, /* LigatureSubst */
5908 3, /* ContextSubst */
5909 3, /* ChainContextSubst */
5910 0, /* Extension, unused */
5911 1, /* ReverseChainSubst */
5913 unsigned int i
, coverage
, lookup_type
, format
;
5915 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
5917 unsigned int subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, i
);
5919 lookup_type
= opentype_layout_adjust_extension_subtable(context
, &subtable_offset
, lookup
);
5921 format
= table_read_be_word(gsub
, subtable_offset
);
5923 if (!format
|| format
> ARRAY_SIZE(gsub_formats
) || format
> gsub_formats
[lookup_type
])
5926 coverage
= table_read_be_word(gsub
, subtable_offset
+ 2);
5928 switch (lookup_type
)
5930 case GSUB_LOOKUP_SINGLE_SUBST
:
5931 case GSUB_LOOKUP_MULTIPLE_SUBST
:
5932 case GSUB_LOOKUP_ALTERNATE_SUBST
:
5933 case GSUB_LOOKUP_LIGATURE_SUBST
:
5934 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST
:
5936 if (opentype_layout_is_glyph_covered(gsub
, subtable_offset
+ coverage
, glyph
) != GLYPH_NOT_COVERED
)
5941 case GSUB_LOOKUP_CONTEXTUAL_SUBST
:
5943 if (opentype_layout_contextual_lookup_is_glyph_covered(context
, glyph
, subtable_offset
, coverage
, format
))
5948 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST
:
5950 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context
, glyph
, subtable_offset
, coverage
, format
))
5956 WARN("Unknown lookup type %u.\n", lookup_type
);
5963 static BOOL
opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context
*context
, UINT16 glyph
,
5964 const struct lookup
*lookup
)
5966 const struct dwrite_fonttable
*gpos
= &context
->table
->table
;
5967 static const unsigned short gpos_formats
[] =
5973 1, /* MarkBasePos */
5975 1, /* MarkMarkPos */
5977 3, /* ChainContextPos */
5978 0, /* Extension, unused */
5980 unsigned int i
, coverage
, lookup_type
, format
;
5982 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
5984 unsigned int subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, i
);
5986 lookup_type
= opentype_layout_adjust_extension_subtable(context
, &subtable_offset
, lookup
);
5988 format
= table_read_be_word(gpos
, subtable_offset
);
5990 if (!format
|| format
> ARRAY_SIZE(gpos_formats
) || format
> gpos_formats
[lookup_type
])
5993 coverage
= table_read_be_word(gpos
, subtable_offset
+ 2);
5995 switch (lookup_type
)
5997 case GPOS_LOOKUP_SINGLE_ADJUSTMENT
:
5998 case GPOS_LOOKUP_PAIR_ADJUSTMENT
:
5999 case GPOS_LOOKUP_CURSIVE_ATTACHMENT
:
6000 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT
:
6001 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT
:
6002 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT
:
6004 if (opentype_layout_is_glyph_covered(gpos
, subtable_offset
+ coverage
, glyph
) != GLYPH_NOT_COVERED
)
6009 case GPOS_LOOKUP_CONTEXTUAL_POSITION
:
6011 if (opentype_layout_contextual_lookup_is_glyph_covered(context
, glyph
, subtable_offset
, coverage
, format
))
6016 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION
:
6018 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context
, glyph
, subtable_offset
, coverage
, format
))
6024 WARN("Unknown lookup type %u.\n", lookup_type
);
6031 typedef BOOL (*p_lookup_is_glyph_covered_func
)(struct scriptshaping_context
*context
, UINT16 glyph
, const struct lookup
*lookup
);
6033 BOOL
opentype_layout_check_feature(struct scriptshaping_context
*context
, unsigned int script_index
,
6034 unsigned int language_index
, struct shaping_feature
*feature
, unsigned int glyph_count
,
6035 const UINT16
*glyphs
, UINT8
*feature_applies
)
6037 p_lookup_is_glyph_covered_func func_is_covered
;
6038 struct shaping_features features
= { 0 };
6039 struct lookups lookups
= { 0 };
6040 BOOL ret
= FALSE
, is_covered
;
6041 unsigned int i
, j
, applies
;
6043 features
.features
= feature
;
6046 for (i
= 0; i
< context
->glyph_count
; ++i
)
6047 opentype_set_glyph_props(context
, i
);
6049 opentype_layout_collect_lookups(context
, script_index
, language_index
, &features
, context
->table
, &lookups
);
6051 func_is_covered
= context
->table
== &context
->cache
->gsub
? opentype_layout_gsub_lookup_is_glyph_covered
:
6052 opentype_layout_gpos_lookup_is_glyph_covered
;
6054 for (i
= 0; i
< lookups
.count
; ++i
)
6056 struct lookup
*lookup
= &lookups
.lookups
[i
];
6059 for (j
= 0; j
< context
->glyph_count
; ++j
)
6061 if (lookup_is_glyph_match(context
, j
, lookup
->flags
))
6063 if ((is_covered
= func_is_covered(context
, glyphs
[i
], lookup
)))
6065 feature_applies
[j
] |= is_covered
;
6069 if ((ret
= (applies
== context
->glyph_count
)))
6073 heap_free(lookups
.lookups
);
6078 BOOL
opentype_has_vertical_variants(struct dwrite_fontface
*fontface
)
6080 unsigned int i
, j
, count
= 0, lookup_type
, subtable_offset
;
6081 struct shaping_features features
= { 0 };
6082 struct shaping_feature vert_feature
= { 0 };
6083 struct scriptshaping_context context
= { 0 };
6084 struct lookups lookups
= { 0 };
6087 context
.cache
= fontface_get_shaping_cache(fontface
);
6088 context
.table
= &context
.cache
->gsub
;
6090 vert_feature
.tag
= DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6091 vert_feature
.flags
= FEATURE_GLOBAL
| FEATURE_GLOBAL_SEARCH
;
6092 vert_feature
.max_value
= 1;
6093 vert_feature
.default_value
= 1;
6095 features
.features
= &vert_feature
;
6096 features
.count
= features
.capacity
= 1;
6098 opentype_layout_collect_lookups(&context
, ~0u, ~0u, &features
, context
.table
, &lookups
);
6100 for (i
= 0; i
< lookups
.count
&& !count
; ++i
)
6102 const struct dwrite_fonttable
*table
= &context
.table
->table
;
6103 const struct lookup
*lookup
= &lookups
.lookups
[i
];
6105 for (j
= 0; j
< lookup
->subtable_count
&& !count
; ++j
)
6107 subtable_offset
= opentype_layout_get_gsubgpos_subtable(&context
, lookup
->offset
, j
);
6108 lookup_type
= opentype_layout_adjust_extension_subtable(&context
, &subtable_offset
, lookup
);
6110 if (lookup_type
!= GSUB_LOOKUP_SINGLE_SUBST
)
6113 format
= table_read_be_word(table
, subtable_offset
);
6119 else if (format
== 2)
6121 count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_singlesubst_format2
, count
));
6124 WARN("Unrecognized single substitution format %u.\n", format
);
6128 heap_free(lookups
.lookups
);
6133 HRESULT
opentype_get_vertical_glyph_variants(struct dwrite_fontface
*fontface
, unsigned int glyph_count
,
6134 const UINT16
*nominal_glyphs
, UINT16
*glyphs
)
6136 struct shaping_features features
= { 0 };
6137 struct shaping_feature vert_feature
= { 0 };
6138 struct scriptshaping_context context
= { 0 };
6139 struct lookups lookups
= { 0 };
6142 memcpy(glyphs
, nominal_glyphs
, glyph_count
* sizeof(*glyphs
));
6144 if (!(fontface
->flags
& FONTFACE_HAS_VERTICAL_VARIANTS
))
6147 context
.cache
= fontface_get_shaping_cache(fontface
);
6148 context
.u
.subst
.glyphs
= glyphs
;
6149 context
.u
.subst
.glyph_props
= heap_calloc(glyph_count
, sizeof(*context
.u
.subst
.glyph_props
));
6150 context
.u
.subst
.max_glyph_count
= glyph_count
;
6151 context
.u
.subst
.capacity
= glyph_count
;
6152 context
.glyph_infos
= heap_alloc_zero(sizeof(*context
.glyph_infos
) * glyph_count
);
6153 context
.table
= &context
.cache
->gsub
;
6155 vert_feature
.tag
= DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6156 vert_feature
.flags
= FEATURE_GLOBAL
| FEATURE_GLOBAL_SEARCH
;
6157 vert_feature
.max_value
= 1;
6158 vert_feature
.default_value
= 1;
6160 features
.features
= &vert_feature
;
6161 features
.count
= features
.capacity
= 1;
6163 opentype_layout_collect_lookups(&context
, ~0u, ~0u, &features
, context
.table
, &lookups
);
6164 opentype_layout_set_glyph_masks(&context
, &features
);
6166 for (i
= 0; i
< lookups
.count
; ++i
)
6168 const struct lookup
*lookup
= &lookups
.lookups
[i
];
6170 /* FIXME: should probably handle extension subtables. */
6171 if (lookup
->type
!= GSUB_LOOKUP_SINGLE_SUBST
)
6175 while (context
.cur
< context
.glyph_count
)
6179 if (lookup_is_glyph_match(&context
, context
.cur
, lookup
->flags
))
6180 ret
= opentype_layout_apply_gsub_lookup(&context
, lookup
);
6187 heap_free(context
.u
.subst
.glyph_props
);
6188 heap_free(context
.glyph_infos
);
6189 heap_free(lookups
.lookups
);