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
;
228 USHORT advanceWidthMax
;
229 SHORT minLeftSideBearing
;
230 SHORT minRightSideBearing
;
232 SHORT caretSlopeRise
;
236 SHORT metricDataFormat
;
237 USHORT numberOfHMetrics
;
245 DWORD strike_offset
[1];
252 DWORD glyphdata_offsets
[1];
255 struct sbix_glyph_data
280 struct cblc_bitmapsize_table
282 DWORD indexSubTableArrayOffset
;
283 DWORD indexTablesSize
;
284 DWORD numberofIndexSubTables
;
286 sbitLineMetrics hori
;
287 sbitLineMetrics vert
;
288 WORD startGlyphIndex
;
306 struct gasp_range ranges
[1];
309 enum OS2_FSSELECTION
{
310 OS2_FSSELECTION_ITALIC
= 1 << 0,
311 OS2_FSSELECTION_UNDERSCORE
= 1 << 1,
312 OS2_FSSELECTION_NEGATIVE
= 1 << 2,
313 OS2_FSSELECTION_OUTLINED
= 1 << 3,
314 OS2_FSSELECTION_STRIKEOUT
= 1 << 4,
315 OS2_FSSELECTION_BOLD
= 1 << 5,
316 OS2_FSSELECTION_REGULAR
= 1 << 6,
317 OS2_FSSELECTION_USE_TYPO_METRICS
= 1 << 7,
318 OS2_FSSELECTION_WWS
= 1 << 8,
319 OS2_FSSELECTION_OBLIQUE
= 1 << 9
335 TT_NameRecord nameRecord
[1];
365 struct vdmx_vtable entries
[1];
368 struct ot_feature_record
374 struct ot_feature_list
377 struct ot_feature_record features
[1];
382 WORD lookup_order
; /* Reserved */
383 WORD required_feature_index
;
385 WORD feature_index
[1];
388 struct ot_langsys_record
396 WORD default_langsys
;
398 struct ot_langsys_record langsys
[1];
401 struct ot_script_record
407 struct ot_script_list
410 struct ot_script_record scripts
[1];
415 GDEF_CLASS_UNCLASSIFIED
= 0,
417 GDEF_CLASS_LIGATURE
= 2,
419 GDEF_CLASS_COMPONENT
= 4,
420 GDEF_CLASS_MAX
= GDEF_CLASS_COMPONENT
,
428 UINT16 ligcaret_list
;
429 UINT16 markattach_classdef
;
430 UINT16 markglyphsetdef
;
433 struct ot_gdef_classdef_format1
441 struct ot_gdef_class_range
448 struct ot_gdef_classdef_format2
452 struct ot_gdef_class_range ranges
[1];
455 struct gpos_gsub_header
463 enum gsub_gpos_lookup_flags
465 LOOKUP_FLAG_RTL
= 0x1, /* Only used for GPOS cursive attachments. */
467 LOOKUP_FLAG_IGNORE_BASE
= 0x2,
468 LOOKUP_FLAG_IGNORE_LIGATURES
= 0x4,
469 LOOKUP_FLAG_IGNORE_MARKS
= 0x8,
470 LOOKUP_FLAG_IGNORE_MASK
= 0xe,
472 LOOKUP_FLAG_USE_MARK_FILTERING_SET
= 0x10,
473 LOOKUP_FLAG_MARK_ATTACHMENT_TYPE
= 0xff00,
476 enum glyph_prop_flags
478 GLYPH_PROP_BASE
= LOOKUP_FLAG_IGNORE_BASE
,
479 GLYPH_PROP_LIGATURE
= LOOKUP_FLAG_IGNORE_LIGATURES
,
480 GLYPH_PROP_MARK
= LOOKUP_FLAG_IGNORE_MARKS
,
483 enum gpos_lookup_type
485 GPOS_LOOKUP_SINGLE_ADJUSTMENT
= 1,
486 GPOS_LOOKUP_PAIR_ADJUSTMENT
= 2,
487 GPOS_LOOKUP_CURSIVE_ATTACHMENT
= 3,
488 GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT
= 4,
489 GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT
= 5,
490 GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT
= 6,
491 GPOS_LOOKUP_CONTEXTUAL_POSITION
= 7,
492 GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION
= 8,
493 GPOS_LOOKUP_EXTENSION_POSITION
= 9,
496 enum gsub_lookup_type
498 GSUB_LOOKUP_SINGLE_SUBST
= 1,
499 GSUB_LOOKUP_MULTIPLE_SUBST
= 2,
500 GSUB_LOOKUP_ALTERNATE_SUBST
= 3,
501 GSUB_LOOKUP_LIGATURE_SUBST
= 4,
502 GSUB_LOOKUP_CONTEXTUAL_SUBST
= 5,
503 GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST
= 6,
504 GSUB_LOOKUP_EXTENSION_SUBST
= 7,
505 GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST
= 8,
508 enum gpos_value_format
510 GPOS_VALUE_X_PLACEMENT
= 0x1,
511 GPOS_VALUE_Y_PLACEMENT
= 0x2,
512 GPOS_VALUE_X_ADVANCE
= 0x4,
513 GPOS_VALUE_Y_ADVANCE
= 0x8,
514 GPOS_VALUE_X_PLACEMENT_DEVICE
= 0x10,
515 GPOS_VALUE_Y_PLACEMENT_DEVICE
= 0x20,
516 GPOS_VALUE_X_ADVANCE_DEVICE
= 0x40,
517 GPOS_VALUE_Y_ADVANCE_DEVICE
= 0x80,
520 enum OPENTYPE_PLATFORM_ID
522 OPENTYPE_PLATFORM_UNICODE
= 0,
523 OPENTYPE_PLATFORM_MAC
,
524 OPENTYPE_PLATFORM_ISO
,
525 OPENTYPE_PLATFORM_WIN
,
526 OPENTYPE_PLATFORM_CUSTOM
529 struct ot_gsubgpos_extension_format1
533 DWORD extension_offset
;
536 struct ot_gsub_singlesubst_format1
543 struct ot_gsub_singlesubst_format2
548 UINT16 substitutes
[1];
551 struct ot_gsub_multsubst_format1
559 struct ot_gsub_altsubst_format1
567 struct ot_gsub_ligsubst_format1
571 UINT16 lig_set_count
;
575 struct ot_gsub_ligset
585 UINT16 components
[1];
588 struct ot_gsubgpos_context_format1
592 UINT16 ruleset_count
;
596 struct ot_gsubgpos_ruleset
606 WORD lookuplist_index
[1];
609 struct ot_lookup_list
615 struct ot_lookup_table
623 #define GLYPH_NOT_COVERED (~0u)
625 struct ot_coverage_format1
632 struct ot_coverage_range
636 WORD startcoverage_index
;
639 struct ot_coverage_format2
643 struct ot_coverage_range ranges
[1];
646 struct ot_gpos_device_table
654 struct ot_gpos_singlepos_format1
662 struct ot_gpos_singlepos_format2
671 struct ot_gpos_pairvalue
677 struct ot_gpos_pairset
679 WORD pairvalue_count
;
680 struct ot_gpos_pairvalue pairvalues
[1];
683 struct ot_gpos_pairpos_format1
693 struct ot_gpos_pairpos_format2
706 struct ot_gpos_anchor_format1
713 struct ot_gpos_anchor_format2
721 struct ot_gpos_anchor_format3
730 struct ot_gpos_cursive_format1
738 struct ot_gpos_mark_record
744 struct ot_gpos_mark_array
747 struct ot_gpos_mark_record records
[1];
750 struct ot_gpos_base_array
756 struct ot_gpos_mark_to_base_format1
761 WORD mark_class_count
;
766 struct ot_gpos_mark_to_lig_format1
771 WORD mark_class_count
;
776 struct ot_gpos_mark_to_mark_format1
781 WORD mark_class_count
;
790 } GSUB_SingleSubstFormat1
;
797 } GSUB_SingleSubstFormat2
;
801 WORD ExtensionLookupType
;
802 DWORD ExtensionOffset
;
803 } GSUB_ExtensionPosFormat1
;
807 enum TT_NAME_WINDOWS_ENCODING_ID
809 TT_NAME_WINDOWS_ENCODING_SYMBOL
= 0,
810 TT_NAME_WINDOWS_ENCODING_UNICODE_BMP
,
811 TT_NAME_WINDOWS_ENCODING_SJIS
,
812 TT_NAME_WINDOWS_ENCODING_PRC
,
813 TT_NAME_WINDOWS_ENCODING_BIG5
,
814 TT_NAME_WINDOWS_ENCODING_WANSUNG
,
815 TT_NAME_WINDOWS_ENCODING_JOHAB
,
816 TT_NAME_WINDOWS_ENCODING_RESERVED1
,
817 TT_NAME_WINDOWS_ENCODING_RESERVED2
,
818 TT_NAME_WINDOWS_ENCODING_RESERVED3
,
819 TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
822 enum TT_NAME_MAC_ENCODING_ID
824 TT_NAME_MAC_ENCODING_ROMAN
= 0,
825 TT_NAME_MAC_ENCODING_JAPANESE
,
826 TT_NAME_MAC_ENCODING_TRAD_CHINESE
,
827 TT_NAME_MAC_ENCODING_KOREAN
,
828 TT_NAME_MAC_ENCODING_ARABIC
,
829 TT_NAME_MAC_ENCODING_HEBREW
,
830 TT_NAME_MAC_ENCODING_GREEK
,
831 TT_NAME_MAC_ENCODING_RUSSIAN
,
832 TT_NAME_MAC_ENCODING_RSYMBOL
,
833 TT_NAME_MAC_ENCODING_DEVANAGARI
,
834 TT_NAME_MAC_ENCODING_GURMUKHI
,
835 TT_NAME_MAC_ENCODING_GUJARATI
,
836 TT_NAME_MAC_ENCODING_ORIYA
,
837 TT_NAME_MAC_ENCODING_BENGALI
,
838 TT_NAME_MAC_ENCODING_TAMIL
,
839 TT_NAME_MAC_ENCODING_TELUGU
,
840 TT_NAME_MAC_ENCODING_KANNADA
,
841 TT_NAME_MAC_ENCODING_MALAYALAM
,
842 TT_NAME_MAC_ENCODING_SINHALESE
,
843 TT_NAME_MAC_ENCODING_BURMESE
,
844 TT_NAME_MAC_ENCODING_KHMER
,
845 TT_NAME_MAC_ENCODING_THAI
,
846 TT_NAME_MAC_ENCODING_LAOTIAN
,
847 TT_NAME_MAC_ENCODING_GEORGIAN
,
848 TT_NAME_MAC_ENCODING_ARMENIAN
,
849 TT_NAME_MAC_ENCODING_SIMPL_CHINESE
,
850 TT_NAME_MAC_ENCODING_TIBETAN
,
851 TT_NAME_MAC_ENCODING_MONGOLIAN
,
852 TT_NAME_MAC_ENCODING_GEEZ
,
853 TT_NAME_MAC_ENCODING_SLAVIC
,
854 TT_NAME_MAC_ENCODING_VIETNAMESE
,
855 TT_NAME_MAC_ENCODING_SINDHI
,
856 TT_NAME_MAC_ENCODING_UNINTERPRETED
859 enum TT_NAME_MAC_LANGUAGE_ID
861 TT_NAME_MAC_LANGID_ENGLISH
= 0,
862 TT_NAME_MAC_LANGID_FRENCH
,
863 TT_NAME_MAC_LANGID_GERMAN
,
864 TT_NAME_MAC_LANGID_ITALIAN
,
865 TT_NAME_MAC_LANGID_DUTCH
,
866 TT_NAME_MAC_LANGID_SWEDISH
,
867 TT_NAME_MAC_LANGID_SPANISH
,
868 TT_NAME_MAC_LANGID_DANISH
,
869 TT_NAME_MAC_LANGID_PORTUGUESE
,
870 TT_NAME_MAC_LANGID_NORWEGIAN
,
871 TT_NAME_MAC_LANGID_HEBREW
,
872 TT_NAME_MAC_LANGID_JAPANESE
,
873 TT_NAME_MAC_LANGID_ARABIC
,
874 TT_NAME_MAC_LANGID_FINNISH
,
875 TT_NAME_MAC_LANGID_GREEK
,
876 TT_NAME_MAC_LANGID_ICELANDIC
,
877 TT_NAME_MAC_LANGID_MALTESE
,
878 TT_NAME_MAC_LANGID_TURKISH
,
879 TT_NAME_MAC_LANGID_CROATIAN
,
880 TT_NAME_MAC_LANGID_TRAD_CHINESE
,
881 TT_NAME_MAC_LANGID_URDU
,
882 TT_NAME_MAC_LANGID_HINDI
,
883 TT_NAME_MAC_LANGID_THAI
,
884 TT_NAME_MAC_LANGID_KOREAN
,
885 TT_NAME_MAC_LANGID_LITHUANIAN
,
886 TT_NAME_MAC_LANGID_POLISH
,
887 TT_NAME_MAC_LANGID_HUNGARIAN
,
888 TT_NAME_MAC_LANGID_ESTONIAN
,
889 TT_NAME_MAC_LANGID_LATVIAN
,
890 TT_NAME_MAC_LANGID_SAMI
,
891 TT_NAME_MAC_LANGID_FAROESE
,
892 TT_NAME_MAC_LANGID_FARSI
,
893 TT_NAME_MAC_LANGID_RUSSIAN
,
894 TT_NAME_MAC_LANGID_SIMPL_CHINESE
,
895 TT_NAME_MAC_LANGID_FLEMISH
,
896 TT_NAME_MAC_LANGID_GAELIC
,
897 TT_NAME_MAC_LANGID_ALBANIAN
,
898 TT_NAME_MAC_LANGID_ROMANIAN
,
899 TT_NAME_MAC_LANGID_CZECH
,
900 TT_NAME_MAC_LANGID_SLOVAK
,
901 TT_NAME_MAC_LANGID_SLOVENIAN
,
902 TT_NAME_MAC_LANGID_YIDDISH
,
903 TT_NAME_MAC_LANGID_SERBIAN
,
904 TT_NAME_MAC_LANGID_MACEDONIAN
,
905 TT_NAME_MAC_LANGID_BULGARIAN
,
906 TT_NAME_MAC_LANGID_UKRAINIAN
,
907 TT_NAME_MAC_LANGID_BYELORUSSIAN
,
908 TT_NAME_MAC_LANGID_UZBEK
,
909 TT_NAME_MAC_LANGID_KAZAKH
,
910 TT_NAME_MAC_LANGID_AZERB_CYR
,
911 TT_NAME_MAC_LANGID_AZERB_ARABIC
,
912 TT_NAME_MAC_LANGID_ARMENIAN
,
913 TT_NAME_MAC_LANGID_GEORGIAN
,
914 TT_NAME_MAC_LANGID_MOLDAVIAN
,
915 TT_NAME_MAC_LANGID_KIRGHIZ
,
916 TT_NAME_MAC_LANGID_TAJIKI
,
917 TT_NAME_MAC_LANGID_TURKMEN
,
918 TT_NAME_MAC_LANGID_MONGOLIAN
,
919 TT_NAME_MAC_LANGID_MONGOLIAN_CYR
,
920 TT_NAME_MAC_LANGID_PASHTO
,
921 TT_NAME_MAC_LANGID_KURDISH
,
922 TT_NAME_MAC_LANGID_KASHMIRI
,
923 TT_NAME_MAC_LANGID_SINDHI
,
924 TT_NAME_MAC_LANGID_TIBETAN
,
925 TT_NAME_MAC_LANGID_NEPALI
,
926 TT_NAME_MAC_LANGID_SANSKRIT
,
927 TT_NAME_MAC_LANGID_MARATHI
,
928 TT_NAME_MAC_LANGID_BENGALI
,
929 TT_NAME_MAC_LANGID_ASSAMESE
,
930 TT_NAME_MAC_LANGID_GUJARATI
,
931 TT_NAME_MAC_LANGID_PUNJABI
,
932 TT_NAME_MAC_LANGID_ORIYA
,
933 TT_NAME_MAC_LANGID_MALAYALAM
,
934 TT_NAME_MAC_LANGID_KANNADA
,
935 TT_NAME_MAC_LANGID_TAMIL
,
936 TT_NAME_MAC_LANGID_TELUGU
,
937 TT_NAME_MAC_LANGID_SINHALESE
,
938 TT_NAME_MAC_LANGID_BURMESE
,
939 TT_NAME_MAC_LANGID_KHMER
,
940 TT_NAME_MAC_LANGID_LAO
,
941 TT_NAME_MAC_LANGID_VIETNAMESE
,
942 TT_NAME_MAC_LANGID_INDONESIAN
,
943 TT_NAME_MAC_LANGID_TAGALOG
,
944 TT_NAME_MAC_LANGID_MALAY_ROMAN
,
945 TT_NAME_MAC_LANGID_MALAY_ARABIC
,
946 TT_NAME_MAC_LANGID_AMHARIC
,
947 TT_NAME_MAC_LANGID_TIGRINYA
,
948 TT_NAME_MAC_LANGID_GALLA
,
949 TT_NAME_MAC_LANGID_SOMALI
,
950 TT_NAME_MAC_LANGID_SWAHILI
,
951 TT_NAME_MAC_LANGID_KINYARWANDA
,
952 TT_NAME_MAC_LANGID_RUNDI
,
953 TT_NAME_MAC_LANGID_NYANJA
,
954 TT_NAME_MAC_LANGID_MALAGASY
,
955 TT_NAME_MAC_LANGID_ESPERANTO
,
956 TT_NAME_MAC_LANGID_WELSH
= 128,
957 TT_NAME_MAC_LANGID_BASQUE
,
958 TT_NAME_MAC_LANGID_CATALAN
,
959 TT_NAME_MAC_LANGID_LATIN
,
960 TT_NAME_MAC_LANGID_QUECHUA
,
961 TT_NAME_MAC_LANGID_GUARANI
,
962 TT_NAME_MAC_LANGID_AYMARA
,
963 TT_NAME_MAC_LANGID_TATAR
,
964 TT_NAME_MAC_LANGID_UIGHUR
,
965 TT_NAME_MAC_LANGID_DZONGKHA
,
966 TT_NAME_MAC_LANGID_JAVANESE
,
967 TT_NAME_MAC_LANGID_SUNDANESE
,
968 TT_NAME_MAC_LANGID_GALICIAN
,
969 TT_NAME_MAC_LANGID_AFRIKAANS
,
970 TT_NAME_MAC_LANGID_BRETON
,
971 TT_NAME_MAC_LANGID_INUKTITUT
,
972 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC
,
973 TT_NAME_MAC_LANGID_MANX_GAELIC
,
974 TT_NAME_MAC_LANGID_IRISH_GAELIC
,
975 TT_NAME_MAC_LANGID_TONGAN
,
976 TT_NAME_MAC_LANGID_GREEK_POLYTONIC
,
977 TT_NAME_MAC_LANGID_GREENLANDIC
,
978 TT_NAME_MAC_LANGID_AZER_ROMAN
981 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
982 static const char name_mac_langid_to_locale
[][10] = {
1136 enum OPENTYPE_STRING_ID
1138 OPENTYPE_STRING_COPYRIGHT_NOTICE
= 0,
1139 OPENTYPE_STRING_FAMILY_NAME
,
1140 OPENTYPE_STRING_SUBFAMILY_NAME
,
1141 OPENTYPE_STRING_UNIQUE_IDENTIFIER
,
1142 OPENTYPE_STRING_FULL_FONTNAME
,
1143 OPENTYPE_STRING_VERSION_STRING
,
1144 OPENTYPE_STRING_POSTSCRIPT_FONTNAME
,
1145 OPENTYPE_STRING_TRADEMARK
,
1146 OPENTYPE_STRING_MANUFACTURER
,
1147 OPENTYPE_STRING_DESIGNER
,
1148 OPENTYPE_STRING_DESCRIPTION
,
1149 OPENTYPE_STRING_VENDOR_URL
,
1150 OPENTYPE_STRING_DESIGNER_URL
,
1151 OPENTYPE_STRING_LICENSE_DESCRIPTION
,
1152 OPENTYPE_STRING_LICENSE_INFO_URL
,
1153 OPENTYPE_STRING_RESERVED_ID15
,
1154 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
,
1155 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
,
1156 OPENTYPE_STRING_COMPATIBLE_FULLNAME
,
1157 OPENTYPE_STRING_SAMPLE_TEXT
,
1158 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
,
1159 OPENTYPE_STRING_WWS_FAMILY_NAME
,
1160 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
1163 static const UINT16 dwriteid_to_opentypeid
[DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME
+ 1] =
1165 (UINT16
)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
1166 OPENTYPE_STRING_COPYRIGHT_NOTICE
,
1167 OPENTYPE_STRING_VERSION_STRING
,
1168 OPENTYPE_STRING_TRADEMARK
,
1169 OPENTYPE_STRING_MANUFACTURER
,
1170 OPENTYPE_STRING_DESIGNER
,
1171 OPENTYPE_STRING_DESIGNER_URL
,
1172 OPENTYPE_STRING_DESCRIPTION
,
1173 OPENTYPE_STRING_VENDOR_URL
,
1174 OPENTYPE_STRING_LICENSE_DESCRIPTION
,
1175 OPENTYPE_STRING_LICENSE_INFO_URL
,
1176 OPENTYPE_STRING_FAMILY_NAME
,
1177 OPENTYPE_STRING_SUBFAMILY_NAME
,
1178 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
,
1179 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
,
1180 OPENTYPE_STRING_SAMPLE_TEXT
,
1181 OPENTYPE_STRING_FULL_FONTNAME
,
1182 OPENTYPE_STRING_POSTSCRIPT_FONTNAME
,
1183 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
,
1184 OPENTYPE_STRING_WWS_FAMILY_NAME
,
1188 struct cpal_header_0
1191 USHORT num_palette_entries
;
1192 USHORT num_palettes
;
1193 USHORT num_color_records
;
1194 ULONG offset_first_color_record
;
1195 USHORT color_record_indices
[1];
1198 struct cpal_color_record
1210 USHORT num_baseglyph_records
;
1211 ULONG offset_baseglyph_records
;
1212 ULONG offset_layer_records
;
1213 USHORT num_layer_records
;
1216 struct colr_baseglyph_record
1219 USHORT first_layer_index
;
1223 struct colr_layer_record
1226 USHORT palette_index
;
1229 struct meta_data_map
1241 DWORD data_maps_count
;
1242 struct meta_data_map maps
[1];
1245 static const void *table_read_ensure(const struct dwrite_fonttable
*table
, unsigned int offset
, unsigned int size
)
1247 if (size
> table
->size
|| offset
> table
->size
- size
)
1250 return table
->data
+ offset
;
1253 static WORD
table_read_be_word(const struct dwrite_fonttable
*table
, unsigned int offset
)
1255 const WORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1256 return ptr
? GET_BE_WORD(*ptr
) : 0;
1259 static DWORD
table_read_be_dword(const struct dwrite_fonttable
*table
, unsigned int offset
)
1261 const DWORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1262 return ptr
? GET_BE_DWORD(*ptr
) : 0;
1265 static DWORD
table_read_dword(const struct dwrite_fonttable
*table
, unsigned int offset
)
1267 const DWORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1268 return ptr
? *ptr
: 0;
1271 BOOL
is_face_type_supported(DWRITE_FONT_FACE_TYPE type
)
1273 return (type
== DWRITE_FONT_FACE_TYPE_CFF
) ||
1274 (type
== DWRITE_FONT_FACE_TYPE_TRUETYPE
) ||
1275 (type
== DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
) ||
1276 (type
== DWRITE_FONT_FACE_TYPE_RAW_CFF
);
1279 typedef HRESULT (*dwrite_fontfile_analyzer
)(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1280 DWRITE_FONT_FACE_TYPE
*face_type
);
1282 static HRESULT
opentype_ttc_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1283 DWRITE_FONT_FACE_TYPE
*face_type
)
1285 static const DWORD ttctag
= MS_TTCF_TAG
;
1286 const TTC_Header_V1
*header
;
1290 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(header
), &context
);
1294 if (!memcmp(header
->TTCTag
, &ttctag
, sizeof(ttctag
))) {
1295 *font_count
= GET_BE_DWORD(header
->numFonts
);
1296 *file_type
= DWRITE_FONT_FILE_TYPE_OPENTYPE_COLLECTION
;
1297 *face_type
= DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
;
1300 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1302 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1305 static HRESULT
opentype_ttf_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1306 DWRITE_FONT_FACE_TYPE
*face_type
)
1308 const DWORD
*header
;
1312 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1316 if (GET_BE_DWORD(*header
) == 0x10000) {
1318 *file_type
= DWRITE_FONT_FILE_TYPE_TRUETYPE
;
1319 *face_type
= DWRITE_FONT_FACE_TYPE_TRUETYPE
;
1322 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1324 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1327 static HRESULT
opentype_otf_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1328 DWRITE_FONT_FACE_TYPE
*face_type
)
1330 const DWORD
*header
;
1334 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1338 if (GET_BE_DWORD(*header
) == MS_OTTO_TAG
) {
1340 *file_type
= DWRITE_FONT_FILE_TYPE_CFF
;
1341 *face_type
= DWRITE_FONT_FACE_TYPE_CFF
;
1344 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1346 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1349 static HRESULT
opentype_type1_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1350 DWRITE_FONT_FACE_TYPE
*face_type
)
1352 #include "pshpack1.h"
1353 /* Specified in Adobe TechNote #5178 */
1361 #include "poppack.h"
1362 struct type1_header
{
1366 const struct type1_header
*header
;
1370 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1374 /* tag is followed by plain text section */
1375 if (header
->tag
== 0x8001 &&
1376 (!memcmp(header
->data
, "%!PS-AdobeFont", 14) ||
1377 !memcmp(header
->data
, "%!FontType", 10))) {
1379 *file_type
= DWRITE_FONT_FILE_TYPE_TYPE1_PFB
;
1380 *face_type
= DWRITE_FONT_FACE_TYPE_TYPE1
;
1383 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1385 /* let's see if it's a .pfm metrics file */
1386 if (*file_type
== DWRITE_FONT_FILE_TYPE_UNKNOWN
) {
1387 const struct pfm_header
*pfm_header
;
1390 BOOL header_checked
;
1392 hr
= IDWriteFontFileStream_GetFileSize(stream
, &filesize
);
1396 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&pfm_header
, 0, sizeof(*pfm_header
), &context
);
1400 offset
= pfm_header
->dfDevice
;
1401 header_checked
= pfm_header
->dfVersion
== 0x100 && pfm_header
->dfSize
== filesize
;
1402 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1404 /* as a last test check static string in PostScript information section */
1405 if (header_checked
) {
1406 static const char postscript
[] = "PostScript";
1409 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&devtype_name
, offset
, sizeof(postscript
), &context
);
1413 if (!memcmp(devtype_name
, postscript
, sizeof(postscript
))) {
1415 *file_type
= DWRITE_FONT_FILE_TYPE_TYPE1_PFM
;
1416 *face_type
= DWRITE_FONT_FACE_TYPE_TYPE1
;
1419 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1423 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1426 HRESULT
opentype_analyze_font(IDWriteFontFileStream
*stream
, BOOL
*supported
, DWRITE_FONT_FILE_TYPE
*file_type
,
1427 DWRITE_FONT_FACE_TYPE
*face_type
, UINT32
*face_count
)
1429 static dwrite_fontfile_analyzer fontfile_analyzers
[] = {
1430 opentype_ttf_analyzer
,
1431 opentype_otf_analyzer
,
1432 opentype_ttc_analyzer
,
1433 opentype_type1_analyzer
,
1436 dwrite_fontfile_analyzer
*analyzer
= fontfile_analyzers
;
1437 DWRITE_FONT_FACE_TYPE face
;
1443 *file_type
= DWRITE_FONT_FILE_TYPE_UNKNOWN
;
1444 *face_type
= DWRITE_FONT_FACE_TYPE_UNKNOWN
;
1448 hr
= (*analyzer
)(stream
, face_count
, file_type
, face_type
);
1458 *supported
= is_face_type_supported(*face_type
);
1462 HRESULT
opentype_try_get_font_table(const struct file_stream_desc
*stream_desc
, UINT32 tag
, const void **table_data
,
1463 void **table_context
, UINT32
*table_size
, BOOL
*found
)
1465 void *table_directory_context
, *sfnt_context
;
1466 TT_TableRecord
*table_record
= NULL
;
1467 TTC_SFNT_V1
*font_header
= NULL
;
1468 UINT32 table_offset
= 0;
1472 if (found
) *found
= FALSE
;
1473 if (table_size
) *table_size
= 0;
1476 *table_context
= NULL
;
1478 if (stream_desc
->face_type
== DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
) {
1479 const TTC_Header_V1
*ttc_header
;
1481 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&ttc_header
, 0, sizeof(*ttc_header
), &ttc_context
);
1482 if (SUCCEEDED(hr
)) {
1483 if (stream_desc
->face_index
>= GET_BE_DWORD(ttc_header
->numFonts
))
1486 table_offset
= GET_BE_DWORD(ttc_header
->OffsetTable
[stream_desc
->face_index
]);
1487 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&font_header
, table_offset
, sizeof(*font_header
), &sfnt_context
);
1489 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, ttc_context
);
1493 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&font_header
, 0, sizeof(*font_header
), &sfnt_context
);
1498 table_count
= GET_BE_WORD(font_header
->numTables
);
1499 table_offset
+= sizeof(*font_header
);
1501 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, sfnt_context
);
1503 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void **)&table_record
, table_offset
,
1504 table_count
* sizeof(*table_record
), &table_directory_context
);
1508 for (i
= 0; i
< table_count
; i
++) {
1509 if (table_record
->tag
== tag
) {
1510 UINT32 offset
= GET_BE_DWORD(table_record
->offset
);
1511 UINT32 length
= GET_BE_DWORD(table_record
->length
);
1516 *table_size
= length
;
1517 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, table_data
, offset
,
1518 length
, table_context
);
1524 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, table_directory_context
);
1530 static HRESULT
opentype_get_font_table(const struct file_stream_desc
*stream_desc
, UINT32 tag
,
1531 struct dwrite_fonttable
*table
)
1533 return opentype_try_get_font_table(stream_desc
, tag
, (const void **)&table
->data
, &table
->context
, &table
->size
, &table
->exists
);
1540 static UINT16
opentype_cmap_format0_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1542 const UINT8
*glyphs
= cmap
->data
;
1543 return (ch
< 0xff) ? glyphs
[ch
] : 0;
1546 static unsigned int opentype_cmap_format0_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1547 DWRITE_UNICODE_RANGE
*ranges
)
1558 struct cmap_format4_compare_context
1560 const struct dwrite_cmap
*cmap
;
1564 static int cmap_format4_compare_range(const void *a
, const void *b
)
1566 const struct cmap_format4_compare_context
*key
= a
;
1567 const UINT16
*end
= b
;
1570 if (key
->ch
> GET_BE_WORD(*end
))
1573 idx
= end
- key
->cmap
->u
.format4
.ends
;
1574 if (key
->ch
< GET_BE_WORD(key
->cmap
->u
.format4
.starts
[idx
]))
1580 static UINT16
opentype_cmap_format4_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1582 struct cmap_format4_compare_context key
= { .cmap
= cmap
, .ch
= ch
};
1583 unsigned int glyph
, idx
, range_offset
;
1584 const UINT16
*end_found
;
1586 /* Look up range. */
1587 end_found
= bsearch(&key
, cmap
->u
.format4
.ends
, cmap
->u
.format4
.seg_count
, sizeof(*cmap
->u
.format4
.ends
),
1588 cmap_format4_compare_range
);
1592 idx
= end_found
- cmap
->u
.format4
.ends
;
1594 range_offset
= GET_BE_WORD(cmap
->u
.format4
.id_range_offset
[idx
]);
1598 glyph
= ch
+ GET_BE_WORD(cmap
->u
.format4
.id_delta
[idx
]);
1602 unsigned int index
= range_offset
/ 2 + (ch
- GET_BE_WORD(cmap
->u
.format4
.starts
[idx
])) + idx
- cmap
->u
.format4
.seg_count
;
1603 if (index
>= cmap
->u
.format4
.glyph_id_array_len
)
1605 glyph
= GET_BE_WORD(cmap
->u
.format4
.glyph_id_array
[index
]);
1608 glyph
+= GET_BE_WORD(cmap
->u
.format4
.id_delta
[idx
]);
1611 return glyph
& 0xffff;
1614 static unsigned int opentype_cmap_format4_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1615 DWRITE_UNICODE_RANGE
*ranges
)
1619 count
= min(count
, cmap
->u
.format4
.seg_count
);
1621 for (i
= 0; i
< count
; ++i
)
1623 ranges
[i
].first
= GET_BE_WORD(cmap
->u
.format4
.starts
[i
]);
1624 ranges
[i
].last
= GET_BE_WORD(cmap
->u
.format4
.ends
[i
]);
1627 return cmap
->u
.format4
.seg_count
;
1630 static UINT16
opentype_cmap_format6_10_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1632 const UINT16
*glyphs
= cmap
->data
;
1633 if (ch
< cmap
->u
.format6_10
.first
|| ch
> cmap
->u
.format6_10
.last
) return 0;
1634 return glyphs
[ch
- cmap
->u
.format6_10
.first
];
1637 static unsigned int opentype_cmap_format6_10_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1638 DWRITE_UNICODE_RANGE
*ranges
)
1642 ranges
->first
= cmap
->u
.format6_10
.first
;
1643 ranges
->last
= cmap
->u
.format6_10
.last
;
1649 static int cmap_format12_13_compare_group(const void *a
, const void *b
)
1651 const unsigned int *ch
= a
;
1652 const UINT32
*group
= b
;
1654 if (*ch
> GET_BE_DWORD(group
[1]))
1657 if (*ch
< GET_BE_DWORD(group
[0]))
1663 static UINT16
opentype_cmap_format12_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1665 const UINT32
*groups
= cmap
->data
;
1666 const UINT32
*group_found
;
1668 if (!(group_found
= bsearch(&ch
, groups
, cmap
->u
.format12_13
.group_count
, 3 * sizeof(*groups
),
1669 cmap_format12_13_compare_group
)))
1672 return GET_BE_DWORD(group_found
[0]) <= GET_BE_DWORD(group_found
[1]) ?
1673 GET_BE_DWORD(group_found
[2]) + (ch
- GET_BE_DWORD(group_found
[0])) : 0;
1676 static unsigned int opentype_cmap_format12_13_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1677 DWRITE_UNICODE_RANGE
*ranges
)
1679 unsigned int i
, group_count
= cmap
->u
.format12_13
.group_count
;
1680 const UINT32
*groups
= cmap
->data
;
1682 count
= min(count
, group_count
);
1684 for (i
= 0; i
< count
; ++i
)
1686 ranges
[i
].first
= GET_BE_DWORD(groups
[3 * i
]);
1687 ranges
[i
].last
= GET_BE_DWORD(groups
[3 * i
+ 1]);
1693 static UINT16
opentype_cmap_format13_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1695 const UINT32
*groups
= cmap
->data
;
1696 const UINT32
*group_found
;
1698 if (!(group_found
= bsearch(&ch
, groups
, cmap
->u
.format12_13
.group_count
, 3 * sizeof(*groups
),
1699 cmap_format12_13_compare_group
)))
1702 return GET_BE_DWORD(group_found
[2]);
1705 static UINT16
opentype_cmap_dummy_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1710 static unsigned int opentype_cmap_dummy_get_ranges(const struct dwrite_cmap
*cmap
, unsigned int count
,
1711 DWRITE_UNICODE_RANGE
*ranges
)
1716 UINT16
opentype_cmap_get_glyph(const struct dwrite_cmap
*cmap
, unsigned int ch
)
1720 if (!cmap
->get_glyph
) return 0;
1721 glyph
= cmap
->get_glyph(cmap
, ch
);
1722 if (!glyph
&& cmap
->symbol
&& ch
<= 0xff)
1723 glyph
= cmap
->get_glyph(cmap
, ch
+ 0xf000);
1727 static int cmap_header_compare(const void *a
, const void *b
)
1729 const UINT16
*key
= a
;
1730 const UINT16
*record
= b
;
1733 if (key
[0] < GET_BE_WORD(record
[0])) return -1;
1734 if (key
[0] > GET_BE_WORD(record
[0])) return 1;
1736 if (key
[1] < GET_BE_WORD(record
[1])) return -1;
1737 if (key
[1] > GET_BE_WORD(record
[1])) return 1;
1742 void dwrite_cmap_init(struct dwrite_cmap
*cmap
, IDWriteFontFile
*file
, unsigned int face_index
,
1743 DWRITE_FONT_FACE_TYPE face_type
)
1745 static const UINT16 encodings
[][2] =
1747 { 3, 0 }, /* MS Symbol encoding is preferred. */
1757 const struct cmap_encoding_record
*records
, *found_record
= NULL
;
1758 unsigned int length
, offset
, format
, count
, f
, i
, num_records
;
1759 struct file_stream_desc stream_desc
;
1760 struct dwrite_fonttable table
;
1761 const UINT16
*pair
= NULL
;
1764 if (cmap
->data
) return;
1766 /* For fontface stream is already available and preset. */
1767 if (!cmap
->stream
&& FAILED(hr
= get_filestream_from_file(file
, &cmap
->stream
)))
1769 WARN("Failed to get file stream, hr %#x.\n", hr
);
1773 stream_desc
.stream
= cmap
->stream
;
1774 stream_desc
.face_type
= face_type
;
1775 stream_desc
.face_index
= face_index
;
1777 opentype_get_font_table(&stream_desc
, MS_CMAP_TAG
, &table
);
1780 cmap
->table_context
= table
.context
;
1782 num_records
= table_read_be_word(&table
, 2);
1783 records
= table_read_ensure(&table
, 4, sizeof(*records
) * num_records
);
1785 for (i
= 0; i
< ARRAY_SIZE(encodings
); ++i
)
1787 pair
= encodings
[i
];
1788 if ((found_record
= bsearch(pair
, records
, num_records
, sizeof(*records
), cmap_header_compare
)))
1794 WARN("No suitable cmap table were found.\n");
1798 /* Symbol encoding. */
1799 cmap
->symbol
= pair
[0] == 3 && pair
[1] == 0;
1800 offset
= GET_BE_DWORD(found_record
->offset
);
1802 format
= table_read_be_word(&table
, offset
);
1807 cmap
->data
= table_read_ensure(&table
, offset
+ 6, 256);
1808 cmap
->get_glyph
= opentype_cmap_format0_get_glyph
;
1809 cmap
->get_ranges
= opentype_cmap_format0_get_ranges
;
1812 length
= table_read_be_word(&table
, offset
+ 2);
1813 cmap
->u
.format4
.seg_count
= count
= table_read_be_word(&table
, offset
+ 6) / 2;
1814 cmap
->u
.format4
.ends
= table_read_ensure(&table
, offset
+ 14, count
* 2);
1815 cmap
->u
.format4
.starts
= cmap
->u
.format4
.ends
+ count
+ 1;
1816 cmap
->u
.format4
.id_delta
= cmap
->u
.format4
.starts
+ count
;
1817 cmap
->u
.format4
.id_range_offset
= cmap
->u
.format4
.id_delta
+ count
;
1818 cmap
->u
.format4
.glyph_id_array
= cmap
->data
= cmap
->u
.format4
.id_range_offset
+ count
;
1819 cmap
->u
.format4
.glyph_id_array_len
= (length
- 16 - 8 * count
) / 2;
1820 cmap
->get_glyph
= opentype_cmap_format4_get_glyph
;
1821 cmap
->get_ranges
= opentype_cmap_format4_get_ranges
;
1825 /* Format 10 uses 4 byte fields. */
1826 f
= format
== 6 ? 1 : 2;
1827 cmap
->u
.format6_10
.first
= table_read_be_word(&table
, offset
+ f
* 6);
1828 count
= table_read_be_word(&table
, offset
+ f
* 8);
1829 cmap
->u
.format6_10
.last
= cmap
->u
.format6_10
.first
+ count
;
1830 cmap
->data
= table_read_ensure(&table
, offset
+ f
* 10, count
* 2);
1831 cmap
->get_glyph
= opentype_cmap_format6_10_get_glyph
;
1832 cmap
->get_ranges
= opentype_cmap_format6_10_get_ranges
;
1836 cmap
->u
.format12_13
.group_count
= count
= table_read_be_dword(&table
, offset
+ 12);
1837 cmap
->data
= table_read_ensure(&table
, offset
+ 16, count
* 3 * 4);
1838 cmap
->get_glyph
= format
== 12 ? opentype_cmap_format12_get_glyph
: opentype_cmap_format13_get_glyph
;
1839 cmap
->get_ranges
= opentype_cmap_format12_13_get_ranges
;
1842 WARN("Unhandled subtable format %u.\n", format
);
1849 /* Dummy implementation, returns 0 unconditionally. */
1851 cmap
->get_glyph
= opentype_cmap_dummy_get_glyph
;
1852 cmap
->get_ranges
= opentype_cmap_dummy_get_ranges
;
1856 void dwrite_cmap_release(struct dwrite_cmap
*cmap
)
1860 IDWriteFontFileStream_ReleaseFileFragment(cmap
->stream
, cmap
->table_context
);
1861 IDWriteFontFileStream_Release(cmap
->stream
);
1864 cmap
->stream
= NULL
;
1867 HRESULT
opentype_cmap_get_unicode_ranges(const struct dwrite_cmap
*cmap
, unsigned int max_count
, DWRITE_UNICODE_RANGE
*ranges
,
1868 unsigned int *count
)
1873 *count
= cmap
->get_ranges(cmap
, max_count
, ranges
);
1875 return *count
> max_count
? E_NOT_SUFFICIENT_BUFFER
: S_OK
;
1878 void opentype_get_font_typo_metrics(struct file_stream_desc
*stream_desc
, unsigned int *ascent
, unsigned int *descent
)
1880 struct dwrite_fonttable os2
;
1881 const TT_OS2_V2
*data
;
1883 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
1884 data
= (const TT_OS2_V2
*)os2
.data
;
1886 *ascent
= *descent
= 0;
1888 if (os2
.size
>= FIELD_OFFSET(TT_OS2_V2
, sTypoLineGap
))
1890 SHORT value
= GET_BE_WORD(data
->sTypoDescender
);
1891 *ascent
= GET_BE_WORD(data
->sTypoAscender
);
1892 *descent
= value
< 0 ? -value
: 0;
1896 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
1899 void opentype_get_font_metrics(struct file_stream_desc
*stream_desc
, DWRITE_FONT_METRICS1
*metrics
, DWRITE_CARET_METRICS
*caret
)
1901 struct dwrite_fonttable os2
, head
, post
, hhea
;
1902 const TT_OS2_V2
*tt_os2
;
1903 const TT_HEAD
*tt_head
;
1904 const TT_POST
*tt_post
;
1905 const TT_HHEA
*tt_hhea
;
1907 memset(metrics
, 0, sizeof(*metrics
));
1909 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
1910 opentype_get_font_table(stream_desc
, MS_HEAD_TAG
, &head
);
1911 opentype_get_font_table(stream_desc
, MS_POST_TAG
, &post
);
1912 opentype_get_font_table(stream_desc
, MS_HHEA_TAG
, &hhea
);
1914 tt_head
= (const TT_HEAD
*)head
.data
;
1915 tt_os2
= (const TT_OS2_V2
*)os2
.data
;
1916 tt_post
= (const TT_POST
*)post
.data
;
1917 tt_hhea
= (const TT_HHEA
*)hhea
.data
;
1920 metrics
->designUnitsPerEm
= GET_BE_WORD(tt_head
->unitsPerEm
);
1921 metrics
->glyphBoxLeft
= GET_BE_WORD(tt_head
->xMin
);
1922 metrics
->glyphBoxTop
= GET_BE_WORD(tt_head
->yMax
);
1923 metrics
->glyphBoxRight
= GET_BE_WORD(tt_head
->xMax
);
1924 metrics
->glyphBoxBottom
= GET_BE_WORD(tt_head
->yMin
);
1930 caret
->slopeRise
= GET_BE_WORD(tt_hhea
->caretSlopeRise
);
1931 caret
->slopeRun
= GET_BE_WORD(tt_hhea
->caretSlopeRun
);
1932 caret
->offset
= GET_BE_WORD(tt_hhea
->caretOffset
);
1935 caret
->slopeRise
= 0;
1936 caret
->slopeRun
= 0;
1942 USHORT version
= GET_BE_WORD(tt_os2
->version
);
1944 metrics
->ascent
= GET_BE_WORD(tt_os2
->usWinAscent
);
1945 /* Some fonts have usWinDescent value stored as signed short, which could be wrongly
1946 interpreted as large unsigned value. */
1947 metrics
->descent
= abs((SHORT
)GET_BE_WORD(tt_os2
->usWinDescent
));
1949 /* line gap is estimated using two sets of ascender/descender values and 'hhea' line gap */
1951 SHORT descender
= (SHORT
)GET_BE_WORD(tt_hhea
->descender
);
1954 linegap
= GET_BE_WORD(tt_hhea
->ascender
) + abs(descender
) + GET_BE_WORD(tt_hhea
->linegap
) -
1955 metrics
->ascent
- metrics
->descent
;
1956 metrics
->lineGap
= linegap
> 0 ? linegap
: 0;
1959 metrics
->strikethroughPosition
= GET_BE_WORD(tt_os2
->yStrikeoutPosition
);
1960 metrics
->strikethroughThickness
= GET_BE_WORD(tt_os2
->yStrikeoutSize
);
1961 metrics
->subscriptPositionX
= GET_BE_WORD(tt_os2
->ySubscriptXOffset
);
1962 /* Y offset is stored as positive offset below baseline */
1963 metrics
->subscriptPositionY
= -GET_BE_WORD(tt_os2
->ySubscriptYOffset
);
1964 metrics
->subscriptSizeX
= GET_BE_WORD(tt_os2
->ySubscriptXSize
);
1965 metrics
->subscriptSizeY
= GET_BE_WORD(tt_os2
->ySubscriptYSize
);
1966 metrics
->superscriptPositionX
= GET_BE_WORD(tt_os2
->ySuperscriptXOffset
);
1967 metrics
->superscriptPositionY
= GET_BE_WORD(tt_os2
->ySuperscriptYOffset
);
1968 metrics
->superscriptSizeX
= GET_BE_WORD(tt_os2
->ySuperscriptXSize
);
1969 metrics
->superscriptSizeY
= GET_BE_WORD(tt_os2
->ySuperscriptYSize
);
1971 /* version 2 fields */
1973 metrics
->capHeight
= GET_BE_WORD(tt_os2
->sCapHeight
);
1974 metrics
->xHeight
= GET_BE_WORD(tt_os2
->sxHeight
);
1977 if (GET_BE_WORD(tt_os2
->fsSelection
) & OS2_FSSELECTION_USE_TYPO_METRICS
) {
1978 SHORT descent
= GET_BE_WORD(tt_os2
->sTypoDescender
);
1979 metrics
->ascent
= GET_BE_WORD(tt_os2
->sTypoAscender
);
1980 metrics
->descent
= descent
< 0 ? -descent
: 0;
1981 metrics
->lineGap
= GET_BE_WORD(tt_os2
->sTypoLineGap
);
1982 metrics
->hasTypographicMetrics
= TRUE
;
1986 metrics
->strikethroughPosition
= metrics
->designUnitsPerEm
/ 3;
1988 metrics
->ascent
= GET_BE_WORD(tt_hhea
->ascender
);
1989 metrics
->descent
= abs((SHORT
)GET_BE_WORD(tt_hhea
->descender
));
1994 metrics
->underlinePosition
= GET_BE_WORD(tt_post
->underlinePosition
);
1995 metrics
->underlineThickness
= GET_BE_WORD(tt_post
->underlineThickness
);
1998 if (metrics
->underlineThickness
== 0)
1999 metrics
->underlineThickness
= metrics
->designUnitsPerEm
/ 14;
2000 if (metrics
->strikethroughThickness
== 0)
2001 metrics
->strikethroughThickness
= metrics
->underlineThickness
;
2003 /* estimate missing metrics */
2004 if (metrics
->xHeight
== 0)
2005 metrics
->xHeight
= metrics
->designUnitsPerEm
/ 2;
2006 if (metrics
->capHeight
== 0)
2007 metrics
->capHeight
= metrics
->designUnitsPerEm
* 7 / 10;
2010 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2012 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, head
.context
);
2014 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, post
.context
);
2016 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, hhea
.context
);
2019 void opentype_get_font_properties(struct file_stream_desc
*stream_desc
, struct dwrite_font_props
*props
)
2021 struct dwrite_fonttable os2
, head
, colr
, cpal
;
2022 BOOL is_symbol
, is_monospaced
;
2023 const TT_OS2_V2
*tt_os2
;
2024 const TT_HEAD
*tt_head
;
2026 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
2027 opentype_get_font_table(stream_desc
, MS_HEAD_TAG
, &head
);
2029 tt_os2
= (const TT_OS2_V2
*)os2
.data
;
2030 tt_head
= (const TT_HEAD
*)head
.data
;
2032 /* default stretch, weight and style to normal */
2033 props
->stretch
= DWRITE_FONT_STRETCH_NORMAL
;
2034 props
->weight
= DWRITE_FONT_WEIGHT_NORMAL
;
2035 props
->style
= DWRITE_FONT_STYLE_NORMAL
;
2036 memset(&props
->panose
, 0, sizeof(props
->panose
));
2037 memset(&props
->fontsig
, 0, sizeof(props
->fontsig
));
2038 memset(&props
->lf
, 0, sizeof(props
->lf
));
2041 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
2044 USHORT version
= GET_BE_WORD(tt_os2
->version
);
2045 USHORT fsSelection
= GET_BE_WORD(tt_os2
->fsSelection
);
2046 USHORT usWeightClass
= GET_BE_WORD(tt_os2
->usWeightClass
);
2047 USHORT usWidthClass
= GET_BE_WORD(tt_os2
->usWidthClass
);
2049 if (usWidthClass
> DWRITE_FONT_STRETCH_UNDEFINED
&& usWidthClass
<= DWRITE_FONT_STRETCH_ULTRA_EXPANDED
)
2050 props
->stretch
= usWidthClass
;
2052 if (usWeightClass
>= 1 && usWeightClass
<= 9)
2053 usWeightClass
*= 100;
2055 if (usWeightClass
> DWRITE_FONT_WEIGHT_ULTRA_BLACK
)
2056 props
->weight
= DWRITE_FONT_WEIGHT_ULTRA_BLACK
;
2057 else if (usWeightClass
> 0)
2058 props
->weight
= usWeightClass
;
2060 if (version
>= 4 && (fsSelection
& OS2_FSSELECTION_OBLIQUE
))
2061 props
->style
= DWRITE_FONT_STYLE_OBLIQUE
;
2062 else if (fsSelection
& OS2_FSSELECTION_ITALIC
)
2063 props
->style
= DWRITE_FONT_STYLE_ITALIC
;
2064 props
->lf
.lfItalic
= !!(fsSelection
& OS2_FSSELECTION_ITALIC
);
2066 memcpy(&props
->panose
, &tt_os2
->panose
, sizeof(props
->panose
));
2069 props
->fontsig
.fsUsb
[0] = GET_BE_DWORD(tt_os2
->ulUnicodeRange1
);
2070 props
->fontsig
.fsUsb
[1] = GET_BE_DWORD(tt_os2
->ulUnicodeRange2
);
2071 props
->fontsig
.fsUsb
[2] = GET_BE_DWORD(tt_os2
->ulUnicodeRange3
);
2072 props
->fontsig
.fsUsb
[3] = GET_BE_DWORD(tt_os2
->ulUnicodeRange4
);
2076 props
->fontsig
.fsCsb
[0] = GET_BE_DWORD(tt_os2
->ulCodePageRange1
);
2077 props
->fontsig
.fsCsb
[1] = GET_BE_DWORD(tt_os2
->ulCodePageRange2
);
2081 USHORT macStyle
= GET_BE_WORD(tt_head
->macStyle
);
2083 if (macStyle
& TT_HEAD_MACSTYLE_CONDENSED
)
2084 props
->stretch
= DWRITE_FONT_STRETCH_CONDENSED
;
2085 else if (macStyle
& TT_HEAD_MACSTYLE_EXTENDED
)
2086 props
->stretch
= DWRITE_FONT_STRETCH_EXPANDED
;
2088 if (macStyle
& TT_HEAD_MACSTYLE_BOLD
)
2089 props
->weight
= DWRITE_FONT_WEIGHT_BOLD
;
2091 if (macStyle
& TT_HEAD_MACSTYLE_ITALIC
) {
2092 props
->style
= DWRITE_FONT_STYLE_ITALIC
;
2093 props
->lf
.lfItalic
= 1;
2097 props
->lf
.lfWeight
= props
->weight
;
2099 /* FONT_IS_SYMBOL */
2100 if (!(is_symbol
= props
->panose
.familyKind
== DWRITE_PANOSE_FAMILY_SYMBOL
))
2102 struct dwrite_fonttable cmap
;
2103 int i
, offset
, num_tables
;
2105 opentype_get_font_table(stream_desc
, MS_CMAP_TAG
, &cmap
);
2109 num_tables
= table_read_be_word(&cmap
, FIELD_OFFSET(struct cmap_header
, num_tables
));
2110 offset
= FIELD_OFFSET(struct cmap_header
, tables
);
2112 for (i
= 0; !is_symbol
&& i
< num_tables
; ++i
)
2114 WORD platform
, encoding
;
2116 platform
= table_read_be_word(&cmap
, offset
+ i
* sizeof(struct cmap_encoding_record
) +
2117 FIELD_OFFSET(struct cmap_encoding_record
, platformID
));
2118 encoding
= table_read_be_word(&cmap
, offset
+ i
* sizeof(struct cmap_encoding_record
) +
2119 FIELD_OFFSET(struct cmap_encoding_record
, encodingID
));
2121 is_symbol
= platform
== OPENTYPE_CMAP_TABLE_PLATFORM_WIN
&&
2122 encoding
== OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL
;
2125 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, cmap
.context
);
2129 props
->flags
|= FONT_IS_SYMBOL
;
2131 /* FONT_IS_MONOSPACED */
2132 if (!(is_monospaced
= props
->panose
.text
.proportion
== DWRITE_PANOSE_PROPORTION_MONOSPACED
))
2134 struct dwrite_fonttable post
;
2136 opentype_get_font_table(stream_desc
, MS_POST_TAG
, &post
);
2140 is_monospaced
= !!table_read_dword(&post
, FIELD_OFFSET(TT_POST
, fixed_pitch
));
2142 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, post
.context
);
2146 props
->flags
|= FONT_IS_MONOSPACED
;
2148 /* FONT_IS_COLORED */
2149 opentype_get_font_table(stream_desc
, MS_COLR_TAG
, &colr
);
2152 opentype_get_font_table(stream_desc
, MS_CPAL_TAG
, &cpal
);
2155 props
->flags
|= FONT_IS_COLORED
;
2156 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, cpal
.context
);
2159 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, colr
.context
);
2162 TRACE("stretch=%d, weight=%d, style %d\n", props
->stretch
, props
->weight
, props
->style
);
2165 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2167 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, head
.context
);
2170 static UINT
get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform
, USHORT encoding
)
2175 case OPENTYPE_PLATFORM_UNICODE
:
2177 case OPENTYPE_PLATFORM_MAC
:
2180 case TT_NAME_MAC_ENCODING_ROMAN
:
2183 case TT_NAME_MAC_ENCODING_JAPANESE
:
2186 case TT_NAME_MAC_ENCODING_TRAD_CHINESE
:
2189 case TT_NAME_MAC_ENCODING_KOREAN
:
2192 case TT_NAME_MAC_ENCODING_ARABIC
:
2195 case TT_NAME_MAC_ENCODING_HEBREW
:
2198 case TT_NAME_MAC_ENCODING_GREEK
:
2201 case TT_NAME_MAC_ENCODING_RUSSIAN
:
2204 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE
:
2207 case TT_NAME_MAC_ENCODING_THAI
:
2211 FIXME("encoding %u not handled, platform %d.\n", encoding
, platform
);
2215 case OPENTYPE_PLATFORM_WIN
:
2218 case TT_NAME_WINDOWS_ENCODING_SYMBOL
:
2219 case TT_NAME_WINDOWS_ENCODING_UNICODE_BMP
:
2220 case TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
:
2222 case TT_NAME_WINDOWS_ENCODING_SJIS
:
2225 case TT_NAME_WINDOWS_ENCODING_PRC
:
2228 case TT_NAME_WINDOWS_ENCODING_BIG5
:
2231 case TT_NAME_WINDOWS_ENCODING_WANSUNG
:
2234 case TT_NAME_WINDOWS_ENCODING_JOHAB
:
2238 FIXME("encoding %u not handled, platform %d.\n", encoding
, platform
);
2243 FIXME("unknown platform %d\n", platform
);
2249 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform
, USHORT lang_id
, WCHAR
*locale
, USHORT locale_len
)
2251 static const WCHAR enusW
[] = {'e','n','-','U','S',0};
2254 case OPENTYPE_PLATFORM_MAC
:
2256 const char *locale_name
= NULL
;
2258 if (lang_id
> TT_NAME_MAC_LANGID_AZER_ROMAN
)
2259 WARN("invalid mac lang id %d\n", lang_id
);
2260 else if (!name_mac_langid_to_locale
[lang_id
][0])
2261 FIXME("failed to map mac lang id %d to locale name\n", lang_id
);
2263 locale_name
= name_mac_langid_to_locale
[lang_id
];
2266 MultiByteToWideChar(CP_ACP
, 0, name_mac_langid_to_locale
[lang_id
], -1, locale
, locale_len
);
2268 strcpyW(locale
, enusW
);
2271 case OPENTYPE_PLATFORM_WIN
:
2272 if (!LCIDToLocaleName(MAKELCID(lang_id
, SORT_DEFAULT
), locale
, locale_len
, 0)) {
2273 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id
, SORT_DEFAULT
));
2274 strcpyW(locale
, enusW
);
2277 case OPENTYPE_PLATFORM_UNICODE
:
2278 strcpyW(locale
, enusW
);
2281 FIXME("unknown platform %d\n", platform
);
2285 static BOOL
opentype_decode_namerecord(const TT_NAME_V0
*header
, BYTE
*storage_area
, USHORT recid
, IDWriteLocalizedStrings
*strings
)
2287 const TT_NameRecord
*record
= &header
->nameRecord
[recid
];
2288 USHORT lang_id
, length
, offset
, encoding
, platform
;
2291 platform
= GET_BE_WORD(record
->platformID
);
2292 lang_id
= GET_BE_WORD(record
->languageID
);
2293 length
= GET_BE_WORD(record
->length
);
2294 offset
= GET_BE_WORD(record
->offset
);
2295 encoding
= GET_BE_WORD(record
->encodingID
);
2297 if (lang_id
< 0x8000) {
2298 WCHAR locale
[LOCALE_NAME_MAX_LENGTH
];
2302 codepage
= get_name_record_codepage(platform
, encoding
);
2303 get_name_record_locale(platform
, lang_id
, locale
, ARRAY_SIZE(locale
));
2306 DWORD len
= MultiByteToWideChar(codepage
, 0, (LPSTR
)(storage_area
+ offset
), length
, NULL
, 0);
2307 name_string
= heap_alloc(sizeof(WCHAR
) * (len
+1));
2308 MultiByteToWideChar(codepage
, 0, (LPSTR
)(storage_area
+ offset
), length
, name_string
, len
);
2309 name_string
[len
] = 0;
2314 length
/= sizeof(WCHAR
);
2315 name_string
= heap_strdupnW((LPWSTR
)(storage_area
+ offset
), length
);
2316 for (i
= 0; i
< length
; i
++)
2317 name_string
[i
] = GET_BE_WORD(name_string
[i
]);
2320 TRACE("string %s for locale %s found\n", debugstr_w(name_string
), debugstr_w(locale
));
2321 add_localizedstring(strings
, locale
, name_string
);
2322 heap_free(name_string
);
2326 FIXME("handle NAME format 1\n");
2331 static HRESULT
opentype_get_font_strings_from_id(const void *table_data
, enum OPENTYPE_STRING_ID id
, IDWriteLocalizedStrings
**strings
)
2333 int i
, count
, candidate_mac
, candidate_unicode
;
2334 const TT_NAME_V0
*header
;
2335 BYTE
*storage_area
= 0;
2343 hr
= create_localizedstrings(strings
);
2344 if (FAILED(hr
)) return hr
;
2346 header
= table_data
;
2347 format
= GET_BE_WORD(header
->format
);
2354 FIXME("unsupported NAME format %d\n", format
);
2357 storage_area
= (LPBYTE
)table_data
+ GET_BE_WORD(header
->stringOffset
);
2358 count
= GET_BE_WORD(header
->count
);
2361 candidate_unicode
= candidate_mac
= -1;
2362 for (i
= 0; i
< count
; i
++) {
2363 const TT_NameRecord
*record
= &header
->nameRecord
[i
];
2366 if (GET_BE_WORD(record
->nameID
) != id
)
2369 platform
= GET_BE_WORD(record
->platformID
);
2372 /* Skip Unicode or Mac entries for now, fonts tend to duplicate those
2373 strings as WIN platform entries. If font does not have WIN entry for
2374 this id, we will use Mac or Unicode platform entry while assuming
2376 case OPENTYPE_PLATFORM_UNICODE
:
2377 if (candidate_unicode
== -1)
2378 candidate_unicode
= i
;
2380 case OPENTYPE_PLATFORM_MAC
:
2381 if (candidate_mac
== -1)
2384 case OPENTYPE_PLATFORM_WIN
:
2385 if (opentype_decode_namerecord(header
, storage_area
, i
, *strings
))
2389 FIXME("platform %i not supported\n", platform
);
2396 if (candidate_mac
!= -1)
2397 exists
= opentype_decode_namerecord(header
, storage_area
, candidate_mac
, *strings
);
2398 if (!exists
&& candidate_unicode
!= -1)
2399 exists
= opentype_decode_namerecord(header
, storage_area
, candidate_unicode
, *strings
);
2403 IDWriteLocalizedStrings_Release(*strings
);
2409 sort_localizedstrings(*strings
);
2411 return exists
? S_OK
: E_FAIL
;
2414 static WCHAR
*meta_get_lng_name(WCHAR
*str
, WCHAR
**ctx
)
2416 static const WCHAR delimW
[] = {',',' ',0};
2419 if (!str
) str
= *ctx
;
2420 while (*str
&& strchrW(delimW
, *str
)) str
++;
2421 if (!*str
) return NULL
;
2423 while (*str
&& !strchrW(delimW
, *str
)) str
++;
2424 if (*str
) *str
++ = 0;
2430 static HRESULT
opentype_get_font_strings_from_meta(const struct file_stream_desc
*stream_desc
,
2431 DWRITE_INFORMATIONAL_STRING_ID id
, IDWriteLocalizedStrings
**ret
)
2433 static const WCHAR emptyW
[] = { 0 };
2434 const struct meta_data_map
*maps
;
2435 IDWriteLocalizedStrings
*strings
;
2436 struct dwrite_fonttable meta
;
2437 DWORD version
, i
, count
, tag
;
2444 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG
:
2447 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG
:
2451 WARN("Unexpected id %d.\n", id
);
2455 if (FAILED(hr
= create_localizedstrings(&strings
)))
2458 opentype_get_font_table(stream_desc
, MS_META_TAG
, &meta
);
2462 version
= table_read_be_dword(&meta
, 0);
2465 WARN("Unexpected meta table version %d.\n", version
);
2469 count
= table_read_be_dword(&meta
, FIELD_OFFSET(struct meta_header
, data_maps_count
));
2470 if (!(maps
= table_read_ensure(&meta
, FIELD_OFFSET(struct meta_header
, maps
),
2471 count
* sizeof(struct meta_data_map
))))
2474 for (i
= 0; i
< count
; ++i
)
2478 if (maps
[i
].tag
== tag
&& maps
[i
].length
)
2480 DWORD length
= GET_BE_DWORD(maps
[i
].length
), j
;
2482 if ((data
= table_read_ensure(&meta
, GET_BE_DWORD(maps
[i
].offset
), length
)))
2484 WCHAR
*ptrW
= heap_alloc((length
+ 1) * sizeof(WCHAR
)), *ctx
, *token
;
2492 /* Data is stored in comma separated list, ASCII range only. */
2493 for (j
= 0; j
< length
; ++j
)
2497 token
= meta_get_lng_name(ptrW
, &ctx
);
2501 add_localizedstring(strings
, emptyW
, token
);
2502 token
= meta_get_lng_name(NULL
, &ctx
);
2510 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, meta
.context
);
2513 if (IDWriteLocalizedStrings_GetCount(strings
))
2516 IDWriteLocalizedStrings_Release(strings
);
2521 HRESULT
opentype_get_font_info_strings(const struct file_stream_desc
*stream_desc
, DWRITE_INFORMATIONAL_STRING_ID id
,
2522 IDWriteLocalizedStrings
**strings
)
2524 struct dwrite_fonttable name
;
2528 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG
:
2529 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG
:
2530 opentype_get_font_strings_from_meta(stream_desc
, id
, strings
);
2533 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2534 opentype_get_font_strings_from_id(name
.data
, dwriteid_to_opentypeid
[id
], strings
);
2536 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2542 /* FamilyName locating order is WWS Family Name -> Preferred Family Name -> Family Name. If font claims to
2543 have 'Preferred Family Name' in WWS format, then WWS name is not used. */
2544 HRESULT
opentype_get_font_familyname(struct file_stream_desc
*stream_desc
, IDWriteLocalizedStrings
**names
)
2546 struct dwrite_fonttable os2
, name
;
2547 const TT_OS2_V2
*tt_os2
;
2548 const void *name_table
;
2551 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
2552 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2554 tt_os2
= (const TT_OS2_V2
*)os2
.data
;
2555 name_table
= (const void *)name
.data
;
2559 /* if Preferred Family doesn't conform to WWS model try WWS name */
2560 if (tt_os2
&& !(GET_BE_WORD(tt_os2
->fsSelection
) & OS2_FSSELECTION_WWS
))
2561 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_WWS_FAMILY_NAME
, names
);
2566 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
, names
);
2568 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_FAMILY_NAME
, names
);
2571 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2573 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2578 /* FaceName locating order is WWS Face Name -> Preferred Face Name -> Face Name. If font claims to
2579 have 'Preferred Face Name' in WWS format, then WWS name is not used. */
2580 HRESULT
opentype_get_font_facename(struct file_stream_desc
*stream_desc
, WCHAR
*lfname
, IDWriteLocalizedStrings
**names
)
2582 struct dwrite_fonttable os2
, name
;
2583 IDWriteLocalizedStrings
*lfnames
;
2584 const TT_OS2_V2
*tt_os2
;
2585 const void *name_table
;
2588 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
2589 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2591 tt_os2
= (const TT_OS2_V2
*)os2
.data
;
2592 name_table
= name
.data
;
2596 /* if Preferred Family doesn't conform to WWS model try WWS name */
2597 if (tt_os2
&& !(GET_BE_WORD(tt_os2
->fsSelection
) & OS2_FSSELECTION_WWS
))
2598 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_WWS_SUBFAMILY_NAME
, names
);
2603 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
, names
);
2605 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_SUBFAMILY_NAME
, names
);
2607 /* User locale is preferred, with fallback to en-us. */
2609 if (SUCCEEDED(opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_FAMILY_NAME
, &lfnames
))) {
2610 static const WCHAR enusW
[] = {'e','n','-','u','s',0};
2611 WCHAR localeW
[LOCALE_NAME_MAX_LENGTH
];
2616 if (GetSystemDefaultLocaleName(localeW
, ARRAY_SIZE(localeW
)))
2617 IDWriteLocalizedStrings_FindLocaleName(lfnames
, localeW
, &index
, &exists
);
2620 IDWriteLocalizedStrings_FindLocaleName(lfnames
, enusW
, &index
, &exists
);
2626 IDWriteLocalizedStrings_GetStringLength(lfnames
, index
, &length
);
2627 nameW
= heap_alloc((length
+ 1) * sizeof(WCHAR
));
2630 IDWriteLocalizedStrings_GetString(lfnames
, index
, nameW
, length
+ 1);
2631 lstrcpynW(lfname
, nameW
, LF_FACESIZE
);
2636 IDWriteLocalizedStrings_Release(lfnames
);
2640 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2642 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2647 static const struct ot_langsys
*opentype_get_langsys(const struct ot_gsubgpos_table
*table
, unsigned int script_index
,
2648 unsigned int language_index
, unsigned int *feature_count
)
2650 unsigned int table_offset
, langsys_offset
;
2651 const struct ot_langsys
*langsys
= NULL
;
2655 if (!table
->table
.data
|| script_index
== ~0u)
2658 /* ScriptTable offset. */
2659 table_offset
= table_read_be_word(&table
->table
, table
->script_list
+ FIELD_OFFSET(struct ot_script_list
, scripts
) +
2660 script_index
* sizeof(struct ot_script_record
) + FIELD_OFFSET(struct ot_script_record
, script
));
2664 if (language_index
== ~0u)
2665 langsys_offset
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
);
2667 langsys_offset
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
+
2668 FIELD_OFFSET(struct ot_script
, langsys
) + language_index
* sizeof(struct ot_langsys_record
) +
2669 FIELD_OFFSET(struct ot_langsys_record
, langsys
));
2670 langsys_offset
+= table
->script_list
+ table_offset
;
2672 *feature_count
= table_read_be_word(&table
->table
, langsys_offset
+ FIELD_OFFSET(struct ot_langsys
, feature_count
));
2674 langsys
= table_read_ensure(&table
->table
, langsys_offset
, FIELD_OFFSET(struct ot_langsys
, feature_index
[*feature_count
]));
2681 void opentype_get_typographic_features(struct ot_gsubgpos_table
*table
, unsigned int script_index
,
2682 unsigned int language_index
, struct tag_array
*t
)
2684 unsigned int i
, total_feature_count
, script_feature_count
;
2685 const struct ot_feature_list
*feature_list
;
2686 const struct ot_langsys
*langsys
= NULL
;
2688 langsys
= opentype_get_langsys(table
, script_index
, language_index
, &script_feature_count
);
2690 total_feature_count
= table_read_be_word(&table
->table
, table
->feature_list
);
2691 if (!total_feature_count
)
2694 feature_list
= table_read_ensure(&table
->table
, table
->feature_list
,
2695 FIELD_OFFSET(struct ot_feature_list
, features
[total_feature_count
]));
2699 for (i
= 0; i
< script_feature_count
; ++i
)
2701 unsigned int feature_index
= GET_BE_WORD(langsys
->feature_index
[i
]);
2702 if (feature_index
>= total_feature_count
)
2705 if (!dwrite_array_reserve((void **)&t
->tags
, &t
->capacity
, t
->count
+ 1, sizeof(*t
->tags
)))
2708 t
->tags
[t
->count
++] = feature_list
->features
[feature_index
].tag
;
2712 static unsigned int find_vdmx_group(const struct vdmx_header
*hdr
)
2715 const struct vdmx_ratio
*ratios
= (struct vdmx_ratio
*)(hdr
+ 1);
2716 BYTE dev_x_ratio
= 1, dev_y_ratio
= 1;
2717 unsigned int group_offset
= 0;
2719 num_ratios
= GET_BE_WORD(hdr
->num_ratios
);
2721 for (i
= 0; i
< num_ratios
; i
++) {
2723 if (!ratios
[i
].bCharSet
) continue;
2725 if ((ratios
[i
].xRatio
== 0 && ratios
[i
].yStartRatio
== 0 &&
2726 ratios
[i
].yEndRatio
== 0) ||
2727 (ratios
[i
].xRatio
== dev_x_ratio
&& ratios
[i
].yStartRatio
<= dev_y_ratio
&&
2728 ratios
[i
].yEndRatio
>= dev_y_ratio
))
2730 group_offset
= GET_BE_WORD(*((WORD
*)(ratios
+ num_ratios
) + i
));
2735 return group_offset
;
2738 BOOL
opentype_get_vdmx_size(const struct dwrite_fonttable
*vdmx
, INT emsize
, UINT16
*ascent
, UINT16
*descent
)
2740 unsigned int num_ratios
, num_recs
, group_offset
, i
;
2741 const struct vdmx_header
*header
;
2742 const struct vdmx_group
*group
;
2747 num_ratios
= table_read_be_word(vdmx
, FIELD_OFFSET(struct vdmx_header
, num_ratios
));
2748 num_recs
= table_read_be_word(vdmx
, FIELD_OFFSET(struct vdmx_header
, num_recs
));
2750 header
= table_read_ensure(vdmx
, 0, sizeof(*header
) + num_ratios
* sizeof(struct vdmx_ratio
) +
2751 num_recs
* sizeof(*group
));
2756 group_offset
= find_vdmx_group(header
);
2760 num_recs
= table_read_be_word(vdmx
, group_offset
);
2761 group
= table_read_ensure(vdmx
, group_offset
, FIELD_OFFSET(struct vdmx_group
, entries
[num_recs
]));
2766 if (emsize
< group
->startsz
|| emsize
>= group
->endsz
)
2769 for (i
= 0; i
< num_recs
; ++i
)
2771 WORD ppem
= GET_BE_WORD(group
->entries
[i
].yPelHeight
);
2772 if (ppem
> emsize
) {
2773 FIXME("interpolate %d\n", emsize
);
2777 if (ppem
== emsize
) {
2778 *ascent
= (SHORT
)GET_BE_WORD(group
->entries
[i
].yMax
);
2779 *descent
= -(SHORT
)GET_BE_WORD(group
->entries
[i
].yMin
);
2787 unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable
*gasp
, float emsize
)
2789 unsigned int version
, num_ranges
, i
;
2790 const struct gasp_header
*table
;
2796 num_ranges
= table_read_be_word(gasp
, FIELD_OFFSET(struct gasp_header
, num_ranges
));
2798 table
= table_read_ensure(gasp
, 0, FIELD_OFFSET(struct gasp_header
, ranges
[num_ranges
]));
2802 version
= GET_BE_WORD(table
->version
);
2805 ERR("Unsupported gasp table format version %u.\n", version
);
2809 for (i
= 0; i
< num_ranges
; ++i
)
2811 flags
= GET_BE_WORD(table
->ranges
[i
].flags
);
2812 if (emsize
<= GET_BE_WORD(table
->ranges
[i
].max_ppem
)) break;
2819 unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable
*cpal
)
2821 return table_read_be_word(cpal
, FIELD_OFFSET(struct cpal_header_0
, num_palettes
));
2824 unsigned int opentype_get_cpal_paletteentrycount(const struct dwrite_fonttable
*cpal
)
2826 return table_read_be_word(cpal
, FIELD_OFFSET(struct cpal_header_0
, num_palette_entries
));
2829 HRESULT
opentype_get_cpal_entries(const struct dwrite_fonttable
*cpal
, unsigned int palette
,
2830 unsigned int first_entry_index
, unsigned int entry_count
, DWRITE_COLOR_F
*entries
)
2832 unsigned int num_palettes
, num_palette_entries
, i
;
2833 const struct cpal_color_record
*records
;
2834 const struct cpal_header_0
*header
;
2836 header
= table_read_ensure(cpal
, 0, sizeof(*header
));
2838 if (!cpal
->exists
|| !header
)
2839 return DWRITE_E_NOCOLOR
;
2841 num_palettes
= GET_BE_WORD(header
->num_palettes
);
2842 if (palette
>= num_palettes
)
2843 return DWRITE_E_NOCOLOR
;
2845 header
= table_read_ensure(cpal
, 0, FIELD_OFFSET(struct cpal_header_0
, color_record_indices
[palette
]));
2847 return DWRITE_E_NOCOLOR
;
2849 num_palette_entries
= GET_BE_WORD(header
->num_palette_entries
);
2850 if (first_entry_index
+ entry_count
> num_palette_entries
)
2851 return E_INVALIDARG
;
2853 records
= table_read_ensure(cpal
, GET_BE_DWORD(header
->offset_first_color_record
),
2854 sizeof(*records
) * GET_BE_WORD(header
->num_color_records
));
2856 return DWRITE_E_NOCOLOR
;
2858 first_entry_index
+= GET_BE_WORD(header
->color_record_indices
[palette
]);
2860 for (i
= 0; i
< entry_count
; i
++) {
2861 entries
[i
].u1
.r
= records
[first_entry_index
+ i
].red
/ 255.0f
;
2862 entries
[i
].u2
.g
= records
[first_entry_index
+ i
].green
/ 255.0f
;
2863 entries
[i
].u3
.b
= records
[first_entry_index
+ i
].blue
/ 255.0f
;
2864 entries
[i
].u4
.a
= records
[first_entry_index
+ i
].alpha
/ 255.0f
;
2870 static int colr_compare_gid(const void *g
, const void *r
)
2872 const struct colr_baseglyph_record
*record
= r
;
2873 UINT16 glyph
= *(UINT16
*)g
, GID
= GET_BE_WORD(record
->glyph
);
2878 else if (glyph
< GID
)
2884 HRESULT
opentype_get_colr_glyph(const struct dwrite_fonttable
*colr
, UINT16 glyph
, struct dwrite_colorglyph
*ret
)
2886 unsigned int num_baseglyph_records
, offset_baseglyph_records
;
2887 const struct colr_baseglyph_record
*record
;
2888 const struct colr_layer_record
*layer
;
2889 const struct colr_header
*header
;
2891 memset(ret
, 0, sizeof(*ret
));
2893 ret
->palette_index
= 0xffff;
2895 header
= table_read_ensure(colr
, 0, sizeof(*header
));
2899 num_baseglyph_records
= GET_BE_WORD(header
->num_baseglyph_records
);
2900 offset_baseglyph_records
= GET_BE_DWORD(header
->offset_baseglyph_records
);
2901 if (!table_read_ensure(colr
, offset_baseglyph_records
, num_baseglyph_records
* sizeof(*record
)))
2906 record
= bsearch(&glyph
, colr
->data
+ offset_baseglyph_records
, num_baseglyph_records
,
2907 sizeof(*record
), colr_compare_gid
);
2911 ret
->first_layer
= GET_BE_WORD(record
->first_layer_index
);
2912 ret
->num_layers
= GET_BE_WORD(record
->num_layers
);
2914 if ((layer
= table_read_ensure(colr
, GET_BE_DWORD(header
->offset_layer_records
),
2915 (ret
->first_layer
+ ret
->layer
) * sizeof(*layer
))))
2917 layer
+= ret
->first_layer
+ ret
->layer
;
2918 ret
->glyph
= GET_BE_WORD(layer
->glyph
);
2919 ret
->palette_index
= GET_BE_WORD(layer
->palette_index
);
2925 void opentype_colr_next_glyph(const struct dwrite_fonttable
*colr
, struct dwrite_colorglyph
*glyph
)
2927 const struct colr_layer_record
*layer
;
2928 const struct colr_header
*header
;
2930 /* iterated all the way through */
2931 if (glyph
->layer
== glyph
->num_layers
)
2934 if (!(header
= table_read_ensure(colr
, 0, sizeof(*header
))))
2939 if ((layer
= table_read_ensure(colr
, GET_BE_DWORD(header
->offset_layer_records
),
2940 (glyph
->first_layer
+ glyph
->layer
) * sizeof(*layer
))))
2942 layer
+= glyph
->first_layer
+ glyph
->layer
;
2943 glyph
->glyph
= GET_BE_WORD(layer
->glyph
);
2944 glyph
->palette_index
= GET_BE_WORD(layer
->palette_index
);
2948 BOOL
opentype_has_vertical_variants(IDWriteFontFace5
*fontface
)
2950 const struct gpos_gsub_header
*header
;
2951 const struct ot_feature_list
*featurelist
;
2952 const struct ot_lookup_list
*lookup_list
;
2953 BOOL exists
= FALSE
, ret
= FALSE
;
2960 hr
= IDWriteFontFace5_TryGetFontTable(fontface
, MS_GSUB_TAG
, &data
, &size
, &context
, &exists
);
2961 if (FAILED(hr
) || !exists
)
2965 featurelist
= (struct ot_feature_list
*)((BYTE
*)header
+ GET_BE_WORD(header
->feature_list
));
2966 lookup_list
= (const struct ot_lookup_list
*)((BYTE
*)header
+ GET_BE_WORD(header
->lookup_list
));
2968 for (i
= 0; i
< GET_BE_WORD(featurelist
->feature_count
); i
++) {
2969 if (featurelist
->features
[i
].tag
== DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING
) {
2970 const struct ot_feature
*feature
= (const struct ot_feature
*)((BYTE
*)featurelist
+ GET_BE_WORD(featurelist
->features
[i
].offset
));
2971 UINT16 lookup_count
= GET_BE_WORD(feature
->lookup_count
), index
, count
, type
;
2972 const GSUB_SingleSubstFormat2
*subst2
;
2973 const struct ot_lookup_table
*lookup_table
;
2976 if (lookup_count
== 0)
2979 for (j
= 0; j
< lookup_count
; ++j
) {
2980 /* check if lookup is empty */
2981 index
= GET_BE_WORD(feature
->lookuplist_index
[j
]);
2982 lookup_table
= (const struct ot_lookup_table
*)((BYTE
*)lookup_list
+ GET_BE_WORD(lookup_list
->lookup
[index
]));
2984 type
= GET_BE_WORD(lookup_table
->lookup_type
);
2985 if (type
!= GSUB_LOOKUP_SINGLE_SUBST
&& type
!= GSUB_LOOKUP_EXTENSION_SUBST
)
2988 count
= GET_BE_WORD(lookup_table
->subtable_count
);
2992 offset
= GET_BE_WORD(lookup_table
->subtable
[0]);
2993 if (type
== GSUB_LOOKUP_EXTENSION_SUBST
) {
2994 const GSUB_ExtensionPosFormat1
*ext
= (const GSUB_ExtensionPosFormat1
*)((const BYTE
*)lookup_table
+ offset
);
2995 if (GET_BE_WORD(ext
->SubstFormat
) == 1)
2996 offset
+= GET_BE_DWORD(ext
->ExtensionOffset
);
2998 FIXME("Unhandled Extension Substitution Format %u\n", GET_BE_WORD(ext
->SubstFormat
));
3001 subst2
= (const GSUB_SingleSubstFormat2
*)((BYTE
*)lookup_table
+ offset
);
3002 index
= GET_BE_WORD(subst2
->SubstFormat
);
3004 FIXME("Validate Single Substitution Format 1\n");
3005 else if (index
== 2) {
3006 /* SimSun-ExtB has 0 glyph count for this substitution */
3007 if (GET_BE_WORD(subst2
->GlyphCount
) > 0) {
3013 WARN("Unknown Single Substitution Format, %u\n", index
);
3018 IDWriteFontFace5_ReleaseFontTable(fontface
, context
);
3023 static BOOL
opentype_has_font_table(IDWriteFontFace5
*fontface
, UINT32 tag
)
3025 BOOL exists
= FALSE
;
3031 hr
= IDWriteFontFace5_TryGetFontTable(fontface
, tag
, &data
, &size
, &context
, &exists
);
3036 IDWriteFontFace5_ReleaseFontTable(fontface
, context
);
3041 static unsigned int opentype_get_sbix_formats(IDWriteFontFace5
*fontface
)
3043 unsigned int num_strikes
, num_glyphs
, i
, j
, ret
= 0;
3044 const struct sbix_header
*sbix_header
;
3045 struct dwrite_fonttable table
;
3047 memset(&table
, 0, sizeof(table
));
3048 table
.exists
= TRUE
;
3050 if (!get_fontface_table(fontface
, MS_MAXP_TAG
, &table
))
3053 num_glyphs
= table_read_be_word(&table
, FIELD_OFFSET(struct maxp
, num_glyphs
));
3055 IDWriteFontFace5_ReleaseFontTable(fontface
, table
.context
);
3057 memset(&table
, 0, sizeof(table
));
3058 table
.exists
= TRUE
;
3060 if (!get_fontface_table(fontface
, MS_SBIX_TAG
, &table
))
3063 num_strikes
= table_read_be_dword(&table
, FIELD_OFFSET(struct sbix_header
, num_strikes
));
3064 sbix_header
= table_read_ensure(&table
, 0, FIELD_OFFSET(struct sbix_header
, strike_offset
[num_strikes
]));
3068 for (i
= 0; i
< num_strikes
; ++i
)
3070 unsigned int strike_offset
= GET_BE_DWORD(sbix_header
->strike_offset
[i
]);
3071 const struct sbix_strike
*strike
= table_read_ensure(&table
, strike_offset
,
3072 FIELD_OFFSET(struct sbix_strike
, glyphdata_offsets
[num_glyphs
+ 1]));
3077 for (j
= 0; j
< num_glyphs
; j
++)
3079 unsigned int offset
= GET_BE_DWORD(strike
->glyphdata_offsets
[j
]);
3080 unsigned int next_offset
= GET_BE_DWORD(strike
->glyphdata_offsets
[j
+ 1]);
3081 const struct sbix_glyph_data
*glyph_data
;
3083 if (offset
== next_offset
)
3086 glyph_data
= table_read_ensure(&table
, strike_offset
+ offset
, sizeof(*glyph_data
));
3090 switch (glyph_data
->graphic_type
)
3093 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PNG
;
3096 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_JPEG
;
3099 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_TIFF
;
3102 FIXME("unexpected bitmap format %s\n", debugstr_tag(GET_BE_DWORD(glyph_data
->graphic_type
)));
3108 IDWriteFontFace5_ReleaseFontTable(fontface
, table
.context
);
3113 static unsigned int opentype_get_cblc_formats(IDWriteFontFace5
*fontface
)
3115 const unsigned int format_mask
= DWRITE_GLYPH_IMAGE_FORMATS_PNG
|
3116 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8
;
3117 const struct cblc_bitmapsize_table
*sizes
;
3118 struct dwrite_fonttable cblc
= { 0 };
3119 unsigned int num_sizes
, i
, ret
= 0;
3120 const struct cblc_header
*header
;
3123 if (!get_fontface_table(fontface
, MS_CBLC_TAG
, &cblc
))
3126 num_sizes
= table_read_be_dword(&cblc
, FIELD_OFFSET(struct cblc_header
, num_sizes
));
3127 sizes
= table_read_ensure(&cblc
, sizeof(*header
), num_sizes
* sizeof(*sizes
));
3131 for (i
= 0; i
< num_sizes
; ++i
)
3133 BYTE bpp
= sizes
[i
].bit_depth
;
3135 if ((ret
& format_mask
) == format_mask
)
3138 if (bpp
== 1 || bpp
== 2 || bpp
== 4 || bpp
== 8)
3139 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PNG
;
3141 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8
;
3145 IDWriteFontFace5_ReleaseFontTable(fontface
, cblc
.context
);
3150 UINT32
opentype_get_glyph_image_formats(IDWriteFontFace5
*fontface
)
3152 UINT32 ret
= DWRITE_GLYPH_IMAGE_FORMATS_NONE
;
3154 if (opentype_has_font_table(fontface
, MS_GLYF_TAG
))
3155 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
;
3157 if (opentype_has_font_table(fontface
, MS_CFF__TAG
) ||
3158 opentype_has_font_table(fontface
, MS_CFF2_TAG
))
3159 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_CFF
;
3161 if (opentype_has_font_table(fontface
, MS_COLR_TAG
))
3162 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_COLR
;
3164 if (opentype_has_font_table(fontface
, MS_SVG__TAG
))
3165 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_SVG
;
3167 if (opentype_has_font_table(fontface
, MS_SBIX_TAG
))
3168 ret
|= opentype_get_sbix_formats(fontface
);
3170 if (opentype_has_font_table(fontface
, MS_CBLC_TAG
))
3171 ret
|= opentype_get_cblc_formats(fontface
);
3176 DWRITE_CONTAINER_TYPE
opentype_analyze_container_type(void const *data
, UINT32 data_size
)
3180 if (data_size
< sizeof(DWORD
))
3181 return DWRITE_CONTAINER_TYPE_UNKNOWN
;
3183 /* Both WOFF and WOFF2 start with 4 bytes signature. */
3184 signature
= *(DWORD
*)data
;
3189 return DWRITE_CONTAINER_TYPE_WOFF
;
3191 return DWRITE_CONTAINER_TYPE_WOFF2
;
3193 return DWRITE_CONTAINER_TYPE_UNKNOWN
;
3197 void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache
*cache
)
3199 cache
->font
->grab_font_table(cache
->context
, MS_GSUB_TAG
, &cache
->gsub
.table
.data
, &cache
->gsub
.table
.size
,
3200 &cache
->gsub
.table
.context
);
3202 if (cache
->gsub
.table
.data
)
3204 cache
->gsub
.script_list
= table_read_be_word(&cache
->gsub
.table
, FIELD_OFFSET(struct gpos_gsub_header
, script_list
));
3205 cache
->gsub
.feature_list
= table_read_be_word(&cache
->gsub
.table
, FIELD_OFFSET(struct gpos_gsub_header
, feature_list
));
3206 cache
->gsub
.lookup_list
= table_read_be_word(&cache
->gsub
.table
, FIELD_OFFSET(struct gpos_gsub_header
, lookup_list
));
3209 cache
->font
->grab_font_table(cache
->context
, MS_GPOS_TAG
, &cache
->gpos
.table
.data
, &cache
->gpos
.table
.size
,
3210 &cache
->gpos
.table
.context
);
3212 if (cache
->gpos
.table
.data
)
3214 cache
->gpos
.script_list
= table_read_be_word(&cache
->gpos
.table
,
3215 FIELD_OFFSET(struct gpos_gsub_header
, script_list
));
3216 cache
->gpos
.feature_list
= table_read_be_word(&cache
->gpos
.table
,
3217 FIELD_OFFSET(struct gpos_gsub_header
, feature_list
));
3218 cache
->gpos
.lookup_list
= table_read_be_word(&cache
->gpos
.table
,
3219 FIELD_OFFSET(struct gpos_gsub_header
, lookup_list
));
3222 cache
->font
->grab_font_table(cache
->context
, MS_GDEF_TAG
, &cache
->gdef
.table
.data
, &cache
->gdef
.table
.size
,
3223 &cache
->gdef
.table
.context
);
3225 if (cache
->gdef
.table
.data
)
3227 unsigned int version
= table_read_be_dword(&cache
->gdef
.table
, 0);
3229 cache
->gdef
.classdef
= table_read_be_word(&cache
->gdef
.table
, FIELD_OFFSET(struct gdef_header
, classdef
));
3230 cache
->gdef
.markattachclassdef
= table_read_be_word(&cache
->gdef
.table
,
3231 FIELD_OFFSET(struct gdef_header
, markattach_classdef
));
3232 if (version
>= 0x00010002)
3233 cache
->gdef
.markglyphsetdef
= table_read_be_word(&cache
->gdef
.table
,
3234 FIELD_OFFSET(struct gdef_header
, markglyphsetdef
));
3238 unsigned int opentype_layout_find_script(const struct scriptshaping_cache
*cache
, unsigned int kind
, DWORD script
,
3239 unsigned int *script_index
)
3241 const struct ot_gsubgpos_table
*table
= kind
== MS_GSUB_TAG
? &cache
->gsub
: &cache
->gpos
;
3242 UINT16 script_count
;
3245 *script_index
= ~0u;
3247 script_count
= table_read_be_word(&table
->table
, table
->script_list
);
3251 for (i
= 0; i
< script_count
; i
++)
3253 unsigned int tag
= table_read_dword(&table
->table
, table
->script_list
+ FIELD_OFFSET(struct ot_script_list
, scripts
) +
3254 i
* sizeof(struct ot_script_record
));
3268 unsigned int opentype_layout_find_language(const struct scriptshaping_cache
*cache
, unsigned int kind
, DWORD language
,
3269 unsigned int script_index
, unsigned int *language_index
)
3271 const struct ot_gsubgpos_table
*table
= kind
== MS_GSUB_TAG
? &cache
->gsub
: &cache
->gpos
;
3272 UINT16 table_offset
, lang_count
;
3275 *language_index
= ~0u;
3277 table_offset
= table_read_be_word(&table
->table
, table
->script_list
+ FIELD_OFFSET(struct ot_script_list
, scripts
) +
3278 script_index
* sizeof(struct ot_script_record
) + FIELD_OFFSET(struct ot_script_record
, script
));
3282 lang_count
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
+
3283 FIELD_OFFSET(struct ot_script
, langsys_count
));
3284 for (i
= 0; i
< lang_count
; i
++)
3286 unsigned int tag
= table_read_dword(&table
->table
, table
->script_list
+ table_offset
+
3287 FIELD_OFFSET(struct ot_script
, langsys
) + i
* sizeof(struct ot_langsys_record
));
3289 if (tag
== language
)
3291 *language_index
= i
;
3296 /* Try 'defaultLangSys' if it's set. */
3297 if (table_read_be_word(&table
->table
, table
->script_list
+ table_offset
))
3303 static int gdef_class_compare_format2(const void *g
, const void *r
)
3305 const struct ot_gdef_class_range
*range
= r
;
3306 UINT16 glyph
= *(UINT16
*)g
;
3308 if (glyph
< GET_BE_WORD(range
->start_glyph
))
3310 else if (glyph
> GET_BE_WORD(range
->end_glyph
))
3316 static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable
*table
,
3317 unsigned int offset
, UINT16 glyph
)
3319 WORD format
= table_read_be_word(table
, offset
), count
;
3320 unsigned int glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3324 const struct ot_gdef_classdef_format1
*format1
;
3326 count
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gdef_classdef_format1
, glyph_count
));
3327 format1
= table_read_ensure(table
, offset
, FIELD_OFFSET(struct ot_gdef_classdef_format1
, classes
[count
]));
3330 WORD start_glyph
= GET_BE_WORD(format1
->start_glyph
);
3331 if (glyph
>= start_glyph
&& (glyph
- start_glyph
) < count
)
3333 glyph_class
= GET_BE_WORD(format1
->classes
[glyph
- start_glyph
]);
3334 if (glyph_class
> GDEF_CLASS_MAX
)
3335 glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3339 else if (format
== 2)
3341 const struct ot_gdef_classdef_format2
*format2
;
3343 count
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gdef_classdef_format2
, range_count
));
3344 format2
= table_read_ensure(table
, offset
, FIELD_OFFSET(struct ot_gdef_classdef_format2
, ranges
[count
]));
3347 const struct ot_gdef_class_range
*range
= bsearch(&glyph
, format2
->ranges
, count
,
3348 sizeof(struct ot_gdef_class_range
), gdef_class_compare_format2
);
3349 glyph_class
= range
&& glyph
<= GET_BE_WORD(range
->end_glyph
) ?
3350 GET_BE_WORD(range
->glyph_class
) : GDEF_CLASS_UNCLASSIFIED
;
3351 if (glyph_class
> GDEF_CLASS_MAX
)
3352 glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3356 WARN("Unknown GDEF format %u.\n", format
);
3361 static unsigned int opentype_set_glyph_props(struct scriptshaping_context
*context
, unsigned int idx
)
3363 struct scriptshaping_cache
*cache
= context
->cache
;
3364 unsigned int glyph_class
= 0, props
;
3366 if (cache
->gdef
.classdef
)
3368 glyph_class
= opentype_layout_get_glyph_class(&cache
->gdef
.table
, cache
->gdef
.classdef
,
3369 context
->u
.buffer
.glyphs
[idx
]);
3372 switch (glyph_class
)
3374 case GDEF_CLASS_BASE
:
3375 props
= GLYPH_PROP_BASE
;
3377 case GDEF_CLASS_LIGATURE
:
3378 props
= GLYPH_PROP_LIGATURE
;
3380 case GDEF_CLASS_MARK
:
3381 props
= GLYPH_PROP_MARK
;
3382 if (cache
->gdef
.markattachclassdef
)
3384 glyph_class
= opentype_layout_get_glyph_class(&cache
->gdef
.table
, cache
->gdef
.markattachclassdef
,
3385 context
->u
.buffer
.glyphs
[idx
]);
3386 props
|= glyph_class
<< 8;
3393 context
->glyph_infos
[idx
].props
= props
;
3398 static void opentype_set_subst_glyph_props(struct scriptshaping_context
*context
, unsigned int idx
)
3400 unsigned int glyph_props
= opentype_set_glyph_props(context
, idx
) & LOOKUP_FLAG_IGNORE_MASK
;
3401 context
->u
.subst
.glyph_props
[idx
].isDiacritic
= !!(glyph_props
== GLYPH_PROP_MARK
);
3402 context
->u
.subst
.glyph_props
[idx
].isZeroWidthSpace
= !!(glyph_props
== GLYPH_PROP_MARK
);
3405 struct coverage_compare_format1_context
3408 const UINT16
*table_base
;
3409 unsigned int *coverage_index
;
3412 static int coverage_compare_format1(const void *left
, const void *right
)
3414 const struct coverage_compare_format1_context
*context
= left
;
3415 UINT16 glyph
= GET_BE_WORD(*(UINT16
*)right
);
3418 ret
= context
->glyph
- glyph
;
3420 *context
->coverage_index
= (UINT16
*)right
- context
->table_base
;
3425 static int coverage_compare_format2(const void *g
, const void *r
)
3427 const struct ot_coverage_range
*range
= r
;
3428 UINT16 glyph
= *(UINT16
*)g
;
3430 if (glyph
< GET_BE_WORD(range
->start_glyph
))
3432 else if (glyph
> GET_BE_WORD(range
->end_glyph
))
3438 static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable
*table
, unsigned int coverage
,
3441 WORD format
= table_read_be_word(table
, coverage
), count
;
3443 count
= table_read_be_word(table
, coverage
+ 2);
3447 const struct ot_coverage_format1
*format1
= table_read_ensure(table
, coverage
,
3448 FIELD_OFFSET(struct ot_coverage_format1
, glyphs
[count
]));
3449 struct coverage_compare_format1_context context
;
3450 unsigned int coverage_index
= GLYPH_NOT_COVERED
;
3454 context
.glyph
= glyph
;
3455 context
.table_base
= format1
->glyphs
;
3456 context
.coverage_index
= &coverage_index
;
3458 bsearch(&context
, format1
->glyphs
, count
, sizeof(glyph
), coverage_compare_format1
);
3461 return coverage_index
;
3463 else if (format
== 2)
3465 const struct ot_coverage_format2
*format2
= table_read_ensure(table
, coverage
,
3466 FIELD_OFFSET(struct ot_coverage_format2
, ranges
[count
]));
3469 const struct ot_coverage_range
*range
= bsearch(&glyph
, format2
->ranges
, count
,
3470 sizeof(struct ot_coverage_range
), coverage_compare_format2
);
3471 return range
&& glyph
<= GET_BE_WORD(range
->end_glyph
) ?
3472 GET_BE_WORD(range
->startcoverage_index
) + glyph
- GET_BE_WORD(range
->start_glyph
) :
3477 WARN("Unknown coverage format %u.\n", format
);
3482 static inline unsigned int dwrite_popcount(unsigned int x
)
3484 #ifdef HAVE___BUILTIN_POPCOUNT
3485 return __builtin_popcount(x
);
3487 x
-= x
>> 1 & 0x55555555;
3488 x
= (x
& 0x33333333) + (x
>> 2 & 0x33333333);
3489 return ((x
+ (x
>> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
3493 static float opentype_scale_gpos_be_value(WORD value
, float emsize
, UINT16 upem
)
3495 return (short)GET_BE_WORD(value
) * emsize
/ upem
;
3498 static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context
*context
, unsigned int offset
)
3500 const struct dwrite_fonttable
*table
= &context
->table
->table
;
3501 unsigned int start_size
, end_size
, format
, value_word
;
3502 unsigned int index
, ppem
, mask
;
3508 start_size
= table_read_be_word(table
, offset
);
3509 end_size
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gpos_device_table
, end_size
));
3511 ppem
= context
->emsize
;
3512 if (ppem
< start_size
|| ppem
> end_size
)
3515 format
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gpos_device_table
, format
));
3517 if (format
< 1 || format
> 3)
3520 index
= ppem
- start_size
;
3522 value_word
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gpos_device_table
, values
[index
>> (4 - format
)]));
3523 mask
= 0xffff >> (16 - (1 << format
));
3525 value
= (value_word
>> ((index
% (4 - format
)) * (1 << format
))) & mask
;
3527 if ((unsigned int)value
>= ((mask
+ 1) >> 1))
3533 static void opentype_layout_apply_gpos_value(struct scriptshaping_context
*context
, unsigned int table_offset
,
3534 WORD value_format
, const WORD
*values
, unsigned int glyph
)
3536 const struct scriptshaping_cache
*cache
= context
->cache
;
3537 DWRITE_GLYPH_OFFSET
*offset
= &context
->offsets
[glyph
];
3538 float *advance
= &context
->advances
[glyph
];
3543 if (value_format
& GPOS_VALUE_X_PLACEMENT
)
3545 offset
->advanceOffset
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3548 if (value_format
& GPOS_VALUE_Y_PLACEMENT
)
3550 offset
->ascenderOffset
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3553 if (value_format
& GPOS_VALUE_X_ADVANCE
)
3555 *advance
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3558 if (value_format
& GPOS_VALUE_Y_ADVANCE
)
3562 if (value_format
& GPOS_VALUE_X_PLACEMENT_DEVICE
)
3564 offset
->advanceOffset
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3567 if (value_format
& GPOS_VALUE_Y_PLACEMENT_DEVICE
)
3569 offset
->ascenderOffset
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3572 if (value_format
& GPOS_VALUE_X_ADVANCE_DEVICE
)
3574 *advance
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3577 if (value_format
& GPOS_VALUE_Y_ADVANCE_DEVICE
)
3583 static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context
*context
,
3584 unsigned int lookup_offset
, unsigned int subtable
)
3586 unsigned int subtable_offset
= table_read_be_word(&context
->table
->table
, lookup_offset
+
3587 FIELD_OFFSET(struct ot_lookup_table
, subtable
[subtable
]));
3589 return lookup_offset
+ subtable_offset
;
3594 unsigned int offset
;
3595 unsigned int subtable_count
;
3601 /* First two to fit matching callback result. */
3607 struct match_context
;
3610 const struct match_context
*mc
;
3611 unsigned int subtable_offset
;
3614 typedef BOOL (*p_match_func
)(UINT16 glyph
, UINT16 glyph_data
, const struct match_data
*match_data
);
3616 struct match_context
3618 struct scriptshaping_context
*context
;
3619 unsigned int backtrack_offset
;
3620 unsigned int input_offset
;
3621 unsigned int lookahead_offset
;
3622 p_match_func match_func
;
3623 const struct lookup
*lookup
;
3626 struct glyph_iterator
3628 struct scriptshaping_context
*context
;
3633 p_match_func match_func
;
3634 const UINT16
*glyph_data
;
3635 const struct match_data
*match_data
;
3638 static void glyph_iterator_init(struct scriptshaping_context
*context
, unsigned int flags
, unsigned int pos
,
3639 unsigned int len
, struct glyph_iterator
*iter
)
3641 iter
->context
= context
;
3642 iter
->flags
= flags
;
3646 iter
->match_func
= NULL
;
3647 iter
->match_data
= NULL
;
3648 iter
->glyph_data
= NULL
;
3651 struct ot_gdef_mark_glyph_sets
3658 static BOOL
opentype_match_glyph_func(UINT16 glyph
, UINT16 glyph_data
, const struct match_data
*data
)
3660 return glyph
== glyph_data
;
3663 static BOOL
opentype_match_class_func(UINT16 glyph
, UINT16 glyph_data
, const struct match_data
*data
)
3665 const struct match_context
*mc
= data
->mc
;
3666 UINT16 glyph_class
= opentype_layout_get_glyph_class(&mc
->context
->table
->table
, data
->subtable_offset
, glyph
);
3667 return glyph_class
== glyph_data
;
3670 static BOOL
opentype_match_coverage_func(UINT16 glyph
, UINT16 glyph_data
, const struct match_data
*data
)
3672 const struct match_context
*mc
= data
->mc
;
3673 return opentype_layout_is_glyph_covered(&mc
->context
->table
->table
, data
->subtable_offset
+ glyph_data
, glyph
)
3674 != GLYPH_NOT_COVERED
;
3677 static BOOL
opentype_layout_mark_set_covers(const struct scriptshaping_cache
*cache
, unsigned int set_index
,
3680 unsigned int format
, offset
= cache
->gdef
.markglyphsetdef
, coverage_offset
, set_count
;
3685 format
= table_read_be_word(&cache
->gdef
.table
, offset
);
3689 set_count
= table_read_be_word(&cache
->gdef
.table
, offset
+ 2);
3690 if (!set_count
|| set_index
>= set_count
)
3693 coverage_offset
= table_read_be_dword(&cache
->gdef
.table
, offset
+ 2 + set_index
* sizeof(coverage_offset
));
3694 return opentype_layout_is_glyph_covered(&cache
->gdef
.table
, offset
+ coverage_offset
, glyph
) != GLYPH_NOT_COVERED
;
3697 WARN("Unexpected MarkGlyphSets format %#x.\n", format
);
3702 static BOOL
lookup_is_glyph_match(const struct scriptshaping_context
*context
, unsigned int idx
, unsigned int match_props
)
3704 unsigned int glyph_props
= context
->glyph_infos
[idx
].props
;
3705 UINT16 glyph
= context
->u
.buffer
.glyphs
[idx
];
3707 if (glyph_props
& match_props
& LOOKUP_FLAG_IGNORE_MASK
)
3710 if (!(glyph_props
& GLYPH_PROP_MARK
))
3713 if (match_props
& LOOKUP_FLAG_USE_MARK_FILTERING_SET
)
3714 return opentype_layout_mark_set_covers(context
->cache
, match_props
>> 16, glyph
);
3716 if (match_props
& LOOKUP_FLAG_MARK_ATTACHMENT_TYPE
)
3717 return (match_props
& LOOKUP_FLAG_MARK_ATTACHMENT_TYPE
) == (glyph_props
& LOOKUP_FLAG_MARK_ATTACHMENT_TYPE
);
3722 static enum iterator_match
glyph_iterator_may_skip(const struct glyph_iterator
*iter
)
3724 if (!lookup_is_glyph_match(iter
->context
, iter
->pos
, iter
->flags
))
3730 static enum iterator_match
glyph_iterator_may_match(const struct glyph_iterator
*iter
)
3732 if (!(iter
->mask
& iter
->context
->glyph_infos
[iter
->pos
].mask
))
3735 /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that
3736 in all matching functions. */
3737 if (iter
->match_func
)
3738 return !!iter
->match_func(iter
->context
->u
.buffer
.glyphs
[iter
->pos
], GET_BE_WORD(*iter
->glyph_data
), iter
->match_data
);
3743 static BOOL
glyph_iterator_next(struct glyph_iterator
*iter
)
3745 enum iterator_match skip
, match
;
3747 while (iter
->pos
+ iter
->len
< iter
->context
->glyph_count
)
3751 skip
= glyph_iterator_may_skip(iter
);
3752 if (skip
== ITER_YES
)
3755 match
= glyph_iterator_may_match(iter
);
3756 if (match
== ITER_YES
|| (match
== ITER_MAYBE
&& skip
== ITER_NO
))
3759 if (iter
->glyph_data
)
3764 if (skip
== ITER_NO
)
3771 static BOOL
glyph_iterator_prev(struct glyph_iterator
*iter
)
3773 enum iterator_match skip
, match
;
3775 while (iter
->pos
> iter
->len
- 1)
3779 skip
= glyph_iterator_may_skip(iter
);
3780 if (skip
== ITER_YES
)
3783 match
= glyph_iterator_may_match(iter
);
3784 if (match
== ITER_YES
|| (match
== ITER_MAYBE
&& skip
== ITER_NO
))
3787 if (iter
->glyph_data
)
3792 if (skip
== ITER_NO
)
3801 unsigned short index
;
3802 unsigned short type
;
3803 unsigned short flags
;
3804 unsigned short subtable_count
;
3807 unsigned int offset
;
3810 static BOOL
opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context
*context
,
3811 const struct lookup
*lookup
, unsigned int subtable_offset
)
3813 const struct dwrite_fonttable
*table
= &context
->table
->table
;
3814 UINT16 format
, value_format
, value_len
, coverage
, glyph
;
3816 unsigned int coverage_index
;
3818 format
= table_read_be_word(table
, subtable_offset
);
3820 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_singlepos_format1
, coverage
));
3821 value_format
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_singlepos_format1
, value_format
));
3822 value_len
= dwrite_popcount(value_format
);
3824 glyph
= context
->u
.pos
.glyphs
[context
->cur
];
3828 const struct ot_gpos_singlepos_format1
*format1
= table_read_ensure(table
, subtable_offset
,
3829 FIELD_OFFSET(struct ot_gpos_singlepos_format1
, value
[value_len
]));
3831 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
3832 if (coverage_index
== GLYPH_NOT_COVERED
)
3835 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format
, format1
->value
, context
->cur
);
3837 else if (format
== 2)
3839 WORD value_count
= table_read_be_word(table
, subtable_offset
+
3840 FIELD_OFFSET(struct ot_gpos_singlepos_format2
, value_count
));
3841 const struct ot_gpos_singlepos_format2
*format2
= table_read_ensure(table
, subtable_offset
,
3842 FIELD_OFFSET(struct ot_gpos_singlepos_format2
, values
) + value_count
* value_len
* sizeof(WORD
));
3844 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
3845 if (coverage_index
== GLYPH_NOT_COVERED
|| coverage_index
>= value_count
)
3848 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format
, &format2
->values
[coverage_index
* value_len
],
3853 WARN("Unknown single adjustment format %u.\n", format
);
3862 static int gpos_pair_adjustment_compare_format1(const void *g
, const void *r
)
3864 const struct ot_gpos_pairvalue
*pairvalue
= r
;
3865 UINT16 second_glyph
= GET_BE_WORD(pairvalue
->second_glyph
);
3866 return *(UINT16
*)g
- second_glyph
;
3869 static BOOL
opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context
*context
,
3870 const struct lookup
*lookup
, unsigned int subtable_offset
)
3872 const struct dwrite_fonttable
*table
= &context
->table
->table
;
3873 unsigned int first_glyph
, second_glyph
;
3874 struct glyph_iterator iter_pair
;
3875 WORD format
, coverage
;
3877 WORD value_format1
, value_format2
, value_len1
, value_len2
;
3878 unsigned int coverage_index
;
3880 glyph_iterator_init(context
, lookup
->flags
, context
->cur
, 1, &iter_pair
);
3881 if (!glyph_iterator_next(&iter_pair
))
3884 if (context
->is_rtl
)
3886 first_glyph
= iter_pair
.pos
;
3887 second_glyph
= context
->cur
;
3891 first_glyph
= context
->cur
;
3892 second_glyph
= iter_pair
.pos
;
3895 format
= table_read_be_word(table
, subtable_offset
);
3897 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_pairpos_format1
, coverage
));
3901 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, context
->u
.pos
.glyphs
[first_glyph
]);
3902 if (coverage_index
== GLYPH_NOT_COVERED
)
3907 const struct ot_gpos_pairpos_format1
*format1
;
3908 WORD pairset_count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_pairpos_format1
,
3910 unsigned int pairvalue_len
, pairset_offset
;
3911 const struct ot_gpos_pairset
*pairset
;
3912 const WORD
*pairvalue
;
3913 WORD pairvalue_count
;
3915 if (!pairset_count
|| coverage_index
>= pairset_count
)
3918 format1
= table_read_ensure(table
, subtable_offset
, FIELD_OFFSET(struct ot_gpos_pairpos_format1
, pairsets
[pairset_count
]));
3922 /* Ordered paired values. */
3923 pairvalue_count
= table_read_be_word(table
, subtable_offset
+ GET_BE_WORD(format1
->pairsets
[coverage_index
]));
3924 if (!pairvalue_count
)
3927 /* Structure length is variable, but does not change across the subtable. */
3928 value_format1
= GET_BE_WORD(format1
->value_format1
) & 0xff;
3929 value_format2
= GET_BE_WORD(format1
->value_format2
) & 0xff;
3931 value_len1
= dwrite_popcount(value_format1
);
3932 value_len2
= dwrite_popcount(value_format2
);
3933 pairvalue_len
= FIELD_OFFSET(struct ot_gpos_pairvalue
, data
) + value_len1
* sizeof(WORD
) +
3934 value_len2
* sizeof(WORD
);
3936 pairset_offset
= subtable_offset
+ GET_BE_WORD(format1
->pairsets
[coverage_index
]);
3937 pairset
= table_read_ensure(table
, subtable_offset
+ pairset_offset
, pairvalue_len
* pairvalue_count
);
3941 pairvalue
= bsearch(&context
->u
.pos
.glyphs
[second_glyph
], pairset
->pairvalues
, pairvalue_count
,
3942 pairvalue_len
, gpos_pair_adjustment_compare_format1
);
3946 pairvalue
+= 1; /* Skip SecondGlyph. */
3947 opentype_layout_apply_gpos_value(context
, pairset_offset
, value_format1
, pairvalue
, first_glyph
);
3948 opentype_layout_apply_gpos_value(context
, pairset_offset
, value_format2
, pairvalue
+ value_len1
,
3951 context
->cur
= iter_pair
.pos
;
3955 else if (format
== 2)
3957 const struct ot_gpos_pairpos_format2
*format2
;
3958 WORD class1_count
, class2_count
;
3959 unsigned int class1
, class2
;
3960 const WCHAR
*values
;
3962 value_format1
= table_read_be_word(table
, subtable_offset
+
3963 FIELD_OFFSET(struct ot_gpos_pairpos_format2
, value_format1
)) & 0xff;
3964 value_format2
= table_read_be_word(table
, subtable_offset
+
3965 FIELD_OFFSET(struct ot_gpos_pairpos_format2
, value_format2
)) & 0xff;
3967 class1_count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_pairpos_format2
, class1_count
));
3968 class2_count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_pairpos_format2
, class2_count
));
3970 value_len1
= dwrite_popcount(value_format1
);
3971 value_len2
= dwrite_popcount(value_format2
);
3973 format2
= table_read_ensure(table
, subtable_offset
, FIELD_OFFSET(struct ot_gpos_pairpos_format2
,
3974 values
[class1_count
* class2_count
* (value_len1
+ value_len2
)]));
3978 class1
= opentype_layout_get_glyph_class(table
, subtable_offset
+ GET_BE_WORD(format2
->class_def1
),
3979 context
->u
.pos
.glyphs
[first_glyph
]);
3980 class2
= opentype_layout_get_glyph_class(table
, subtable_offset
+ GET_BE_WORD(format2
->class_def2
),
3981 context
->u
.pos
.glyphs
[second_glyph
]);
3983 if (!(class1
< class1_count
&& class2
< class2_count
))
3986 values
= &format2
->values
[(class1
* class2_count
+ class2
) * (value_len1
+ value_len2
)];
3987 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format1
, values
, first_glyph
);
3988 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format2
, values
+ value_len1
,
3991 context
->cur
= iter_pair
.pos
;
3997 WARN("Unknown pair adjustment format %u.\n", format
);
4004 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context
*context
, unsigned int anchor_offset
,
4005 unsigned int glyph_index
, float *x
, float *y
)
4007 const struct scriptshaping_cache
*cache
= context
->cache
;
4008 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4010 WORD format
= table_read_be_word(table
, anchor_offset
);
4016 const struct ot_gpos_anchor_format1
*format1
= table_read_ensure(table
, anchor_offset
, sizeof(*format1
));
4020 *x
= opentype_scale_gpos_be_value(format1
->x_coord
, context
->emsize
, cache
->upem
);
4021 *y
= opentype_scale_gpos_be_value(format1
->y_coord
, context
->emsize
, cache
->upem
);
4024 else if (format
== 2)
4026 const struct ot_gpos_anchor_format2
*format2
= table_read_ensure(table
, anchor_offset
, sizeof(*format2
));
4030 if (context
->measuring_mode
!= DWRITE_MEASURING_MODE_NATURAL
)
4031 FIXME("Use outline anchor point for glyph %u.\n", context
->u
.pos
.glyphs
[glyph_index
]);
4033 *x
= opentype_scale_gpos_be_value(format2
->x_coord
, context
->emsize
, cache
->upem
);
4034 *y
= opentype_scale_gpos_be_value(format2
->y_coord
, context
->emsize
, cache
->upem
);
4037 else if (format
== 3)
4039 const struct ot_gpos_anchor_format3
*format3
= table_read_ensure(table
, anchor_offset
, sizeof(*format3
));
4043 *x
= opentype_scale_gpos_be_value(format3
->x_coord
, context
->emsize
, cache
->upem
);
4044 *y
= opentype_scale_gpos_be_value(format3
->y_coord
, context
->emsize
, cache
->upem
);
4046 if (context
->measuring_mode
!= DWRITE_MEASURING_MODE_NATURAL
)
4048 if (format3
->x_dev_offset
)
4049 *x
+= opentype_layout_gpos_get_dev_value(context
, anchor_offset
+ GET_BE_WORD(format3
->x_dev_offset
));
4050 if (format3
->y_dev_offset
)
4051 *y
+= opentype_layout_gpos_get_dev_value(context
, anchor_offset
+ GET_BE_WORD(format3
->y_dev_offset
));
4056 WARN("Unknown anchor format %u.\n", format
);
4059 static BOOL
opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context
*context
,
4060 const struct lookup
*lookup
, unsigned int subtable_offset
)
4062 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4063 UINT16 format
, glyph
;
4065 format
= table_read_be_word(table
, subtable_offset
);
4066 glyph
= context
->u
.pos
.glyphs
[context
->cur
];
4070 WORD coverage_offset
= table_read_be_word(table
, subtable_offset
+
4071 FIELD_OFFSET(struct ot_gpos_cursive_format1
, coverage
));
4072 unsigned int glyph_index
, entry_count
, entry_anchor
, exit_anchor
;
4073 float entry_x
, entry_y
, exit_x
, exit_y
, delta
;
4074 struct glyph_iterator prev_iter
;
4076 if (!coverage_offset
)
4079 entry_count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gpos_cursive_format1
, count
));
4081 glyph_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage_offset
, glyph
);
4082 if (glyph_index
== GLYPH_NOT_COVERED
|| glyph_index
>= entry_count
)
4085 entry_anchor
= table_read_be_word(table
, subtable_offset
+
4086 FIELD_OFFSET(struct ot_gpos_cursive_format1
, anchors
[glyph_index
* 2]));
4090 glyph_iterator_init(context
, lookup
->flags
, context
->cur
, 1, &prev_iter
);
4091 if (!glyph_iterator_prev(&prev_iter
))
4094 glyph_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage_offset
,
4095 context
->u
.pos
.glyphs
[prev_iter
.pos
]);
4096 if (glyph_index
== GLYPH_NOT_COVERED
|| glyph_index
>= entry_count
)
4099 exit_anchor
= table_read_be_word(table
, subtable_offset
+
4100 FIELD_OFFSET(struct ot_gpos_cursive_format1
, anchors
[glyph_index
* 2 + 1]));
4104 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ exit_anchor
, prev_iter
.pos
, &exit_x
, &exit_y
);
4105 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ entry_anchor
, context
->cur
, &entry_x
, &entry_y
);
4107 if (context
->is_rtl
)
4109 delta
= exit_x
+ context
->offsets
[prev_iter
.pos
].advanceOffset
;
4110 context
->advances
[prev_iter
.pos
] -= delta
;
4111 context
->advances
[context
->cur
] = entry_x
+ context
->offsets
[context
->cur
].advanceOffset
;
4112 context
->offsets
[prev_iter
.pos
].advanceOffset
-= delta
;
4116 delta
= entry_x
+ context
->offsets
[context
->cur
].advanceOffset
;
4117 context
->advances
[prev_iter
.pos
] = exit_x
+ context
->offsets
[prev_iter
.pos
].advanceOffset
;
4118 context
->advances
[context
->cur
] -= delta
;
4119 context
->offsets
[context
->cur
].advanceOffset
-= delta
;
4122 if (lookup
->flags
& LOOKUP_FLAG_RTL
)
4123 context
->offsets
[prev_iter
.pos
].ascenderOffset
= entry_y
- exit_y
;
4125 context
->offsets
[context
->cur
].ascenderOffset
= exit_y
- entry_y
;
4131 WARN("Unknown cursive attachment format %u.\n", format
);
4138 static BOOL
opentype_layout_apply_mark_array(struct scriptshaping_context
*context
, unsigned int subtable_offset
,
4139 unsigned int mark_array
, unsigned int mark_index
, unsigned int glyph_index
, unsigned int anchors_matrix
,
4140 unsigned int class_count
, unsigned int glyph_pos
)
4142 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4143 unsigned int mark_class
, mark_count
, glyph_count
;
4144 const struct ot_gpos_mark_record
*record
;
4145 float mark_x
, mark_y
, base_x
, base_y
;
4146 const UINT16
*anchors
;
4148 mark_count
= table_read_be_word(table
, subtable_offset
+ mark_array
);
4149 if (mark_index
>= mark_count
) return FALSE
;
4151 if (!(record
= table_read_ensure(table
, subtable_offset
+ mark_array
+
4152 FIELD_OFFSET(struct ot_gpos_mark_array
, records
[mark_index
]), sizeof(*record
))))
4157 mark_class
= GET_BE_WORD(record
->mark_class
);
4158 if (mark_class
>= class_count
) return FALSE
;
4160 glyph_count
= table_read_be_word(table
, subtable_offset
+ anchors_matrix
);
4161 if (glyph_index
>= glyph_count
) return FALSE
;
4163 /* Anchors data is stored as two dimensional array [glyph_count][class_count], starting with row count field. */
4164 anchors
= table_read_ensure(table
, subtable_offset
+ anchors_matrix
+ 2, glyph_count
* class_count
* sizeof(*anchors
));
4165 if (!anchors
) return FALSE
;
4167 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ mark_array
+ GET_BE_WORD(record
->mark_anchor
),
4168 context
->cur
, &mark_x
, &mark_y
);
4169 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ anchors_matrix
+
4170 GET_BE_WORD(anchors
[glyph_index
* class_count
+ mark_class
]), glyph_pos
, &base_x
, &base_y
);
4172 if (context
->is_rtl
)
4173 context
->offsets
[context
->cur
].advanceOffset
= mark_x
- base_x
;
4175 context
->offsets
[context
->cur
].advanceOffset
= -context
->advances
[glyph_pos
] + base_x
- mark_x
;
4177 context
->offsets
[context
->cur
].ascenderOffset
= base_y
- mark_y
;
4183 static BOOL
opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context
*context
,
4184 const struct lookup
*lookup
, unsigned int subtable_offset
)
4186 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4189 format
= table_read_be_word(table
, subtable_offset
);
4193 const struct ot_gpos_mark_to_base_format1
*format1
;
4194 unsigned int base_index
, mark_index
;
4195 struct glyph_iterator base_iter
;
4197 if (!(format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
)))) return FALSE
;
4199 mark_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->mark_coverage
),
4200 context
->u
.pos
.glyphs
[context
->cur
]);
4201 if (mark_index
== GLYPH_NOT_COVERED
) return FALSE
;
4203 /* Look back for first base glyph. */
4204 glyph_iterator_init(context
, LOOKUP_FLAG_IGNORE_MARKS
, context
->cur
, 1, &base_iter
);
4205 if (!glyph_iterator_prev(&base_iter
))
4208 base_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->base_coverage
),
4209 context
->u
.pos
.glyphs
[base_iter
.pos
]);
4210 if (base_index
== GLYPH_NOT_COVERED
) return FALSE
;
4212 return opentype_layout_apply_mark_array(context
, subtable_offset
, GET_BE_WORD(format1
->mark_array
), mark_index
,
4213 base_index
, GET_BE_WORD(format1
->base_array
), GET_BE_WORD(format1
->mark_class_count
), base_iter
.pos
);
4217 WARN("Unknown mark-to-base format %u.\n", format
);
4224 static const UINT16
* table_read_array_be_word(const struct dwrite_fonttable
*table
, unsigned int offset
,
4225 unsigned int index
, UINT16
*data
)
4227 unsigned int count
= table_read_be_word(table
, offset
);
4228 const UINT16
*array
;
4230 if (index
!= ~0u && index
>= count
) return NULL
;
4231 if (!(array
= table_read_ensure(table
, offset
+ 2, count
* sizeof(*array
)))) return FALSE
;
4232 *data
= index
== ~0u ? count
: GET_BE_WORD(array
[index
]);
4236 static BOOL
opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context
*context
,
4237 const struct lookup
*lookup
, unsigned int subtable_offset
)
4239 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4242 format
= table_read_be_word(table
, subtable_offset
);
4246 unsigned int mark_index
, lig_index
, comp_index
, class_count
, comp_count
;
4247 const struct ot_gpos_mark_to_lig_format1
*format1
;
4248 struct glyph_iterator lig_iter
;
4249 unsigned int lig_array
;
4252 if (!(format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
)))) return FALSE
;
4254 mark_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->mark_coverage
),
4255 context
->u
.pos
.glyphs
[context
->cur
]);
4256 if (mark_index
== GLYPH_NOT_COVERED
) return FALSE
;
4258 glyph_iterator_init(context
, LOOKUP_FLAG_IGNORE_MARKS
, context
->cur
, 1, &lig_iter
);
4259 if (!glyph_iterator_prev(&lig_iter
))
4262 lig_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->lig_coverage
),
4263 context
->u
.pos
.glyphs
[lig_iter
.pos
]);
4264 if (lig_index
== GLYPH_NOT_COVERED
) return FALSE
;
4266 class_count
= GET_BE_WORD(format1
->mark_class_count
);
4268 lig_array
= GET_BE_WORD(format1
->lig_array
);
4270 if (!table_read_array_be_word(table
, subtable_offset
+ lig_array
, lig_index
, &lig_attach
)) return FALSE
;
4272 comp_count
= table_read_be_word(table
, subtable_offset
+ lig_array
+ lig_attach
);
4273 if (!comp_count
) return FALSE
;
4275 comp_index
= context
->u
.buffer
.glyph_props
[lig_iter
.pos
].components
-
4276 context
->u
.buffer
.glyph_props
[context
->cur
].lig_component
- 1;
4277 if (comp_index
>= comp_count
) return FALSE
;
4279 return opentype_layout_apply_mark_array(context
, subtable_offset
, GET_BE_WORD(format1
->mark_array
), mark_index
,
4280 comp_index
, lig_array
+ lig_attach
, class_count
, lig_iter
.pos
);
4283 WARN("Unknown mark-to-ligature format %u.\n", format
);
4288 static BOOL
opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context
*context
,
4289 const struct lookup
*lookup
, unsigned int subtable_offset
)
4291 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4294 format
= table_read_be_word(table
, subtable_offset
);
4298 const struct ot_gpos_mark_to_mark_format1
*format1
;
4299 unsigned int mark1_index
, mark2_index
;
4300 struct glyph_iterator mark_iter
;
4302 if (!(format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
)))) return FALSE
;
4304 mark1_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->mark1_coverage
),
4305 context
->u
.pos
.glyphs
[context
->cur
]);
4306 if (mark1_index
== GLYPH_NOT_COVERED
) return FALSE
;
4308 glyph_iterator_init(context
, lookup
->flags
& ~LOOKUP_FLAG_IGNORE_MASK
, context
->cur
, 1, &mark_iter
);
4309 if (!glyph_iterator_prev(&mark_iter
))
4312 if (!context
->u
.pos
.glyph_props
[mark_iter
.pos
].isDiacritic
)
4315 mark2_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(format1
->mark2_coverage
),
4316 context
->u
.pos
.glyphs
[mark_iter
.pos
]);
4317 if (mark2_index
== GLYPH_NOT_COVERED
) return FALSE
;
4319 return opentype_layout_apply_mark_array(context
, subtable_offset
, GET_BE_WORD(format1
->mark1_array
), mark1_index
,
4320 mark2_index
, GET_BE_WORD(format1
->mark2_array
), GET_BE_WORD(format1
->mark_class_count
), mark_iter
.pos
);
4324 WARN("Unknown mark-to-mark format %u.\n", format
);
4331 static unsigned int opentype_layout_adjust_extension_subtable(struct scriptshaping_context
*context
,
4332 unsigned int *subtable_offset
)
4334 const struct ot_gsubgpos_extension_format1
*format1
;
4336 if (!(format1
= table_read_ensure(&context
->table
->table
, *subtable_offset
, sizeof(*format1
))))
4339 if (GET_BE_WORD(format1
->format
) != 1)
4341 WARN("Unexpected extension table format %#x.\n", format1
->format
);
4345 *subtable_offset
= *subtable_offset
+ GET_BE_DWORD(format1
->extension_offset
);
4347 return GET_BE_WORD(format1
->lookup_type
);
4350 static BOOL
opentype_layout_apply_context(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4351 unsigned int subtable_offset
);
4352 static BOOL
opentype_layout_apply_chain_context(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4353 unsigned int subtable_offset
);
4355 static BOOL
opentype_layout_apply_gpos_lookup(struct scriptshaping_context
*context
, const struct lookup
*lookup
)
4357 unsigned int i
, lookup_type
;
4360 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
4362 unsigned int subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, i
);
4364 if (lookup
->type
== GPOS_LOOKUP_EXTENSION_POSITION
)
4366 lookup_type
= opentype_layout_adjust_extension_subtable(context
, &subtable_offset
);
4371 lookup_type
= lookup
->type
;
4373 switch (lookup_type
)
4375 case GPOS_LOOKUP_SINGLE_ADJUSTMENT
:
4376 ret
= opentype_layout_apply_gpos_single_adjustment(context
, lookup
, subtable_offset
);
4378 case GPOS_LOOKUP_PAIR_ADJUSTMENT
:
4379 ret
= opentype_layout_apply_gpos_pair_adjustment(context
, lookup
, subtable_offset
);
4381 case GPOS_LOOKUP_CURSIVE_ATTACHMENT
:
4382 ret
= opentype_layout_apply_gpos_cursive_attachment(context
, lookup
, subtable_offset
);
4384 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT
:
4385 ret
= opentype_layout_apply_gpos_mark_to_base_attachment(context
, lookup
, subtable_offset
);
4387 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT
:
4388 ret
= opentype_layout_apply_gpos_mark_to_lig_attachment(context
, lookup
, subtable_offset
);
4390 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT
:
4391 ret
= opentype_layout_apply_gpos_mark_to_mark_attachment(context
, lookup
, subtable_offset
);
4393 case GPOS_LOOKUP_CONTEXTUAL_POSITION
:
4394 ret
= opentype_layout_apply_context(context
, lookup
, subtable_offset
);
4396 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION
:
4397 ret
= opentype_layout_apply_chain_context(context
, lookup
, subtable_offset
);
4399 case GPOS_LOOKUP_EXTENSION_POSITION
:
4400 WARN("Recursive extension lookup.\n");
4403 WARN("Unknown lookup type %u.\n", lookup_type
);
4415 struct lookup
*lookups
;
4420 static int lookups_sorting_compare(const void *a
, const void *b
)
4422 const struct lookup
*left
= (const struct lookup
*)a
;
4423 const struct lookup
*right
= (const struct lookup
*)b
;
4424 return left
->index
< right
->index
? -1 : left
->index
> right
->index
? 1 : 0;
4427 static BOOL
opentype_layout_init_lookup(const struct ot_gsubgpos_table
*table
, unsigned short lookup_index
, unsigned int mask
,
4428 struct lookup
*lookup
)
4430 unsigned short subtable_count
, lookup_type
, flags
, mark_filtering_set
;
4431 const struct ot_lookup_table
*lookup_table
;
4432 unsigned int offset
;
4434 if (!(offset
= table_read_be_word(&table
->table
, table
->lookup_list
+
4435 FIELD_OFFSET(struct ot_lookup_list
, lookup
[lookup_index
]))))
4440 offset
+= table
->lookup_list
;
4442 if (!(lookup_table
= table_read_ensure(&table
->table
, offset
, sizeof(*lookup_table
))))
4445 if (!(subtable_count
= GET_BE_WORD(lookup_table
->subtable_count
)))
4448 lookup_type
= GET_BE_WORD(lookup_table
->lookup_type
);
4449 flags
= GET_BE_WORD(lookup_table
->flags
);
4451 if (flags
& LOOKUP_FLAG_USE_MARK_FILTERING_SET
)
4453 mark_filtering_set
= table_read_be_word(&table
->table
, offset
+
4454 FIELD_OFFSET(struct ot_lookup_table
, subtable
[subtable_count
]));
4455 flags
|= mark_filtering_set
<< 16;
4458 lookup
->index
= lookup_index
;
4459 lookup
->type
= lookup_type
;
4460 lookup
->flags
= flags
;
4461 lookup
->subtable_count
= subtable_count
;
4462 lookup
->mask
= mask
;
4463 lookup
->offset
= offset
;
4468 static void opentype_layout_add_lookups(const struct ot_feature_list
*feature_list
, UINT16 total_lookup_count
,
4469 const struct ot_gsubgpos_table
*table
, struct shaping_feature
*feature
, struct lookups
*lookups
)
4471 UINT16 feature_offset
, lookup_count
;
4474 /* Feature wasn't found */
4475 if (feature
->index
== 0xffff)
4478 feature_offset
= GET_BE_WORD(feature_list
->features
[feature
->index
].offset
);
4480 lookup_count
= table_read_be_word(&table
->table
, table
->feature_list
+ feature_offset
+
4481 FIELD_OFFSET(struct ot_feature
, lookup_count
));
4485 if (!dwrite_array_reserve((void **)&lookups
->lookups
, &lookups
->capacity
, lookups
->count
+ lookup_count
,
4486 sizeof(*lookups
->lookups
)))
4491 for (i
= 0; i
< lookup_count
; ++i
)
4493 UINT16 lookup_index
= table_read_be_word(&table
->table
, table
->feature_list
+ feature_offset
+
4494 FIELD_OFFSET(struct ot_feature
, lookuplist_index
[i
]));
4496 if (lookup_index
>= total_lookup_count
)
4499 if (opentype_layout_init_lookup(table
, lookup_index
, feature
->mask
, &lookups
->lookups
[lookups
->count
]))
4504 static void opentype_layout_collect_lookups(struct scriptshaping_context
*context
, unsigned int script_index
,
4505 unsigned int language_index
, const struct shaping_features
*features
, const struct ot_gsubgpos_table
*table
,
4506 struct lookups
*lookups
)
4508 unsigned int last_num_lookups
= 0, stage
, script_feature_count
= 0;
4509 UINT16 total_feature_count
, total_lookup_count
;
4510 struct shaping_feature required_feature
= { 0 };
4511 const struct ot_feature_list
*feature_list
;
4512 const struct ot_langsys
*langsys
= NULL
;
4513 struct shaping_feature
*feature
;
4514 unsigned int i
, j
, next_bit
;
4515 unsigned int global_bit_shift
= 1;
4516 unsigned int global_bit_mask
= 2;
4517 UINT16 feature_index
;
4519 if (!table
->table
.data
)
4522 if (script_index
!= ~0u)
4524 unsigned int table_offset
, langsys_offset
;
4526 /* ScriptTable offset. */
4527 table_offset
= table_read_be_word(&table
->table
, table
->script_list
+ FIELD_OFFSET(struct ot_script_list
, scripts
) +
4528 script_index
* sizeof(struct ot_script_record
) + FIELD_OFFSET(struct ot_script_record
, script
));
4532 if (language_index
== ~0u)
4533 langsys_offset
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
);
4535 langsys_offset
= table_read_be_word(&table
->table
, table
->script_list
+ table_offset
+
4536 FIELD_OFFSET(struct ot_script
, langsys
) + language_index
* sizeof(struct ot_langsys_record
) +
4537 FIELD_OFFSET(struct ot_langsys_record
, langsys
));
4538 langsys_offset
+= table
->script_list
+ table_offset
;
4540 script_feature_count
= table_read_be_word(&table
->table
, langsys_offset
+ FIELD_OFFSET(struct ot_langsys
, feature_count
));
4541 if (script_feature_count
)
4542 langsys
= table_read_ensure(&table
->table
, langsys_offset
,
4543 FIELD_OFFSET(struct ot_langsys
, feature_index
[script_feature_count
]));
4545 script_feature_count
= 0;
4548 total_feature_count
= table_read_be_word(&table
->table
, table
->feature_list
);
4549 if (!total_feature_count
)
4552 total_lookup_count
= table_read_be_word(&table
->table
, table
->lookup_list
);
4553 if (!total_lookup_count
)
4556 feature_list
= table_read_ensure(&table
->table
, table
->feature_list
,
4557 FIELD_OFFSET(struct ot_feature_list
, features
[total_feature_count
]));
4561 /* Required feature. */
4562 required_feature
.index
= langsys
? GET_BE_WORD(langsys
->required_feature_index
) : 0xffff;
4563 if (required_feature
.index
< total_feature_count
)
4564 required_feature
.tag
= feature_list
->features
[required_feature
.index
].tag
;
4565 required_feature
.mask
= global_bit_mask
;
4567 context
->global_mask
= global_bit_mask
;
4568 next_bit
= global_bit_shift
+ 1;
4569 for (i
= 0; i
< features
->count
; ++i
)
4571 unsigned int bits_needed
;
4574 feature
= &features
->features
[i
];
4576 feature
->index
= 0xffff;
4578 if ((feature
->flags
& FEATURE_GLOBAL
) && feature
->max_value
== 1)
4581 BitScanReverse(&bits_needed
, min(feature
->max_value
, 256));
4583 if (!feature
->max_value
|| next_bit
+ bits_needed
> 8 * sizeof (feature
->mask
))
4586 if (required_feature
.tag
== feature
->tag
)
4587 required_feature
.stage
= feature
->stage
;
4589 for (j
= 0; j
< script_feature_count
; ++j
)
4591 feature_index
= GET_BE_WORD(langsys
->feature_index
[j
]);
4592 if (feature_index
>= total_feature_count
)
4594 if ((found
= feature_list
->features
[feature_index
].tag
== feature
->tag
))
4596 feature
->index
= feature_index
;
4601 if (!found
&& (features
->features
[i
].flags
& FEATURE_GLOBAL_SEARCH
))
4603 for (j
= 0; j
< total_feature_count
; ++j
)
4605 if ((found
= (feature_list
->features
[j
].tag
== feature
->tag
)))
4616 if (feature
->flags
& FEATURE_GLOBAL
&& feature
->max_value
== 1)
4618 feature
->shift
= global_bit_shift
;
4619 feature
->mask
= global_bit_mask
;
4623 feature
->shift
= next_bit
;
4624 feature
->mask
= (1 << (next_bit
+ bits_needed
)) - (1 << next_bit
);
4625 next_bit
+= bits_needed
;
4626 context
->global_mask
|= (feature
->default_value
<< feature
->shift
) & feature
->mask
;
4630 for (stage
= 0; stage
<= features
->stage
; ++stage
)
4632 if (required_feature
.index
!= 0xffff && required_feature
.stage
== stage
)
4633 opentype_layout_add_lookups(feature_list
, total_lookup_count
, table
, &required_feature
, lookups
);
4635 for (i
= 0; i
< features
->count
; ++i
)
4637 if (features
->features
[i
].stage
== stage
)
4638 opentype_layout_add_lookups(feature_list
, total_lookup_count
, table
, &features
->features
[i
], lookups
);
4641 /* Sort and merge lookups for current stage. */
4642 if (last_num_lookups
< lookups
->count
)
4644 qsort(lookups
->lookups
+ last_num_lookups
, lookups
->count
- last_num_lookups
, sizeof(*lookups
->lookups
),
4645 lookups_sorting_compare
);
4647 j
= last_num_lookups
;
4648 for (i
= j
+ 1; i
< lookups
->count
; ++i
)
4650 if (lookups
->lookups
[i
].index
!= lookups
->lookups
[j
].index
)
4652 lookups
->lookups
[++j
] = lookups
->lookups
[i
];
4656 lookups
->lookups
[j
].mask
|= lookups
->lookups
[i
].mask
;
4659 lookups
->count
= j
+ 1;
4662 last_num_lookups
= lookups
->count
;
4666 static int feature_search_compare(const void *a
, const void* b
)
4668 unsigned int tag
= *(unsigned int *)a
;
4669 const struct shaping_feature
*feature
= b
;
4671 return tag
< feature
->tag
? -1 : tag
> feature
->tag
? 1 : 0;
4674 static unsigned int shaping_features_get_mask(const struct shaping_features
*features
, unsigned int tag
, unsigned int *shift
)
4676 struct shaping_feature
*feature
;
4678 feature
= bsearch(&tag
, features
->features
, features
->count
, sizeof(*features
->features
), feature_search_compare
);
4680 if (!feature
|| feature
->index
== 0xffff)
4683 if (shift
) *shift
= feature
->shift
;
4684 return feature
->mask
;
4687 static void opentype_layout_get_glyph_range_for_text(struct scriptshaping_context
*context
, unsigned int start_char
,
4688 unsigned int end_char
, unsigned int *start_glyph
, unsigned int *end_glyph
)
4690 *start_glyph
= context
->u
.buffer
.clustermap
[start_char
];
4691 if (end_char
>= context
->length
- 1)
4692 *end_glyph
= context
->glyph_count
- 1;
4694 *end_glyph
= context
->u
.buffer
.clustermap
[end_char
+ 1] - 1;
4697 static void opentype_layout_set_glyph_masks(struct scriptshaping_context
*context
, const struct shaping_features
*features
)
4699 const DWRITE_TYPOGRAPHIC_FEATURES
**user_features
= context
->user_features
.features
;
4700 unsigned int f
, r
, g
, start_char
, mask
, shift
, value
;
4702 for (g
= 0; g
< context
->glyph_count
; ++g
)
4703 context
->glyph_infos
[g
].mask
= context
->global_mask
;
4705 /* FIXME: set shaper masks */
4707 for (r
= 0, start_char
= 0; r
< context
->user_features
.range_count
; ++r
)
4709 unsigned int start_glyph
, end_glyph
;
4711 if (start_char
>= context
->length
)
4714 opentype_layout_get_glyph_range_for_text(context
, start_char
, start_char
+ context
->user_features
.range_lengths
[r
],
4715 &start_glyph
, &end_glyph
);
4716 start_char
+= context
->user_features
.range_lengths
[r
];
4718 if (start_glyph
> end_glyph
|| end_glyph
>= context
->glyph_count
)
4721 for (f
= 0; f
< user_features
[r
]->featureCount
; ++f
)
4723 mask
= shaping_features_get_mask(features
, user_features
[r
]->features
[f
].nameTag
, &shift
);
4727 value
= (user_features
[r
]->features
[f
].parameter
<< shift
) & mask
;
4729 for (g
= start_glyph
; g
<= end_glyph
; ++g
)
4730 context
->glyph_infos
[g
].mask
= (context
->glyph_infos
[g
].mask
& ~mask
) | value
;
4735 static void opentype_layout_apply_gpos_context_lookup(struct scriptshaping_context
*context
, unsigned int lookup_index
)
4737 struct lookup lookup
= { 0 };
4738 /* Feature mask is intentionally zero, it's not used outside of main loop. */
4739 if (opentype_layout_init_lookup(context
->table
, lookup_index
, 0, &lookup
))
4740 opentype_layout_apply_gpos_lookup(context
, &lookup
);
4743 void opentype_layout_apply_gpos_features(struct scriptshaping_context
*context
, unsigned int script_index
,
4744 unsigned int language_index
, const struct shaping_features
*features
)
4746 struct lookups lookups
= { 0 };
4750 context
->nesting_level_left
= SHAPE_MAX_NESTING_LEVEL
;
4751 context
->u
.buffer
.apply_context_lookup
= opentype_layout_apply_gpos_context_lookup
;
4752 opentype_layout_collect_lookups(context
, script_index
, language_index
, features
, &context
->cache
->gpos
, &lookups
);
4754 for (i
= 0; i
< context
->glyph_count
; ++i
)
4755 opentype_set_glyph_props(context
, i
);
4756 opentype_layout_set_glyph_masks(context
, features
);
4758 for (i
= 0; i
< lookups
.count
; ++i
)
4760 const struct lookup
*lookup
= &lookups
.lookups
[i
];
4763 context
->lookup_mask
= lookup
->mask
;
4765 while (context
->cur
< context
->glyph_count
)
4769 if ((context
->glyph_infos
[context
->cur
].mask
& lookup
->mask
) &&
4770 lookup_is_glyph_match(context
, context
->cur
, lookup
->flags
))
4772 ret
= opentype_layout_apply_gpos_lookup(context
, lookup
);
4780 heap_free(lookups
.lookups
);
4783 static void opentype_layout_replace_glyph(struct scriptshaping_context
*context
, UINT16 glyph
)
4785 UINT16 orig_glyph
= context
->u
.subst
.glyphs
[context
->cur
];
4786 if (glyph
!= orig_glyph
)
4788 context
->u
.subst
.glyphs
[context
->cur
] = glyph
;
4789 opentype_set_subst_glyph_props(context
, context
->cur
);
4793 static BOOL
opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4794 unsigned int subtable_offset
)
4796 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4797 UINT16 format
, coverage
, orig_glyph
, glyph
;
4798 unsigned int coverage_index
;
4800 orig_glyph
= glyph
= context
->u
.subst
.glyphs
[context
->cur
];
4802 format
= table_read_be_word(table
, subtable_offset
);
4804 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_singlesubst_format1
, coverage
));
4808 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
4809 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
4811 glyph
= orig_glyph
+ table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_singlesubst_format1
, delta
));
4813 else if (format
== 2)
4815 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
4816 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
4818 if (!table_read_array_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_singlesubst_format2
, count
),
4819 coverage_index
, &glyph
))
4826 WARN("Unknown single substitution format %u.\n", format
);
4830 opentype_layout_replace_glyph(context
, glyph
);
4836 static BOOL
opentype_layout_gsub_ensure_buffer(struct scriptshaping_context
*context
, unsigned int count
)
4838 DWRITE_SHAPING_GLYPH_PROPERTIES
*glyph_props
;
4839 struct shaping_glyph_info
*glyph_infos
;
4840 unsigned int new_capacity
;
4844 if (context
->u
.subst
.capacity
>= count
)
4847 new_capacity
= context
->u
.subst
.capacity
* 2;
4849 if ((glyphs
= heap_realloc(context
->u
.subst
.glyphs
, new_capacity
* sizeof(*glyphs
))))
4850 context
->u
.subst
.glyphs
= glyphs
;
4851 if ((glyph_props
= heap_realloc(context
->u
.subst
.glyph_props
, new_capacity
* sizeof(*glyph_props
))))
4852 context
->u
.subst
.glyph_props
= glyph_props
;
4853 if ((glyph_infos
= heap_realloc(context
->glyph_infos
, new_capacity
* sizeof(*glyph_infos
))))
4854 context
->glyph_infos
= glyph_infos
;
4856 if ((ret
= (glyphs
&& glyph_props
&& glyph_infos
)))
4857 context
->u
.subst
.capacity
= new_capacity
;
4862 static BOOL
opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4863 unsigned int subtable_offset
)
4865 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4866 UINT16 format
, coverage
, glyph
, glyph_count
;
4867 unsigned int i
, idx
, coverage_index
;
4868 const UINT16
*glyphs
;
4871 glyph
= context
->u
.subst
.glyphs
[idx
];
4873 format
= table_read_be_word(table
, subtable_offset
);
4875 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_multsubst_format1
, coverage
));
4881 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
4882 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
4884 if (!table_read_array_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_multsubst_format1
, seq_count
),
4885 coverage_index
, &seq_offset
))
4890 if (!(glyphs
= table_read_array_be_word(table
, subtable_offset
+ seq_offset
, ~0u, &glyph_count
))) return FALSE
;
4892 if (glyph_count
== 1)
4894 /* Equivalent of single substitution. */
4895 opentype_layout_replace_glyph(context
, GET_BE_WORD(glyphs
[0]));
4898 else if (glyph_count
== 0)
4904 unsigned int shift_len
, src_idx
, dest_idx
, mask
;
4906 /* Current glyph is also replaced. */
4909 if (!(opentype_layout_gsub_ensure_buffer(context
, context
->glyph_count
+ glyph_count
)))
4912 shift_len
= context
->cur
+ 1 < context
->glyph_count
? context
->glyph_count
- context
->cur
- 1 : 0;
4916 src_idx
= context
->cur
+ 1;
4917 dest_idx
= src_idx
+ glyph_count
;
4919 memmove(&context
->u
.subst
.glyphs
[dest_idx
], &context
->u
.subst
.glyphs
[src_idx
],
4920 shift_len
* sizeof(*context
->u
.subst
.glyphs
));
4921 memmove(&context
->u
.subst
.glyph_props
[dest_idx
], &context
->u
.subst
.glyph_props
[src_idx
],
4922 shift_len
* sizeof(*context
->u
.subst
.glyph_props
));
4923 memmove(&context
->glyph_infos
[dest_idx
], &context
->glyph_infos
[src_idx
],
4924 shift_len
* sizeof(*context
->glyph_infos
));
4927 mask
= context
->glyph_infos
[context
->cur
].mask
;
4928 for (i
= 0, idx
= context
->cur
; i
<= glyph_count
; ++i
)
4930 glyph
= GET_BE_WORD(glyphs
[i
]);
4931 context
->u
.subst
.glyphs
[idx
+ i
] = glyph
;
4934 context
->u
.subst
.glyph_props
[idx
+ i
].isClusterStart
= 0;
4935 context
->u
.buffer
.glyph_props
[idx
+ i
].components
= 0;
4936 context
->glyph_infos
[idx
+ i
].start_text_idx
= 0;
4938 opentype_set_subst_glyph_props(context
, idx
+ i
);
4939 /* Inherit feature mask from original matched glyph. */
4940 context
->glyph_infos
[idx
+ i
].mask
= mask
;
4943 context
->cur
+= glyph_count
+ 1;
4944 context
->glyph_count
+= glyph_count
;
4949 WARN("Unknown multiple substitution format %u.\n", format
);
4956 static BOOL
opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
4957 unsigned int subtable_offset
)
4959 const struct dwrite_fonttable
*table
= &context
->table
->table
;
4960 unsigned int idx
, coverage_index
;
4961 UINT16 format
, coverage
, glyph
;
4964 glyph
= context
->u
.subst
.glyphs
[idx
];
4966 format
= table_read_be_word(table
, subtable_offset
);
4970 const struct ot_gsub_altsubst_format1
*format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
));
4971 unsigned int shift
, alt_index
;
4974 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_altsubst_format1
, coverage
));
4976 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
4977 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
4979 if (!table_read_array_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_altsubst_format1
, count
),
4980 coverage_index
, &set_offset
))
4983 /* Argument is 1-based. */
4984 BitScanForward(&shift
, context
->lookup_mask
);
4985 alt_index
= (context
->lookup_mask
& context
->glyph_infos
[idx
].mask
) >> shift
;
4986 if (!alt_index
) return FALSE
;
4988 if (!table_read_array_be_word(table
, subtable_offset
+ set_offset
, alt_index
- 1, &glyph
)) return FALSE
;
4992 WARN("Unexpected alternate substitution format %d.\n", format
);
4996 opentype_layout_replace_glyph(context
, glyph
);
5002 static BOOL
opentype_layout_context_match_input(const struct match_context
*mc
, unsigned int count
, const UINT16
*input
,
5003 unsigned int *end_offset
, unsigned int *match_positions
)
5005 struct match_data match_data
= { .mc
= mc
, .subtable_offset
= mc
->input_offset
};
5006 struct scriptshaping_context
*context
= mc
->context
;
5007 struct glyph_iterator iter
;
5010 if (count
> GLYPH_CONTEXT_MAX_LENGTH
)
5013 match_positions
[0] = context
->cur
;
5015 glyph_iterator_init(context
, mc
->lookup
->flags
, context
->cur
, count
- 1, &iter
);
5016 iter
.mask
= context
->lookup_mask
;
5017 iter
.match_func
= mc
->match_func
;
5018 iter
.match_data
= &match_data
;
5019 iter
.glyph_data
= input
;
5021 for (i
= 1; i
< count
; ++i
)
5023 if (!glyph_iterator_next(&iter
))
5026 match_positions
[i
] = iter
.pos
;
5029 *end_offset
= iter
.pos
- context
->cur
+ 1;
5034 static void opentype_layout_unsafe_to_break(struct scriptshaping_context
*context
, unsigned int idx
)
5036 if (context
->u
.buffer
.glyph_props
[idx
].isClusterStart
)
5037 context
->u
.buffer
.text_props
[context
->glyph_infos
[idx
].start_text_idx
].canBreakShapingAfter
= 0;
5040 static void opentype_layout_delete_glyph(struct scriptshaping_context
*context
, unsigned int idx
)
5042 unsigned int shift_len
;
5044 shift_len
= context
->glyph_count
- context
->cur
- 1;
5048 memmove(&context
->u
.buffer
.glyphs
[idx
], &context
->u
.buffer
.glyphs
[idx
+ 1],
5049 shift_len
* sizeof(*context
->u
.buffer
.glyphs
));
5050 memmove(&context
->u
.buffer
.glyph_props
[idx
], &context
->u
.buffer
.glyph_props
[idx
+ 1],
5051 shift_len
* sizeof(*context
->u
.buffer
.glyph_props
));
5052 memmove(&context
->glyph_infos
[idx
], &context
->glyph_infos
[idx
+ 1], shift_len
* sizeof(*context
->glyph_infos
));
5055 context
->glyph_count
--;
5058 static BOOL
opentype_layout_apply_ligature(struct scriptshaping_context
*context
, unsigned int offset
,
5059 const struct lookup
*lookup
)
5061 struct match_context mc
= { .context
= context
, .lookup
= lookup
, .match_func
= opentype_match_glyph_func
};
5062 const struct dwrite_fonttable
*gsub
= &context
->table
->table
;
5063 unsigned int match_positions
[GLYPH_CONTEXT_MAX_LENGTH
];
5064 unsigned int i
, j
, comp_count
, match_length
= 0;
5065 const struct ot_gsub_lig
*lig
;
5068 comp_count
= table_read_be_word(gsub
, offset
+ FIELD_OFFSET(struct ot_gsub_lig
, comp_count
));
5073 lig
= table_read_ensure(gsub
, offset
, FIELD_OFFSET(struct ot_gsub_lig
, components
[comp_count
-1]));
5077 lig_glyph
= GET_BE_WORD(lig
->lig_glyph
);
5079 if (comp_count
== 1)
5081 opentype_layout_replace_glyph(context
, lig_glyph
);
5086 if (!opentype_layout_context_match_input(&mc
, comp_count
, lig
->components
, &match_length
, match_positions
))
5089 opentype_layout_replace_glyph(context
, lig_glyph
);
5090 context
->u
.buffer
.glyph_props
[context
->cur
].components
= comp_count
;
5092 /* Positioning against a ligature implies keeping track of ligature component
5093 glyph should be attached to. Update per-glyph property for interleaving glyphs,
5094 0 means attaching to last component, n - attaching to n-th glyph before last. */
5095 for (i
= 1; i
< comp_count
; ++i
)
5097 j
= match_positions
[i
- 1] + 1;
5098 while (j
< match_positions
[i
])
5100 context
->u
.buffer
.glyph_props
[j
++].lig_component
= comp_count
- i
;
5102 opentype_layout_unsafe_to_break(context
, i
);
5103 context
->u
.buffer
.glyph_props
[i
].isClusterStart
= 0;
5104 context
->glyph_infos
[i
].start_text_idx
= 0;
5107 /* Delete ligated glyphs, backwards to preserve index. */
5108 for (i
= 1; i
< comp_count
; ++i
)
5110 opentype_layout_delete_glyph(context
, match_positions
[comp_count
- i
]);
5113 /* Skip whole matched sequence, accounting for deleted glyphs. */
5114 context
->cur
+= match_length
- (comp_count
- 1);
5119 static BOOL
opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
5120 unsigned int subtable_offset
)
5122 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5123 UINT16 format
, coverage
, glyph
, lig_set_offset
;
5124 unsigned int coverage_index
;
5126 glyph
= context
->u
.subst
.glyphs
[context
->cur
];
5128 format
= table_read_be_word(table
, subtable_offset
);
5132 const struct ot_gsub_ligsubst_format1
*format1
= table_read_ensure(table
, subtable_offset
, sizeof(*format1
));
5134 const UINT16
*offsets
;
5137 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_ligsubst_format1
, coverage
));
5139 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5140 if (coverage_index
== GLYPH_NOT_COVERED
) return FALSE
;
5142 if (!table_read_array_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsub_ligsubst_format1
, lig_set_count
),
5143 coverage_index
, &lig_set_offset
))
5146 if (!(offsets
= table_read_array_be_word(table
, subtable_offset
+ lig_set_offset
, ~0u, &lig_count
)))
5149 /* First applicable ligature is used. */
5150 for (i
= 0; i
< lig_count
; ++i
)
5152 if (opentype_layout_apply_ligature(context
, subtable_offset
+ lig_set_offset
+ GET_BE_WORD(offsets
[i
]), lookup
))
5157 WARN("Unexpected ligature substitution format %d.\n", format
);
5162 static BOOL
opentype_layout_context_match_backtrack(const struct match_context
*mc
, unsigned int count
,
5163 const UINT16
*backtrack
, unsigned int *match_start
)
5165 struct match_data match_data
= { .mc
= mc
, .subtable_offset
= mc
->backtrack_offset
};
5166 struct scriptshaping_context
*context
= mc
->context
;
5167 struct glyph_iterator iter
;
5170 glyph_iterator_init(context
, mc
->lookup
->flags
, context
->cur
, count
, &iter
);
5171 iter
.match_func
= mc
->match_func
;
5172 iter
.match_data
= &match_data
;
5173 iter
.glyph_data
= backtrack
;
5175 for (i
= 0; i
< count
; ++i
)
5177 if (!glyph_iterator_prev(&iter
))
5181 *match_start
= iter
.pos
;
5186 static BOOL
opentype_layout_context_match_lookahead(const struct match_context
*mc
, unsigned int count
,
5187 const UINT16
*lookahead
, unsigned int offset
, unsigned int *end_index
)
5189 struct match_data match_data
= { .mc
= mc
, .subtable_offset
= mc
->lookahead_offset
};
5190 struct scriptshaping_context
*context
= mc
->context
;
5191 struct glyph_iterator iter
;
5194 glyph_iterator_init(context
, mc
->lookup
->flags
, context
->cur
+ offset
- 1, count
, &iter
);
5195 iter
.match_func
= mc
->match_func
;
5196 iter
.match_data
= &match_data
;
5197 iter
.glyph_data
= lookahead
;
5199 for (i
= 0; i
< count
; ++i
)
5201 if (!glyph_iterator_next(&iter
))
5205 *end_index
= iter
.pos
;
5210 static BOOL
opentype_layout_context_apply_lookup(struct scriptshaping_context
*context
, unsigned int count
,
5211 unsigned int *match_positions
, unsigned int lookup_count
, const UINT16
*lookup_records
, unsigned int match_length
)
5216 if (!context
->nesting_level_left
)
5219 end
= context
->cur
+ match_length
;
5221 for (i
= 0; i
< lookup_count
; ++i
)
5223 unsigned int idx
= GET_BE_WORD(lookup_records
[i
]);
5224 unsigned int orig_len
, lookup_index
, next
;
5229 context
->cur
= match_positions
[idx
];
5231 orig_len
= context
->glyph_count
;
5233 lookup_index
= GET_BE_WORD(lookup_records
[i
+1]);
5235 --context
->nesting_level_left
;
5236 context
->u
.buffer
.apply_context_lookup(context
, lookup_index
);
5237 ++context
->nesting_level_left
;
5239 delta
= context
->glyph_count
- orig_len
;
5244 if (end
<= (int)match_positions
[idx
])
5246 end
= match_positions
[idx
];
5254 if (delta
+ count
> GLYPH_CONTEXT_MAX_LENGTH
)
5259 delta
= max(delta
, (int)next
- (int)count
);
5263 memmove(match_positions
+ next
+ delta
, match_positions
+ next
,
5264 (count
- next
) * sizeof (*match_positions
));
5268 for (j
= idx
+ 1; j
< next
; j
++)
5269 match_positions
[j
] = match_positions
[j
- 1] + 1;
5271 for (; next
< count
; next
++)
5272 match_positions
[next
] += delta
;
5280 static BOOL
opentype_layout_apply_chain_context_match(unsigned int backtrack_count
, const UINT16
*backtrack
,
5281 unsigned int input_count
, const UINT16
*input
, unsigned int lookahead_count
, const UINT16
*lookahead
,
5282 unsigned int lookup_count
, const UINT16
*lookup_records
, const struct match_context
*mc
)
5284 unsigned int start_index
= 0, match_length
= 0, end_index
= 0;
5285 unsigned int match_positions
[GLYPH_CONTEXT_MAX_LENGTH
];
5287 return opentype_layout_context_match_input(mc
, input_count
, input
, &match_length
, match_positions
) &&
5288 opentype_layout_context_match_backtrack(mc
, backtrack_count
, backtrack
, &start_index
) &&
5289 opentype_layout_context_match_lookahead(mc
, lookahead_count
, lookahead
, input_count
, &end_index
) &&
5290 opentype_layout_context_apply_lookup(mc
->context
, input_count
, match_positions
, lookup_count
, lookup_records
, match_length
);
5293 static BOOL
opentype_layout_apply_chain_rule_set(const struct match_context
*mc
, unsigned int offset
)
5295 unsigned int backtrack_count
, input_count
, lookahead_count
, lookup_count
;
5296 const struct dwrite_fonttable
*table
= &mc
->context
->table
->table
;
5297 const UINT16
*backtrack
, *lookahead
, *input
, *lookup_records
;
5298 const struct ot_gsubgpos_ruleset
*ruleset
;
5299 unsigned int i
, count
;
5301 count
= table_read_be_word(table
, offset
);
5302 ruleset
= table_read_ensure(table
, offset
, count
* sizeof(ruleset
->offsets
));
5304 for (i
= 0; i
< count
; ++i
)
5306 unsigned int rule_offset
= offset
+ GET_BE_WORD(ruleset
->offsets
[i
]);
5308 backtrack_count
= table_read_be_word(table
, rule_offset
);
5310 backtrack
= table_read_ensure(table
, rule_offset
, backtrack_count
* sizeof(*backtrack
));
5311 rule_offset
+= backtrack_count
* sizeof(*backtrack
);
5313 if (!(input_count
= table_read_be_word(table
, rule_offset
)))
5317 input
= table_read_ensure(table
, rule_offset
, (input_count
- 1) * sizeof(*input
));
5318 rule_offset
+= (input_count
- 1) * sizeof(*input
);
5320 lookahead_count
= table_read_be_word(table
, rule_offset
);
5322 lookahead
= table_read_ensure(table
, rule_offset
, lookahead_count
* sizeof(*lookahead
));
5323 rule_offset
+= lookahead_count
* sizeof(*lookahead
);
5325 lookup_count
= table_read_be_word(table
, rule_offset
);
5327 lookup_records
= table_read_ensure(table
, rule_offset
, lookup_count
* 2 * sizeof(*lookup_records
));
5329 /* First applicable rule is used. */
5330 if (opentype_layout_apply_chain_context_match(backtrack_count
, backtrack
, input_count
, input
, lookahead_count
,
5331 lookahead
, lookup_count
, lookup_records
, mc
))
5340 static BOOL
opentype_layout_apply_context_match(unsigned int input_count
, const UINT16
*input
, unsigned int lookup_count
,
5341 const UINT16
*lookup_records
, const struct match_context
*mc
)
5343 unsigned int match_positions
[GLYPH_CONTEXT_MAX_LENGTH
];
5344 unsigned int match_length
= 0;
5346 return opentype_layout_context_match_input(mc
, input_count
, input
, &match_length
, match_positions
) &&
5347 opentype_layout_context_apply_lookup(mc
->context
, input_count
, match_positions
, lookup_count
,
5348 lookup_records
, match_length
);
5351 static BOOL
opentype_layout_apply_rule_set(const struct match_context
*mc
, unsigned int offset
)
5353 unsigned int input_count
, lookup_count
;
5354 const struct dwrite_fonttable
*table
= &mc
->context
->table
->table
;
5355 const UINT16
*input
, *lookup_records
;
5356 const struct ot_gsubgpos_ruleset
*ruleset
;
5357 unsigned int i
, count
;
5359 count
= table_read_be_word(table
, offset
);
5360 ruleset
= table_read_ensure(table
, offset
, count
* sizeof(ruleset
->offsets
));
5362 for (i
= 0; i
< count
; ++i
)
5364 unsigned int rule_offset
= offset
+ GET_BE_WORD(ruleset
->offsets
[i
]);
5366 if (!(input_count
= table_read_be_word(table
, rule_offset
)))
5370 if (!(lookup_count
= table_read_be_word(table
, rule_offset
)))
5374 if (!(input
= table_read_ensure(table
, rule_offset
, (input_count
- 1) * sizeof(*input
))))
5376 rule_offset
+= (input_count
- 1) * sizeof(*input
);
5378 if (!(lookup_records
= table_read_ensure(table
, rule_offset
, lookup_count
* 2 * sizeof(*lookup_records
))))
5381 /* First applicable rule is used. */
5382 if (opentype_layout_apply_context_match(input_count
, input
, lookup_count
, lookup_records
, mc
))
5389 static BOOL
opentype_layout_apply_context(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
5390 unsigned int subtable_offset
)
5392 struct match_context mc
= { .context
= context
, .lookup
= lookup
};
5393 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5394 unsigned int coverage_index
= GLYPH_NOT_COVERED
, count
, offset
;
5395 UINT16 glyph
, format
, coverage
;
5398 glyph
= context
->u
.subst
.glyphs
[context
->cur
];
5400 format
= table_read_be_word(table
, subtable_offset
);
5404 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
, coverage
));
5406 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5407 if (coverage_index
== GLYPH_NOT_COVERED
)
5410 count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
, ruleset_count
));
5411 if (coverage_index
>= count
)
5414 offset
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
,
5415 rulesets
[coverage_index
]));
5416 offset
+= subtable_offset
;
5418 mc
.match_func
= opentype_match_glyph_func
;
5420 ret
= opentype_layout_apply_rule_set(&mc
, offset
);
5422 else if (format
== 2)
5424 unsigned int input_classdef
, rule_set_idx
;
5426 offset
= subtable_offset
+ 2 /* format */;
5428 coverage
= table_read_be_word(table
, offset
);
5431 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5432 if (coverage_index
== GLYPH_NOT_COVERED
)
5435 input_classdef
= table_read_be_word(table
, offset
) + subtable_offset
;
5438 count
= table_read_be_word(table
, offset
);
5441 rule_set_idx
= opentype_layout_get_glyph_class(table
, input_classdef
, glyph
);
5442 if (rule_set_idx
>= count
)
5445 offset
= table_read_be_word(table
, offset
+ rule_set_idx
* 2);
5446 offset
+= subtable_offset
;
5448 mc
.input_offset
= input_classdef
;
5449 mc
.match_func
= opentype_match_class_func
;
5451 ret
= opentype_layout_apply_rule_set(&mc
, offset
);
5453 else if (format
== 3)
5455 unsigned int input_count
, lookup_count
;
5456 const UINT16
*input
, *lookup_records
;
5458 offset
= subtable_offset
+ 2 /* format */;
5460 input_count
= table_read_be_word(table
, offset
);
5466 lookup_count
= table_read_be_word(table
, offset
);
5469 if (!(input
= table_read_ensure(table
, offset
, sizeof(*input
) * input_count
)))
5471 offset
+= sizeof(*input
) * input_count
;
5473 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(input
[0]), glyph
);
5474 if (coverage_index
== GLYPH_NOT_COVERED
)
5477 lookup_records
= table_read_ensure(table
, offset
, lookup_count
* 2 * sizeof(*lookup_records
));
5479 mc
.input_offset
= subtable_offset
;
5480 mc
.match_func
= opentype_match_coverage_func
;
5482 ret
= opentype_layout_apply_context_match(input_count
, input
+ 1, lookup_count
, lookup_records
, &mc
);
5485 WARN("Unknown contextual substitution format %u.\n", format
);
5490 static BOOL
opentype_layout_apply_chain_context(struct scriptshaping_context
*context
, const struct lookup
*lookup
,
5491 unsigned int subtable_offset
)
5493 struct match_context mc
= { .context
= context
, .lookup
= lookup
};
5494 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5495 unsigned int coverage_index
= GLYPH_NOT_COVERED
, count
, offset
;
5496 UINT16 glyph
, format
, coverage
;
5499 glyph
= context
->u
.subst
.glyphs
[context
->cur
];
5501 format
= table_read_be_word(table
, subtable_offset
);
5505 coverage
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
, coverage
));
5507 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5508 if (coverage_index
== GLYPH_NOT_COVERED
)
5511 count
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
, ruleset_count
));
5512 if (coverage_index
>= count
)
5515 offset
= table_read_be_word(table
, subtable_offset
+ FIELD_OFFSET(struct ot_gsubgpos_context_format1
,
5516 rulesets
[coverage_index
]));
5517 offset
+= subtable_offset
;
5519 mc
.match_func
= opentype_match_glyph_func
;
5521 ret
= opentype_layout_apply_chain_rule_set(&mc
, offset
);
5523 else if (format
== 2)
5525 unsigned int backtrack_classdef
, input_classdef
, lookahead_classdef
, rule_set_idx
;
5527 offset
= subtable_offset
+ 2 /* format */;
5529 coverage
= table_read_be_word(table
, offset
);
5532 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5533 if (coverage_index
== GLYPH_NOT_COVERED
)
5536 backtrack_classdef
= table_read_be_word(table
, offset
) + subtable_offset
;
5539 input_classdef
= table_read_be_word(table
, offset
) + subtable_offset
;
5542 lookahead_classdef
= table_read_be_word(table
, offset
) + subtable_offset
;
5545 count
= table_read_be_word(table
, offset
);
5548 rule_set_idx
= opentype_layout_get_glyph_class(table
, input_classdef
, glyph
);
5549 if (rule_set_idx
>= count
)
5552 offset
= table_read_be_word(table
, offset
+ rule_set_idx
* 2);
5553 offset
+= subtable_offset
;
5555 mc
.backtrack_offset
= backtrack_classdef
;
5556 mc
.input_offset
= input_classdef
;
5557 mc
.lookahead_offset
= lookahead_classdef
;
5558 mc
.match_func
= opentype_match_class_func
;
5560 ret
= opentype_layout_apply_chain_rule_set(&mc
, offset
);
5562 else if (format
== 3)
5564 unsigned int backtrack_count
, input_count
, lookahead_count
, lookup_count
;
5565 const UINT16
*backtrack
, *lookahead
, *input
, *lookup_records
;
5567 offset
= subtable_offset
+ 2 /* format */;
5569 backtrack_count
= table_read_be_word(table
, offset
);
5571 backtrack
= table_read_ensure(table
, offset
, backtrack_count
* sizeof(*backtrack
));
5572 offset
+= backtrack_count
* sizeof(*backtrack
);
5574 input_count
= table_read_be_word(table
, offset
);
5576 input
= table_read_ensure(table
, offset
, input_count
* sizeof(*input
));
5577 offset
+= input_count
* sizeof(*input
);
5579 lookahead_count
= table_read_be_word(table
, offset
);
5581 lookahead
= table_read_ensure(table
, offset
, lookahead_count
* sizeof(*lookahead
));
5582 offset
+= lookahead_count
* sizeof(*lookahead
);
5584 lookup_count
= table_read_be_word(table
, offset
);
5586 lookup_records
= table_read_ensure(table
, offset
, lookup_count
* 2 * sizeof(*lookup_records
));
5589 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(input
[0]), glyph
);
5591 if (coverage_index
== GLYPH_NOT_COVERED
)
5594 mc
.backtrack_offset
= subtable_offset
;
5595 mc
.input_offset
= subtable_offset
;
5596 mc
.lookahead_offset
= subtable_offset
;
5597 mc
.match_func
= opentype_match_coverage_func
;
5599 ret
= opentype_layout_apply_chain_context_match(backtrack_count
, backtrack
, input_count
, input
+ 1, lookahead_count
,
5600 lookahead
, lookup_count
, lookup_records
, &mc
);
5603 WARN("Unknown chaining contextual substitution format %u.\n", format
);
5608 static BOOL
opentype_layout_apply_gsub_reverse_chain_context_substitution(struct scriptshaping_context
*context
,
5609 const struct lookup
*lookup
, unsigned int subtable_offset
)
5611 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5612 unsigned int offset
= subtable_offset
;
5613 UINT16 glyph
, format
;
5615 if (context
->nesting_level_left
!= SHAPE_MAX_NESTING_LEVEL
)
5618 glyph
= context
->u
.subst
.glyphs
[context
->cur
];
5620 format
= table_read_be_word(table
, offset
);
5625 struct match_context mc
= { .context
= context
, .lookup
= lookup
};
5626 unsigned int start_index
= 0, end_index
= 0, backtrack_count
, lookahead_count
;
5627 unsigned int coverage
, coverage_index
;
5628 const UINT16
*backtrack
, *lookahead
;
5630 coverage
= table_read_be_word(table
, offset
);
5633 coverage_index
= opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
);
5634 if (coverage_index
== GLYPH_NOT_COVERED
)
5637 backtrack_count
= table_read_be_word(table
, offset
);
5640 backtrack
= table_read_ensure(table
, offset
, sizeof(*backtrack
) * backtrack_count
);
5641 offset
+= sizeof(*backtrack
) * backtrack_count
;
5643 lookahead_count
= table_read_be_word(table
, offset
);
5646 lookahead
= table_read_ensure(table
, offset
, sizeof(*lookahead
) * lookahead_count
);
5647 offset
+= sizeof(*lookahead
) * lookahead_count
;
5649 mc
.match_func
= opentype_match_coverage_func
;
5650 mc
.backtrack_offset
= subtable_offset
;
5651 mc
.lookahead_offset
= subtable_offset
;
5653 if (opentype_layout_context_match_backtrack(&mc
, backtrack_count
, backtrack
, &start_index
) &&
5654 opentype_layout_context_match_lookahead(&mc
, lookahead_count
, lookahead
, 1, &end_index
))
5656 unsigned int glyph_count
= table_read_be_word(table
, offset
);
5657 if (coverage_index
>= glyph_count
)
5661 glyph
= table_read_be_word(table
, offset
+ coverage_index
* sizeof(glyph
));
5662 opentype_layout_replace_glyph(context
, glyph
);
5668 WARN("Unknown reverse chaining contextual substitution format %u.\n", format
);
5673 static BOOL
opentype_layout_apply_gsub_lookup(struct scriptshaping_context
*context
, const struct lookup
*lookup
)
5675 unsigned int i
, lookup_type
;
5678 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
5680 unsigned int subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, i
);
5682 if (lookup
->type
== GSUB_LOOKUP_EXTENSION_SUBST
)
5684 lookup_type
= opentype_layout_adjust_extension_subtable(context
, &subtable_offset
);
5689 lookup_type
= lookup
->type
;
5691 switch (lookup_type
)
5693 case GSUB_LOOKUP_SINGLE_SUBST
:
5694 ret
= opentype_layout_apply_gsub_single_substitution(context
, lookup
, subtable_offset
);
5696 case GSUB_LOOKUP_MULTIPLE_SUBST
:
5697 ret
= opentype_layout_apply_gsub_mult_substitution(context
, lookup
, subtable_offset
);
5699 case GSUB_LOOKUP_ALTERNATE_SUBST
:
5700 ret
= opentype_layout_apply_gsub_alt_substitution(context
, lookup
, subtable_offset
);
5702 case GSUB_LOOKUP_LIGATURE_SUBST
:
5703 ret
= opentype_layout_apply_gsub_lig_substitution(context
, lookup
, subtable_offset
);
5705 case GSUB_LOOKUP_CONTEXTUAL_SUBST
:
5706 ret
= opentype_layout_apply_context(context
, lookup
, subtable_offset
);
5708 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST
:
5709 ret
= opentype_layout_apply_chain_context(context
, lookup
, subtable_offset
);
5711 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST
:
5712 ret
= opentype_layout_apply_gsub_reverse_chain_context_substitution(context
, lookup
, subtable_offset
);
5714 case GSUB_LOOKUP_EXTENSION_SUBST
:
5715 WARN("Invalid lookup type for extension substitution %#x.\n", lookup_type
);
5718 WARN("Unknown lookup type %u.\n", lookup_type
);
5728 static unsigned int unicode_get_mirrored_char(unsigned int codepoint
)
5730 extern const WCHAR wine_mirror_map
[] DECLSPEC_HIDDEN
;
5732 /* TODO: check if mirroring for higher planes makes sense at all */
5733 if (codepoint
> 0xffff) return codepoint
;
5734 mirror
= get_table_entry(wine_mirror_map
, codepoint
);
5735 return mirror
? mirror
: codepoint
;
5739 * 034F # Mn COMBINING GRAPHEME JOINER
5740 * 061C # Cf ARABIC LETTER MARK
5741 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5742 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5743 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5744 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5746 static unsigned int opentype_is_default_ignorable(unsigned int codepoint
)
5748 return codepoint
== 0x34f || codepoint
== 0x61c || codepoint
== 0xfeff ||
5749 (codepoint
>= 0x180b && codepoint
<= 0x180e) || (codepoint
>= 0x200b && codepoint
<= 0x200f);
5752 static unsigned int opentype_is_diacritic(unsigned int codepoint
)
5754 WCHAR ch
= codepoint
;
5756 /* Ignore higher planes for now. */
5757 if (codepoint
> 0xffff) return 0;
5758 GetStringTypeW(CT_CTYPE3
, &ch
, 1, &type
);
5759 return !!(type
& C3_DIACRITIC
);
5762 static void opentype_get_nominal_glyphs(struct scriptshaping_context
*context
, const struct shaping_features
*features
)
5764 unsigned int rtlm_mask
= shaping_features_get_mask(features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), NULL
);
5765 const struct shaping_font_ops
*font
= context
->cache
->font
;
5766 unsigned int i
, g
, c
, codepoint
, cluster_start_idx
= 0;
5767 UINT16
*clustermap
= context
->u
.subst
.clustermap
;
5768 const WCHAR
*text
= context
->text
;
5771 memset(context
->u
.subst
.glyph_props
, 0, context
->u
.subst
.max_glyph_count
* sizeof(*context
->u
.subst
.glyph_props
));
5772 memset(context
->u
.buffer
.text_props
, 0, context
->length
* sizeof(*context
->u
.buffer
.text_props
));
5774 for (i
= 0; i
< context
->length
; ++i
)
5776 g
= context
->glyph_count
;
5778 if ((bmp
= !(IS_HIGH_SURROGATE(text
[i
]) && (i
< context
->length
- 1) && IS_LOW_SURROGATE(text
[i
+ 1]))))
5780 codepoint
= text
[i
];
5784 codepoint
= 0x10000 + ((text
[i
] - 0xd800) << 10) + (text
[i
+ 1] - 0xdc00);
5787 if (context
->is_rtl
)
5789 c
= unicode_get_mirrored_char(codepoint
);
5790 if (c
!= codepoint
&& font
->has_glyph(context
->cache
->context
, c
))
5793 context
->glyph_infos
[i
].mask
|= rtlm_mask
;
5796 /* TODO: should this check for glyph availability? */
5797 if (*context
->u
.subst
.digits
&& codepoint
>= '0' && codepoint
<= '9')
5798 codepoint
= context
->u
.subst
.digits
[codepoint
- '0'];
5800 context
->u
.buffer
.glyphs
[g
] = font
->get_glyph(context
->cache
->context
, codepoint
);
5801 context
->u
.buffer
.glyph_props
[g
].justification
= SCRIPT_JUSTIFY_CHARACTER
;
5802 opentype_set_subst_glyph_props(context
, g
);
5804 /* Group diacritics with preceding base. Glyph class is ignored here. */
5805 if (!g
|| !opentype_is_diacritic(codepoint
))
5807 context
->u
.buffer
.glyph_props
[g
].isClusterStart
= 1;
5808 context
->glyph_infos
[g
].start_text_idx
= i
;
5809 cluster_start_idx
= g
;
5812 if (opentype_is_default_ignorable(codepoint
))
5813 context
->u
.buffer
.glyph_props
[g
].isZeroWidthSpace
= 1;
5814 context
->u
.buffer
.glyph_props
[g
].components
= 1;
5815 context
->glyph_count
++;
5817 /* Set initial cluster map here, it's used for setting user features masks. */
5818 clustermap
[i
] = cluster_start_idx
;
5820 context
->u
.buffer
.text_props
[i
].canBreakShapingAfter
= 1;
5823 clustermap
[i
+ 1] = cluster_start_idx
;
5824 context
->u
.buffer
.text_props
[i
+ 1].canBreakShapingAfter
= 1;
5830 static BOOL
opentype_is_gsub_lookup_reversed(const struct scriptshaping_context
*context
, const struct lookup
*lookup
)
5832 unsigned int subtable_offset
, lookup_type
= lookup
->type
;
5834 if (lookup
->type
== GSUB_LOOKUP_EXTENSION_SUBST
)
5836 subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, 0);
5837 /* Assumes format 1. */
5838 lookup_type
= table_read_be_word(&context
->table
->table
, subtable_offset
+ 2);
5840 return lookup_type
== GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST
;
5843 static void opentype_layout_apply_gsub_context_lookup(struct scriptshaping_context
*context
, unsigned int lookup_index
)
5845 struct lookup lookup
= { 0 };
5846 /* Feature mask is intentionally zero, it's not used outside of main loop. */
5847 if (opentype_layout_init_lookup(context
->table
, lookup_index
, 0, &lookup
))
5848 opentype_layout_apply_gsub_lookup(context
, &lookup
);
5851 void opentype_layout_apply_gsub_features(struct scriptshaping_context
*context
, unsigned int script_index
,
5852 unsigned int language_index
, const struct shaping_features
*features
)
5854 struct lookups lookups
= { 0 };
5855 unsigned int i
, j
, start_idx
;
5858 context
->nesting_level_left
= SHAPE_MAX_NESTING_LEVEL
;
5859 context
->u
.buffer
.apply_context_lookup
= opentype_layout_apply_gsub_context_lookup
;
5860 opentype_layout_collect_lookups(context
, script_index
, language_index
, features
, context
->table
, &lookups
);
5862 opentype_get_nominal_glyphs(context
, features
);
5863 opentype_layout_set_glyph_masks(context
, features
);
5865 for (i
= 0; i
< lookups
.count
; ++i
)
5867 const struct lookup
*lookup
= &lookups
.lookups
[i
];
5869 context
->lookup_mask
= lookup
->mask
;
5871 if (!opentype_is_gsub_lookup_reversed(context
, lookup
))
5874 while (context
->cur
< context
->glyph_count
)
5878 if ((context
->glyph_infos
[context
->cur
].mask
& lookup
->mask
) &&
5879 lookup_is_glyph_match(context
, context
->cur
, lookup
->flags
))
5881 ret
= opentype_layout_apply_gsub_lookup(context
, lookup
);
5890 context
->cur
= context
->glyph_count
- 1;
5894 if ((context
->glyph_infos
[context
->cur
].mask
& lookup
->mask
) &&
5895 lookup_is_glyph_match(context
, context
->cur
, lookup
->flags
))
5897 opentype_layout_apply_gsub_lookup(context
, lookup
);
5900 if (context
->cur
== 0) break;
5906 /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding
5907 text span to start_idx. */
5909 for (i
= 1; i
< context
->glyph_count
; ++i
)
5911 if (context
->u
.buffer
.glyph_props
[i
].isClusterStart
)
5913 unsigned int start_text
, end_text
;
5915 start_text
= context
->glyph_infos
[start_idx
].start_text_idx
;
5916 end_text
= context
->glyph_infos
[i
].start_text_idx
;
5918 for (j
= start_text
; j
< end_text
; ++j
)
5919 context
->u
.buffer
.clustermap
[j
] = start_idx
;
5925 /* Fill the tail. */
5926 for (j
= context
->glyph_infos
[start_idx
].start_text_idx
; j
< context
->length
; ++j
)
5927 context
->u
.buffer
.clustermap
[j
] = start_idx
;
5929 heap_free(lookups
.lookups
);
5932 static BOOL
opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context
*context
, UINT16 glyph
,
5933 unsigned int subtable_offset
, unsigned int coverage
, unsigned int format
)
5935 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5936 const UINT16
*offsets
;
5939 if (format
== 1 || format
== 2)
5941 if (opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
) != GLYPH_NOT_COVERED
)
5944 else if (format
== 3)
5946 count
= table_read_be_word(table
, subtable_offset
+ 2);
5947 if (!count
|| !(offsets
= table_read_ensure(table
, subtable_offset
+ 6, count
* sizeof(*offsets
))))
5950 if (opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(offsets
[0]), glyph
) != GLYPH_NOT_COVERED
)
5957 static BOOL
opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context
*context
, UINT16 glyph
,
5958 unsigned int subtable_offset
, unsigned int coverage
, unsigned int format
)
5960 const struct dwrite_fonttable
*table
= &context
->table
->table
;
5961 unsigned int count
, backtrack_count
;
5962 const UINT16
*offsets
;
5964 if (format
== 1 || format
== 2)
5966 if (opentype_layout_is_glyph_covered(table
, subtable_offset
+ coverage
, glyph
) != GLYPH_NOT_COVERED
)
5969 else if (format
== 3)
5971 backtrack_count
= table_read_be_word(table
, subtable_offset
+ 2);
5973 count
= table_read_be_word(table
, subtable_offset
+ 4 + backtrack_count
* sizeof(*offsets
));
5975 if (!count
|| !(offsets
= table_read_ensure(table
, subtable_offset
+ 6 + backtrack_count
* sizeof(*offsets
),
5976 count
* sizeof(*offsets
))))
5979 if (opentype_layout_is_glyph_covered(table
, subtable_offset
+ GET_BE_WORD(offsets
[0]), glyph
) != GLYPH_NOT_COVERED
)
5986 static BOOL
opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context
*context
, UINT16 glyph
,
5987 const struct lookup
*lookup
)
5989 const struct dwrite_fonttable
*gsub
= &context
->table
->table
;
5990 static const unsigned short gsub_formats
[] =
5993 1, /* SingleSubst */
5994 1, /* MultipleSubst */
5995 1, /* AlternateSubst */
5996 1, /* LigatureSubst */
5997 3, /* ContextSubst */
5998 3, /* ChainContextSubst */
5999 0, /* Extension, unused */
6000 1, /* ReverseChainSubst */
6002 unsigned int i
, coverage
, lookup_type
, format
;
6004 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
6006 unsigned int subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, i
);
6008 if (lookup
->type
== GSUB_LOOKUP_EXTENSION_SUBST
)
6010 lookup_type
= opentype_layout_adjust_extension_subtable(context
, &subtable_offset
);
6015 lookup_type
= lookup
->type
;
6017 format
= table_read_be_word(gsub
, subtable_offset
);
6019 if (!format
|| format
> ARRAY_SIZE(gsub_formats
) || format
> gsub_formats
[lookup_type
])
6022 coverage
= table_read_be_word(gsub
, subtable_offset
+ 2);
6024 switch (lookup_type
)
6026 case GSUB_LOOKUP_SINGLE_SUBST
:
6027 case GSUB_LOOKUP_MULTIPLE_SUBST
:
6028 case GSUB_LOOKUP_ALTERNATE_SUBST
:
6029 case GSUB_LOOKUP_LIGATURE_SUBST
:
6030 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST
:
6032 if (opentype_layout_is_glyph_covered(gsub
, subtable_offset
+ coverage
, glyph
) != GLYPH_NOT_COVERED
)
6037 case GSUB_LOOKUP_CONTEXTUAL_SUBST
:
6039 if (opentype_layout_contextual_lookup_is_glyph_covered(context
, glyph
, subtable_offset
, coverage
, format
))
6044 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST
:
6046 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context
, glyph
, subtable_offset
, coverage
, format
))
6052 WARN("Unknown lookup type %u.\n", lookup_type
);
6059 static BOOL
opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context
*context
, UINT16 glyph
,
6060 const struct lookup
*lookup
)
6062 const struct dwrite_fonttable
*gpos
= &context
->table
->table
;
6063 static const unsigned short gpos_formats
[] =
6069 1, /* MarkBasePos */
6071 1, /* MarkMarkPos */
6073 3, /* ChainContextPos */
6074 0, /* Extension, unused */
6076 unsigned int i
, coverage
, lookup_type
, format
;
6078 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
6080 unsigned int subtable_offset
= opentype_layout_get_gsubgpos_subtable(context
, lookup
->offset
, i
);
6082 if (lookup
->type
== GPOS_LOOKUP_EXTENSION_POSITION
)
6084 lookup_type
= opentype_layout_adjust_extension_subtable(context
, &subtable_offset
);
6089 lookup_type
= lookup
->type
;
6091 format
= table_read_be_word(gpos
, subtable_offset
);
6093 if (!format
|| format
> ARRAY_SIZE(gpos_formats
) || format
> gpos_formats
[lookup_type
])
6096 coverage
= table_read_be_word(gpos
, subtable_offset
+ 2);
6098 switch (lookup_type
)
6100 case GPOS_LOOKUP_SINGLE_ADJUSTMENT
:
6101 case GPOS_LOOKUP_PAIR_ADJUSTMENT
:
6102 case GPOS_LOOKUP_CURSIVE_ATTACHMENT
:
6103 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT
:
6104 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT
:
6105 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT
:
6107 if (opentype_layout_is_glyph_covered(gpos
, subtable_offset
+ coverage
, glyph
) != GLYPH_NOT_COVERED
)
6112 case GPOS_LOOKUP_CONTEXTUAL_POSITION
:
6114 if (opentype_layout_contextual_lookup_is_glyph_covered(context
, glyph
, subtable_offset
, coverage
, format
))
6119 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION
:
6121 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context
, glyph
, subtable_offset
, coverage
, format
))
6127 WARN("Unknown lookup type %u.\n", lookup_type
);
6134 typedef BOOL (*p_lookup_is_glyph_covered_func
)(struct scriptshaping_context
*context
, UINT16 glyph
, const struct lookup
*lookup
);
6136 BOOL
opentype_layout_check_feature(struct scriptshaping_context
*context
, unsigned int script_index
,
6137 unsigned int language_index
, struct shaping_feature
*feature
, unsigned int glyph_count
,
6138 const UINT16
*glyphs
, UINT8
*feature_applies
)
6140 p_lookup_is_glyph_covered_func func_is_covered
;
6141 struct shaping_features features
= { 0 };
6142 struct lookups lookups
= { 0 };
6143 BOOL ret
= FALSE
, is_covered
;
6144 unsigned int i
, j
, applies
;
6146 features
.features
= feature
;
6149 for (i
= 0; i
< context
->glyph_count
; ++i
)
6150 opentype_set_glyph_props(context
, i
);
6152 opentype_layout_collect_lookups(context
, script_index
, language_index
, &features
, context
->table
, &lookups
);
6154 func_is_covered
= context
->table
== &context
->cache
->gsub
? opentype_layout_gsub_lookup_is_glyph_covered
:
6155 opentype_layout_gpos_lookup_is_glyph_covered
;
6157 for (i
= 0; i
< lookups
.count
; ++i
)
6159 struct lookup
*lookup
= &lookups
.lookups
[i
];
6162 for (j
= 0; j
< context
->glyph_count
; ++j
)
6164 if (lookup_is_glyph_match(context
, j
, lookup
->flags
))
6166 if ((is_covered
= func_is_covered(context
, glyphs
[i
], lookup
)))
6168 feature_applies
[j
] |= is_covered
;
6172 if ((ret
= (applies
== context
->glyph_count
)))
6176 heap_free(lookups
.lookups
);
6181 HRESULT
opentype_get_vertical_glyph_variants(struct dwrite_fontface
*fontface
, unsigned int glyph_count
,
6182 const UINT16
*nominal_glyphs
, UINT16
*glyphs
)
6184 struct shaping_features features
= { 0 };
6185 struct shaping_feature vert_feature
= { 0 };
6186 struct scriptshaping_context context
= { 0 };
6187 struct lookups lookups
= { 0 };
6190 memcpy(glyphs
, nominal_glyphs
, glyph_count
* sizeof(*glyphs
));
6192 if (!(fontface
->flags
& FONTFACE_HAS_VERTICAL_VARIANTS
))
6195 context
.cache
= fontface_get_shaping_cache(fontface
);
6196 context
.u
.subst
.glyphs
= glyphs
;
6197 context
.u
.subst
.glyph_props
= heap_calloc(glyph_count
, sizeof(*context
.u
.subst
.glyph_props
));
6198 context
.u
.subst
.max_glyph_count
= glyph_count
;
6199 context
.u
.subst
.capacity
= glyph_count
;
6200 context
.glyph_infos
= heap_alloc_zero(sizeof(*context
.glyph_infos
) * glyph_count
);
6201 context
.table
= &context
.cache
->gsub
;
6203 vert_feature
.tag
= DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6204 vert_feature
.flags
= FEATURE_GLOBAL
| FEATURE_GLOBAL_SEARCH
;
6205 vert_feature
.max_value
= 1;
6206 vert_feature
.default_value
= 1;
6208 features
.features
= &vert_feature
;
6209 features
.count
= features
.capacity
= 1;
6211 opentype_layout_collect_lookups(&context
, ~0u, ~0u, &features
, context
.table
, &lookups
);
6212 opentype_layout_set_glyph_masks(&context
, &features
);
6214 for (i
= 0; i
< lookups
.count
; ++i
)
6216 const struct lookup
*lookup
= &lookups
.lookups
[i
];
6218 if (lookup
->type
!= GSUB_LOOKUP_SINGLE_SUBST
)
6222 while (context
.cur
< context
.glyph_count
)
6226 if (lookup_is_glyph_match(&context
, context
.cur
, lookup
->flags
))
6227 ret
= opentype_layout_apply_gsub_lookup(&context
, lookup
);
6234 heap_free(context
.u
.subst
.glyph_props
);
6235 heap_free(context
.glyph_infos
);
6236 heap_free(lookups
.lookups
);