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
24 #include "dwrite_private.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
29 #define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d')
30 #define MS_HHEA_TAG DWRITE_MAKE_OPENTYPE_TAG('h','h','e','a')
31 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
32 #define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2')
33 #define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t')
34 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
35 #define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
36 #define MS_GPOS_TAG DWRITE_MAKE_OPENTYPE_TAG('G','P','O','S')
37 #define MS_GSUB_TAG DWRITE_MAKE_OPENTYPE_TAG('G','S','U','B')
38 #define MS_NAME_TAG DWRITE_MAKE_OPENTYPE_TAG('n','a','m','e')
39 #define MS_GLYF_TAG DWRITE_MAKE_OPENTYPE_TAG('g','l','y','f')
40 #define MS_CFF__TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F',' ')
41 #define MS_CFF2_TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F','2')
42 #define MS_CPAL_TAG DWRITE_MAKE_OPENTYPE_TAG('C','P','A','L')
43 #define MS_COLR_TAG DWRITE_MAKE_OPENTYPE_TAG('C','O','L','R')
44 #define MS_SVG__TAG DWRITE_MAKE_OPENTYPE_TAG('S','V','G',' ')
45 #define MS_SBIX_TAG DWRITE_MAKE_OPENTYPE_TAG('s','b','i','x')
46 #define MS_MAXP_TAG DWRITE_MAKE_OPENTYPE_TAG('m','a','x','p')
47 #define MS_CBLC_TAG DWRITE_MAKE_OPENTYPE_TAG('C','B','L','C')
48 #define MS_CMAP_TAG DWRITE_MAKE_OPENTYPE_TAG('c','m','a','p')
49 #define MS_META_TAG DWRITE_MAKE_OPENTYPE_TAG('m','e','t','a')
52 #define MS_PNG__TAG DWRITE_MAKE_OPENTYPE_TAG('p','n','g',' ')
53 #define MS_JPG__TAG DWRITE_MAKE_OPENTYPE_TAG('j','p','g',' ')
54 #define MS_TIFF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','i','f','f')
56 #define MS_WOFF_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','F')
57 #define MS_WOF2_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','2')
60 #define MS_DLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('d','l','n','g')
61 #define MS_SLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('s','l','n','g')
63 #ifdef WORDS_BIGENDIAN
64 #define GET_BE_WORD(x) (x)
65 #define GET_BE_DWORD(x) (x)
67 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
68 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
93 struct cmap_encoding_record
104 struct cmap_encoding_record tables
[1];
111 } CMAP_SegmentedCoverage_group
;
113 struct cmap_segmented_coverage
120 CMAP_SegmentedCoverage_group groups
[1];
123 struct cmap_segment_mapping
135 enum OPENTYPE_CMAP_TABLE_FORMAT
137 OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING
= 4,
138 OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE
= 12
141 enum opentype_cmap_table_platform
143 OPENTYPE_CMAP_TABLE_PLATFORM_WIN
= 3,
146 enum opentype_cmap_table_encoding
148 OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL
= 0,
149 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_BMP
= 1,
150 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_FULL
= 10,
153 /* PANOSE is 10 bytes in size, need to pack the structure properly */
154 #include "pshpack2.h"
171 USHORT lowestRecPPEM
;
172 SHORT direction_hint
;
174 SHORT glyphdata_format
;
177 enum TT_HEAD_MACSTYLE
179 TT_HEAD_MACSTYLE_BOLD
= 1 << 0,
180 TT_HEAD_MACSTYLE_ITALIC
= 1 << 1,
181 TT_HEAD_MACSTYLE_UNDERLINE
= 1 << 2,
182 TT_HEAD_MACSTYLE_OUTLINE
= 1 << 3,
183 TT_HEAD_MACSTYLE_SHADOW
= 1 << 4,
184 TT_HEAD_MACSTYLE_CONDENSED
= 1 << 5,
185 TT_HEAD_MACSTYLE_EXTENDED
= 1 << 6,
192 SHORT underlinePosition
;
193 SHORT underlineThickness
;
205 USHORT usWeightClass
;
208 SHORT ySubscriptXSize
;
209 SHORT ySubscriptYSize
;
210 SHORT ySubscriptXOffset
;
211 SHORT ySubscriptYOffset
;
212 SHORT ySuperscriptXSize
;
213 SHORT ySuperscriptYSize
;
214 SHORT ySuperscriptXOffset
;
215 SHORT ySuperscriptYOffset
;
216 SHORT yStrikeoutSize
;
217 SHORT yStrikeoutPosition
;
220 ULONG ulUnicodeRange1
;
221 ULONG ulUnicodeRange2
;
222 ULONG ulUnicodeRange3
;
223 ULONG ulUnicodeRange4
;
226 USHORT usFirstCharIndex
;
227 USHORT usLastCharIndex
;
228 /* According to the Apple spec, original version didn't have the below fields,
229 * version numbers were taken from the OpenType spec.
231 /* version 0 (TrueType 1.5) */
232 USHORT sTypoAscender
;
233 USHORT sTypoDescender
;
237 /* version 1 (TrueType 1.66) */
238 ULONG ulCodePageRange1
;
239 ULONG ulCodePageRange2
;
240 /* version 2 (OpenType 1.2) */
243 USHORT usDefaultChar
;
254 USHORT advanceWidthMax
;
255 SHORT minLeftSideBearing
;
256 SHORT minRightSideBearing
;
258 SHORT caretSlopeRise
;
262 SHORT metricDataFormat
;
263 USHORT numberOfHMetrics
;
271 DWORD strike_offset
[1];
278 DWORD glyphdata_offsets
[1];
281 struct sbix_glyph_data
306 struct cblc_bitmapsize_table
308 DWORD indexSubTableArrayOffset
;
309 DWORD indexTablesSize
;
310 DWORD numberofIndexSubTables
;
312 sbitLineMetrics hori
;
313 sbitLineMetrics vert
;
314 WORD startGlyphIndex
;
332 struct gasp_range ranges
[1];
335 enum OS2_FSSELECTION
{
336 OS2_FSSELECTION_ITALIC
= 1 << 0,
337 OS2_FSSELECTION_UNDERSCORE
= 1 << 1,
338 OS2_FSSELECTION_NEGATIVE
= 1 << 2,
339 OS2_FSSELECTION_OUTLINED
= 1 << 3,
340 OS2_FSSELECTION_STRIKEOUT
= 1 << 4,
341 OS2_FSSELECTION_BOLD
= 1 << 5,
342 OS2_FSSELECTION_REGULAR
= 1 << 6,
343 OS2_FSSELECTION_USE_TYPO_METRICS
= 1 << 7,
344 OS2_FSSELECTION_WWS
= 1 << 8,
345 OS2_FSSELECTION_OBLIQUE
= 1 << 9
361 TT_NameRecord nameRecord
[1];
391 struct vdmx_vtable entries
[1];
394 struct ot_feature_record
400 struct ot_feature_list
403 struct ot_feature_record features
[1];
408 WORD lookup_order
; /* Reserved */
409 WORD required_feature_index
;
411 WORD feature_index
[1];
414 struct ot_langsys_record
422 WORD default_langsys
;
424 struct ot_langsys_record langsys
[1];
427 struct ot_script_record
433 struct ot_script_list
436 struct ot_script_record scripts
[1];
441 GDEF_CLASS_UNCLASSIFIED
= 0,
443 GDEF_CLASS_LIGATURE
= 2,
445 GDEF_CLASS_COMPONENT
= 4,
446 GDEF_CLASS_MAX
= GDEF_CLASS_COMPONENT
,
455 WORD markattach_classdef
;
458 struct ot_gdef_classdef_format1
466 struct ot_gdef_class_range
473 struct ot_gdef_classdef_format2
477 struct ot_gdef_class_range ranges
[1];
480 struct gpos_gsub_header
488 enum gsub_gpos_lookup_flags
490 LOOKUP_FLAG_RTL
= 0x1,
491 LOOKUP_FLAG_IGNORE_BASE
= 0x2,
492 LOOKUP_FLAG_IGNORE_LIGATURES
= 0x4,
493 LOOKUP_FLAG_IGNORE_MARKS
= 0x8,
495 LOOKUP_FLAG_IGNORE_MASK
= 0xe,
498 enum gpos_lookup_type
500 GPOS_LOOKUP_SINGLE_ADJUSTMENT
= 1,
501 GPOS_LOOKUP_PAIR_ADJUSTMENT
= 2,
502 GPOS_LOOKUP_CURSIVE_ATTACHMENT
= 3,
503 GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT
= 4,
504 GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT
= 5,
505 GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT
= 6,
506 GPOS_LOOKUP_CONTEXTUAL_POSITION
= 7,
507 GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION
= 8,
508 GPOS_LOOKUP_EXTENSION_POSITION
= 9,
511 enum gpos_value_format
513 GPOS_VALUE_X_PLACEMENT
= 0x1,
514 GPOS_VALUE_Y_PLACEMENT
= 0x2,
515 GPOS_VALUE_X_ADVANCE
= 0x4,
516 GPOS_VALUE_Y_ADVANCE
= 0x8,
517 GPOS_VALUE_X_PLACEMENT_DEVICE
= 0x10,
518 GPOS_VALUE_Y_PLACEMENT_DEVICE
= 0x20,
519 GPOS_VALUE_X_ADVANCE_DEVICE
= 0x40,
520 GPOS_VALUE_Y_ADVANCE_DEVICE
= 0x80,
523 enum OPENTYPE_PLATFORM_ID
525 OPENTYPE_PLATFORM_UNICODE
= 0,
526 OPENTYPE_PLATFORM_MAC
,
527 OPENTYPE_PLATFORM_ISO
,
528 OPENTYPE_PLATFORM_WIN
,
529 OPENTYPE_PLATFORM_CUSTOM
532 struct ot_gpos_extensionpos_format1
536 DWORD extension_offset
;
543 WORD lookuplist_index
[1];
546 struct ot_lookup_list
552 struct ot_lookup_table
560 #define GLYPH_NOT_COVERED (~0u)
562 struct ot_coverage_format1
569 struct ot_coverage_range
573 WORD startcoverage_index
;
576 struct ot_coverage_format2
580 struct ot_coverage_range ranges
[1];
583 struct ot_gpos_device_table
591 struct ot_gpos_singlepos_format1
599 struct ot_gpos_singlepos_format2
608 struct ot_gpos_pairvalue
614 struct ot_gpos_pairset
616 WORD pairvalue_count
;
617 struct ot_gpos_pairvalue pairvalues
[1];
620 struct ot_gpos_pairpos_format1
630 struct ot_gpos_pairpos_format2
643 struct ot_gpos_anchor_format1
650 struct ot_gpos_anchor_format2
658 struct ot_gpos_anchor_format3
667 struct ot_gpos_cursive_format1
675 struct ot_gpos_mark_record
681 struct ot_gpos_mark_array
684 struct ot_gpos_mark_record records
[1];
687 struct ot_gpos_base_array
693 struct ot_gpos_mark_to_base_format1
698 WORD mark_class_count
;
703 struct ot_gpos_mark_to_lig_format1
708 WORD mark_class_count
;
713 struct ot_gpos_mark_to_mark_format1
718 WORD mark_class_count
;
727 } GSUB_SingleSubstFormat1
;
734 } GSUB_SingleSubstFormat2
;
738 WORD ExtensionLookupType
;
739 DWORD ExtensionOffset
;
740 } GSUB_ExtensionPosFormat1
;
744 enum gsub_lookup_type
746 GSUB_LOOKUP_SINGLE_SUBST
= 1,
747 GSUB_LOOKUP_EXTENSION_SUBST
= 7,
750 enum TT_NAME_WINDOWS_ENCODING_ID
752 TT_NAME_WINDOWS_ENCODING_SYMBOL
= 0,
753 TT_NAME_WINDOWS_ENCODING_UCS2
,
754 TT_NAME_WINDOWS_ENCODING_SJIS
,
755 TT_NAME_WINDOWS_ENCODING_PRC
,
756 TT_NAME_WINDOWS_ENCODING_BIG5
,
757 TT_NAME_WINDOWS_ENCODING_WANSUNG
,
758 TT_NAME_WINDOWS_ENCODING_JOHAB
,
759 TT_NAME_WINDOWS_ENCODING_RESERVED1
,
760 TT_NAME_WINDOWS_ENCODING_RESERVED2
,
761 TT_NAME_WINDOWS_ENCODING_RESERVED3
,
762 TT_NAME_WINDOWS_ENCODING_UCS4
765 enum TT_NAME_MAC_ENCODING_ID
767 TT_NAME_MAC_ENCODING_ROMAN
= 0,
768 TT_NAME_MAC_ENCODING_JAPANESE
,
769 TT_NAME_MAC_ENCODING_TRAD_CHINESE
,
770 TT_NAME_MAC_ENCODING_KOREAN
,
771 TT_NAME_MAC_ENCODING_ARABIC
,
772 TT_NAME_MAC_ENCODING_HEBREW
,
773 TT_NAME_MAC_ENCODING_GREEK
,
774 TT_NAME_MAC_ENCODING_RUSSIAN
,
775 TT_NAME_MAC_ENCODING_RSYMBOL
,
776 TT_NAME_MAC_ENCODING_DEVANAGARI
,
777 TT_NAME_MAC_ENCODING_GURMUKHI
,
778 TT_NAME_MAC_ENCODING_GUJARATI
,
779 TT_NAME_MAC_ENCODING_ORIYA
,
780 TT_NAME_MAC_ENCODING_BENGALI
,
781 TT_NAME_MAC_ENCODING_TAMIL
,
782 TT_NAME_MAC_ENCODING_TELUGU
,
783 TT_NAME_MAC_ENCODING_KANNADA
,
784 TT_NAME_MAC_ENCODING_MALAYALAM
,
785 TT_NAME_MAC_ENCODING_SINHALESE
,
786 TT_NAME_MAC_ENCODING_BURMESE
,
787 TT_NAME_MAC_ENCODING_KHMER
,
788 TT_NAME_MAC_ENCODING_THAI
,
789 TT_NAME_MAC_ENCODING_LAOTIAN
,
790 TT_NAME_MAC_ENCODING_GEORGIAN
,
791 TT_NAME_MAC_ENCODING_ARMENIAN
,
792 TT_NAME_MAC_ENCODING_SIMPL_CHINESE
,
793 TT_NAME_MAC_ENCODING_TIBETAN
,
794 TT_NAME_MAC_ENCODING_MONGOLIAN
,
795 TT_NAME_MAC_ENCODING_GEEZ
,
796 TT_NAME_MAC_ENCODING_SLAVIC
,
797 TT_NAME_MAC_ENCODING_VIETNAMESE
,
798 TT_NAME_MAC_ENCODING_SINDHI
,
799 TT_NAME_MAC_ENCODING_UNINTERPRETED
802 enum TT_NAME_MAC_LANGUAGE_ID
804 TT_NAME_MAC_LANGID_ENGLISH
= 0,
805 TT_NAME_MAC_LANGID_FRENCH
,
806 TT_NAME_MAC_LANGID_GERMAN
,
807 TT_NAME_MAC_LANGID_ITALIAN
,
808 TT_NAME_MAC_LANGID_DUTCH
,
809 TT_NAME_MAC_LANGID_SWEDISH
,
810 TT_NAME_MAC_LANGID_SPANISH
,
811 TT_NAME_MAC_LANGID_DANISH
,
812 TT_NAME_MAC_LANGID_PORTUGUESE
,
813 TT_NAME_MAC_LANGID_NORWEGIAN
,
814 TT_NAME_MAC_LANGID_HEBREW
,
815 TT_NAME_MAC_LANGID_JAPANESE
,
816 TT_NAME_MAC_LANGID_ARABIC
,
817 TT_NAME_MAC_LANGID_FINNISH
,
818 TT_NAME_MAC_LANGID_GREEK
,
819 TT_NAME_MAC_LANGID_ICELANDIC
,
820 TT_NAME_MAC_LANGID_MALTESE
,
821 TT_NAME_MAC_LANGID_TURKISH
,
822 TT_NAME_MAC_LANGID_CROATIAN
,
823 TT_NAME_MAC_LANGID_TRAD_CHINESE
,
824 TT_NAME_MAC_LANGID_URDU
,
825 TT_NAME_MAC_LANGID_HINDI
,
826 TT_NAME_MAC_LANGID_THAI
,
827 TT_NAME_MAC_LANGID_KOREAN
,
828 TT_NAME_MAC_LANGID_LITHUANIAN
,
829 TT_NAME_MAC_LANGID_POLISH
,
830 TT_NAME_MAC_LANGID_HUNGARIAN
,
831 TT_NAME_MAC_LANGID_ESTONIAN
,
832 TT_NAME_MAC_LANGID_LATVIAN
,
833 TT_NAME_MAC_LANGID_SAMI
,
834 TT_NAME_MAC_LANGID_FAROESE
,
835 TT_NAME_MAC_LANGID_FARSI
,
836 TT_NAME_MAC_LANGID_RUSSIAN
,
837 TT_NAME_MAC_LANGID_SIMPL_CHINESE
,
838 TT_NAME_MAC_LANGID_FLEMISH
,
839 TT_NAME_MAC_LANGID_GAELIC
,
840 TT_NAME_MAC_LANGID_ALBANIAN
,
841 TT_NAME_MAC_LANGID_ROMANIAN
,
842 TT_NAME_MAC_LANGID_CZECH
,
843 TT_NAME_MAC_LANGID_SLOVAK
,
844 TT_NAME_MAC_LANGID_SLOVENIAN
,
845 TT_NAME_MAC_LANGID_YIDDISH
,
846 TT_NAME_MAC_LANGID_SERBIAN
,
847 TT_NAME_MAC_LANGID_MACEDONIAN
,
848 TT_NAME_MAC_LANGID_BULGARIAN
,
849 TT_NAME_MAC_LANGID_UKRAINIAN
,
850 TT_NAME_MAC_LANGID_BYELORUSSIAN
,
851 TT_NAME_MAC_LANGID_UZBEK
,
852 TT_NAME_MAC_LANGID_KAZAKH
,
853 TT_NAME_MAC_LANGID_AZERB_CYR
,
854 TT_NAME_MAC_LANGID_AZERB_ARABIC
,
855 TT_NAME_MAC_LANGID_ARMENIAN
,
856 TT_NAME_MAC_LANGID_GEORGIAN
,
857 TT_NAME_MAC_LANGID_MOLDAVIAN
,
858 TT_NAME_MAC_LANGID_KIRGHIZ
,
859 TT_NAME_MAC_LANGID_TAJIKI
,
860 TT_NAME_MAC_LANGID_TURKMEN
,
861 TT_NAME_MAC_LANGID_MONGOLIAN
,
862 TT_NAME_MAC_LANGID_MONGOLIAN_CYR
,
863 TT_NAME_MAC_LANGID_PASHTO
,
864 TT_NAME_MAC_LANGID_KURDISH
,
865 TT_NAME_MAC_LANGID_KASHMIRI
,
866 TT_NAME_MAC_LANGID_SINDHI
,
867 TT_NAME_MAC_LANGID_TIBETAN
,
868 TT_NAME_MAC_LANGID_NEPALI
,
869 TT_NAME_MAC_LANGID_SANSKRIT
,
870 TT_NAME_MAC_LANGID_MARATHI
,
871 TT_NAME_MAC_LANGID_BENGALI
,
872 TT_NAME_MAC_LANGID_ASSAMESE
,
873 TT_NAME_MAC_LANGID_GUJARATI
,
874 TT_NAME_MAC_LANGID_PUNJABI
,
875 TT_NAME_MAC_LANGID_ORIYA
,
876 TT_NAME_MAC_LANGID_MALAYALAM
,
877 TT_NAME_MAC_LANGID_KANNADA
,
878 TT_NAME_MAC_LANGID_TAMIL
,
879 TT_NAME_MAC_LANGID_TELUGU
,
880 TT_NAME_MAC_LANGID_SINHALESE
,
881 TT_NAME_MAC_LANGID_BURMESE
,
882 TT_NAME_MAC_LANGID_KHMER
,
883 TT_NAME_MAC_LANGID_LAO
,
884 TT_NAME_MAC_LANGID_VIETNAMESE
,
885 TT_NAME_MAC_LANGID_INDONESIAN
,
886 TT_NAME_MAC_LANGID_TAGALOG
,
887 TT_NAME_MAC_LANGID_MALAY_ROMAN
,
888 TT_NAME_MAC_LANGID_MALAY_ARABIC
,
889 TT_NAME_MAC_LANGID_AMHARIC
,
890 TT_NAME_MAC_LANGID_TIGRINYA
,
891 TT_NAME_MAC_LANGID_GALLA
,
892 TT_NAME_MAC_LANGID_SOMALI
,
893 TT_NAME_MAC_LANGID_SWAHILI
,
894 TT_NAME_MAC_LANGID_KINYARWANDA
,
895 TT_NAME_MAC_LANGID_RUNDI
,
896 TT_NAME_MAC_LANGID_NYANJA
,
897 TT_NAME_MAC_LANGID_MALAGASY
,
898 TT_NAME_MAC_LANGID_ESPERANTO
,
899 TT_NAME_MAC_LANGID_WELSH
= 128,
900 TT_NAME_MAC_LANGID_BASQUE
,
901 TT_NAME_MAC_LANGID_CATALAN
,
902 TT_NAME_MAC_LANGID_LATIN
,
903 TT_NAME_MAC_LANGID_QUECHUA
,
904 TT_NAME_MAC_LANGID_GUARANI
,
905 TT_NAME_MAC_LANGID_AYMARA
,
906 TT_NAME_MAC_LANGID_TATAR
,
907 TT_NAME_MAC_LANGID_UIGHUR
,
908 TT_NAME_MAC_LANGID_DZONGKHA
,
909 TT_NAME_MAC_LANGID_JAVANESE
,
910 TT_NAME_MAC_LANGID_SUNDANESE
,
911 TT_NAME_MAC_LANGID_GALICIAN
,
912 TT_NAME_MAC_LANGID_AFRIKAANS
,
913 TT_NAME_MAC_LANGID_BRETON
,
914 TT_NAME_MAC_LANGID_INUKTITUT
,
915 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC
,
916 TT_NAME_MAC_LANGID_MANX_GAELIC
,
917 TT_NAME_MAC_LANGID_IRISH_GAELIC
,
918 TT_NAME_MAC_LANGID_TONGAN
,
919 TT_NAME_MAC_LANGID_GREEK_POLYTONIC
,
920 TT_NAME_MAC_LANGID_GREENLANDIC
,
921 TT_NAME_MAC_LANGID_AZER_ROMAN
924 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
925 static const char name_mac_langid_to_locale
[][10] = {
1079 enum OPENTYPE_STRING_ID
1081 OPENTYPE_STRING_COPYRIGHT_NOTICE
= 0,
1082 OPENTYPE_STRING_FAMILY_NAME
,
1083 OPENTYPE_STRING_SUBFAMILY_NAME
,
1084 OPENTYPE_STRING_UNIQUE_IDENTIFIER
,
1085 OPENTYPE_STRING_FULL_FONTNAME
,
1086 OPENTYPE_STRING_VERSION_STRING
,
1087 OPENTYPE_STRING_POSTSCRIPT_FONTNAME
,
1088 OPENTYPE_STRING_TRADEMARK
,
1089 OPENTYPE_STRING_MANUFACTURER
,
1090 OPENTYPE_STRING_DESIGNER
,
1091 OPENTYPE_STRING_DESCRIPTION
,
1092 OPENTYPE_STRING_VENDOR_URL
,
1093 OPENTYPE_STRING_DESIGNER_URL
,
1094 OPENTYPE_STRING_LICENSE_DESCRIPTION
,
1095 OPENTYPE_STRING_LICENSE_INFO_URL
,
1096 OPENTYPE_STRING_RESERVED_ID15
,
1097 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
,
1098 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
,
1099 OPENTYPE_STRING_COMPATIBLE_FULLNAME
,
1100 OPENTYPE_STRING_SAMPLE_TEXT
,
1101 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
,
1102 OPENTYPE_STRING_WWS_FAMILY_NAME
,
1103 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
1106 static const UINT16 dwriteid_to_opentypeid
[DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME
+ 1] =
1108 (UINT16
)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
1109 OPENTYPE_STRING_COPYRIGHT_NOTICE
,
1110 OPENTYPE_STRING_VERSION_STRING
,
1111 OPENTYPE_STRING_TRADEMARK
,
1112 OPENTYPE_STRING_MANUFACTURER
,
1113 OPENTYPE_STRING_DESIGNER
,
1114 OPENTYPE_STRING_DESIGNER_URL
,
1115 OPENTYPE_STRING_DESCRIPTION
,
1116 OPENTYPE_STRING_VENDOR_URL
,
1117 OPENTYPE_STRING_LICENSE_DESCRIPTION
,
1118 OPENTYPE_STRING_LICENSE_INFO_URL
,
1119 OPENTYPE_STRING_FAMILY_NAME
,
1120 OPENTYPE_STRING_SUBFAMILY_NAME
,
1121 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
,
1122 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
,
1123 OPENTYPE_STRING_SAMPLE_TEXT
,
1124 OPENTYPE_STRING_FULL_FONTNAME
,
1125 OPENTYPE_STRING_POSTSCRIPT_FONTNAME
,
1126 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
,
1127 OPENTYPE_STRING_WWS_FAMILY_NAME
,
1131 struct cpal_header_0
1134 USHORT num_palette_entries
;
1135 USHORT num_palettes
;
1136 USHORT num_color_records
;
1137 ULONG offset_first_color_record
;
1138 USHORT color_record_indices
[1];
1141 struct cpal_color_record
1153 USHORT num_baseglyph_records
;
1154 ULONG offset_baseglyph_records
;
1155 ULONG offset_layer_records
;
1156 USHORT num_layer_records
;
1159 struct colr_baseglyph_record
1162 USHORT first_layer_index
;
1166 struct colr_layer_record
1169 USHORT palette_index
;
1172 struct meta_data_map
1184 DWORD data_maps_count
;
1185 struct meta_data_map maps
[1];
1188 static const void *table_read_ensure(const struct dwrite_fonttable
*table
, unsigned int offset
, unsigned int size
)
1190 if (size
> table
->size
|| offset
> table
->size
- size
)
1193 return table
->data
+ offset
;
1196 static WORD
table_read_be_word(const struct dwrite_fonttable
*table
, unsigned int offset
)
1198 const WORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1199 return ptr
? GET_BE_WORD(*ptr
) : 0;
1202 static DWORD
table_read_be_dword(const struct dwrite_fonttable
*table
, unsigned int offset
)
1204 const DWORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1205 return ptr
? GET_BE_DWORD(*ptr
) : 0;
1208 static DWORD
table_read_dword(const struct dwrite_fonttable
*table
, unsigned int offset
)
1210 const DWORD
*ptr
= table_read_ensure(table
, offset
, sizeof(*ptr
));
1211 return ptr
? *ptr
: 0;
1214 BOOL
is_face_type_supported(DWRITE_FONT_FACE_TYPE type
)
1216 return (type
== DWRITE_FONT_FACE_TYPE_CFF
) ||
1217 (type
== DWRITE_FONT_FACE_TYPE_TRUETYPE
) ||
1218 (type
== DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
) ||
1219 (type
== DWRITE_FONT_FACE_TYPE_RAW_CFF
);
1222 typedef HRESULT (*dwrite_fontfile_analyzer
)(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1223 DWRITE_FONT_FACE_TYPE
*face_type
);
1225 static HRESULT
opentype_ttc_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1226 DWRITE_FONT_FACE_TYPE
*face_type
)
1228 static const DWORD ttctag
= MS_TTCF_TAG
;
1229 const TTC_Header_V1
*header
;
1233 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(header
), &context
);
1237 if (!memcmp(header
->TTCTag
, &ttctag
, sizeof(ttctag
))) {
1238 *font_count
= GET_BE_DWORD(header
->numFonts
);
1239 *file_type
= DWRITE_FONT_FILE_TYPE_OPENTYPE_COLLECTION
;
1240 *face_type
= DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
;
1243 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1245 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1248 static HRESULT
opentype_ttf_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1249 DWRITE_FONT_FACE_TYPE
*face_type
)
1251 const DWORD
*header
;
1255 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1259 if (GET_BE_DWORD(*header
) == 0x10000) {
1261 *file_type
= DWRITE_FONT_FILE_TYPE_TRUETYPE
;
1262 *face_type
= DWRITE_FONT_FACE_TYPE_TRUETYPE
;
1265 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1267 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1270 static HRESULT
opentype_otf_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1271 DWRITE_FONT_FACE_TYPE
*face_type
)
1273 const DWORD
*header
;
1277 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1281 if (GET_BE_DWORD(*header
) == MS_OTTO_TAG
) {
1283 *file_type
= DWRITE_FONT_FILE_TYPE_CFF
;
1284 *face_type
= DWRITE_FONT_FACE_TYPE_CFF
;
1287 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1289 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1292 static HRESULT
opentype_type1_analyzer(IDWriteFontFileStream
*stream
, UINT32
*font_count
, DWRITE_FONT_FILE_TYPE
*file_type
,
1293 DWRITE_FONT_FACE_TYPE
*face_type
)
1295 #include "pshpack1.h"
1296 /* Specified in Adobe TechNote #5178 */
1304 #include "poppack.h"
1305 struct type1_header
{
1309 const struct type1_header
*header
;
1313 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&header
, 0, sizeof(*header
), &context
);
1317 /* tag is followed by plain text section */
1318 if (header
->tag
== 0x8001 &&
1319 (!memcmp(header
->data
, "%!PS-AdobeFont", 14) ||
1320 !memcmp(header
->data
, "%!FontType", 10))) {
1322 *file_type
= DWRITE_FONT_FILE_TYPE_TYPE1_PFB
;
1323 *face_type
= DWRITE_FONT_FACE_TYPE_TYPE1
;
1326 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1328 /* let's see if it's a .pfm metrics file */
1329 if (*file_type
== DWRITE_FONT_FILE_TYPE_UNKNOWN
) {
1330 const struct pfm_header
*pfm_header
;
1333 BOOL header_checked
;
1335 hr
= IDWriteFontFileStream_GetFileSize(stream
, &filesize
);
1339 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&pfm_header
, 0, sizeof(*pfm_header
), &context
);
1343 offset
= pfm_header
->dfDevice
;
1344 header_checked
= pfm_header
->dfVersion
== 0x100 && pfm_header
->dfSize
== filesize
;
1345 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1347 /* as a last test check static string in PostScript information section */
1348 if (header_checked
) {
1349 static const char postscript
[] = "PostScript";
1352 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&devtype_name
, offset
, sizeof(postscript
), &context
);
1356 if (!memcmp(devtype_name
, postscript
, sizeof(postscript
))) {
1358 *file_type
= DWRITE_FONT_FILE_TYPE_TYPE1_PFM
;
1359 *face_type
= DWRITE_FONT_FACE_TYPE_TYPE1
;
1362 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
1366 return *file_type
!= DWRITE_FONT_FILE_TYPE_UNKNOWN
? S_OK
: S_FALSE
;
1369 HRESULT
opentype_analyze_font(IDWriteFontFileStream
*stream
, BOOL
*supported
, DWRITE_FONT_FILE_TYPE
*file_type
,
1370 DWRITE_FONT_FACE_TYPE
*face_type
, UINT32
*face_count
)
1372 static dwrite_fontfile_analyzer fontfile_analyzers
[] = {
1373 opentype_ttf_analyzer
,
1374 opentype_otf_analyzer
,
1375 opentype_ttc_analyzer
,
1376 opentype_type1_analyzer
,
1379 dwrite_fontfile_analyzer
*analyzer
= fontfile_analyzers
;
1380 DWRITE_FONT_FACE_TYPE face
;
1386 *file_type
= DWRITE_FONT_FILE_TYPE_UNKNOWN
;
1387 *face_type
= DWRITE_FONT_FACE_TYPE_UNKNOWN
;
1391 hr
= (*analyzer
)(stream
, face_count
, file_type
, face_type
);
1401 *supported
= is_face_type_supported(*face_type
);
1405 HRESULT
opentype_try_get_font_table(const struct file_stream_desc
*stream_desc
, UINT32 tag
, const void **table_data
,
1406 void **table_context
, UINT32
*table_size
, BOOL
*found
)
1408 void *table_directory_context
, *sfnt_context
;
1409 TT_TableRecord
*table_record
= NULL
;
1410 TTC_SFNT_V1
*font_header
= NULL
;
1411 UINT32 table_offset
= 0;
1415 if (found
) *found
= FALSE
;
1416 if (table_size
) *table_size
= 0;
1419 *table_context
= NULL
;
1421 if (stream_desc
->face_type
== DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION
) {
1422 const TTC_Header_V1
*ttc_header
;
1424 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&ttc_header
, 0, sizeof(*ttc_header
), &ttc_context
);
1425 if (SUCCEEDED(hr
)) {
1426 if (stream_desc
->face_index
>= GET_BE_DWORD(ttc_header
->numFonts
))
1429 table_offset
= GET_BE_DWORD(ttc_header
->OffsetTable
[stream_desc
->face_index
]);
1430 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&font_header
, table_offset
, sizeof(*font_header
), &sfnt_context
);
1432 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, ttc_context
);
1436 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void**)&font_header
, 0, sizeof(*font_header
), &sfnt_context
);
1441 table_count
= GET_BE_WORD(font_header
->numTables
);
1442 table_offset
+= sizeof(*font_header
);
1444 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, sfnt_context
);
1446 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, (const void **)&table_record
, table_offset
,
1447 table_count
* sizeof(*table_record
), &table_directory_context
);
1451 for (i
= 0; i
< table_count
; i
++) {
1452 if (table_record
->tag
== tag
) {
1453 UINT32 offset
= GET_BE_DWORD(table_record
->offset
);
1454 UINT32 length
= GET_BE_DWORD(table_record
->length
);
1459 *table_size
= length
;
1460 hr
= IDWriteFontFileStream_ReadFileFragment(stream_desc
->stream
, table_data
, offset
,
1461 length
, table_context
);
1467 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, table_directory_context
);
1473 static HRESULT
opentype_get_font_table(const struct file_stream_desc
*stream_desc
, UINT32 tag
,
1474 struct dwrite_fonttable
*table
)
1476 return opentype_try_get_font_table(stream_desc
, tag
, (const void **)&table
->data
, &table
->context
, &table
->size
, &table
->exists
);
1483 static unsigned int opentype_cmap_get_unicode_ranges_count(const struct dwrite_fonttable
*cmap
)
1485 unsigned int i
, num_tables
, count
= 0;
1486 const struct cmap_header
*header
;
1488 num_tables
= table_read_be_word(cmap
, FIELD_OFFSET(struct cmap_header
, num_tables
));
1489 header
= table_read_ensure(cmap
, 0, FIELD_OFFSET(struct cmap_header
, tables
[num_tables
]));
1494 for (i
= 0; i
< num_tables
; ++i
)
1496 unsigned int format
, offset
;
1498 if (GET_BE_WORD(header
->tables
[i
].platformID
) != 3)
1501 offset
= GET_BE_DWORD(header
->tables
[i
].offset
);
1502 format
= table_read_be_word(cmap
, offset
);
1506 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING
:
1508 count
+= table_read_be_word(cmap
, offset
+ FIELD_OFFSET(struct cmap_segment_mapping
, seg_count_x2
)) / 2;
1511 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE
:
1513 count
+= table_read_be_dword(cmap
, offset
+ FIELD_OFFSET(struct cmap_segmented_coverage
, num_groups
));
1517 FIXME("table format %u is not supported.\n", format
);
1524 HRESULT
opentype_cmap_get_unicode_ranges(const struct file_stream_desc
*stream_desc
, unsigned int max_count
,
1525 DWRITE_UNICODE_RANGE
*ranges
, unsigned int *count
)
1527 unsigned int i
, num_tables
, k
= 0;
1528 const struct cmap_header
*header
;
1529 struct dwrite_fonttable cmap
;
1531 opentype_get_font_table(stream_desc
, MS_CMAP_TAG
, &cmap
);
1536 *count
= opentype_cmap_get_unicode_ranges_count(&cmap
);
1538 num_tables
= table_read_be_word(&cmap
, FIELD_OFFSET(struct cmap_header
, num_tables
));
1539 header
= table_read_ensure(&cmap
, 0, FIELD_OFFSET(struct cmap_header
, tables
[num_tables
]));
1543 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, cmap
.context
);
1547 for (i
= 0; i
< num_tables
&& k
< max_count
; ++i
)
1549 unsigned int j
, offset
, format
;
1551 if (GET_BE_WORD(header
->tables
[i
].platformID
) != 3)
1554 offset
= GET_BE_DWORD(header
->tables
[i
].offset
);
1556 format
= table_read_be_word(&cmap
, offset
);
1559 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING
:
1561 unsigned int segment_count
= table_read_be_word(&cmap
, offset
+
1562 FIELD_OFFSET(struct cmap_segment_mapping
, seg_count_x2
)) / 2;
1563 const UINT16
*start_code
= table_read_ensure(&cmap
, offset
,
1564 FIELD_OFFSET(struct cmap_segment_mapping
, end_code
[segment_count
]) +
1565 2 /* reservedPad */ +
1566 2 * segment_count
/* start code array */);
1567 const UINT16
*end_code
= table_read_ensure(&cmap
, offset
,
1568 FIELD_OFFSET(struct cmap_segment_mapping
, end_code
[segment_count
]));
1570 if (!start_code
|| !end_code
)
1573 for (j
= 0; j
< segment_count
&& GET_BE_WORD(end_code
[j
]) != 0xffff && k
< max_count
; ++j
, ++k
)
1575 ranges
[k
].first
= GET_BE_WORD(start_code
[j
]);
1576 ranges
[k
].last
= GET_BE_WORD(end_code
[j
]);
1580 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE
:
1582 unsigned int num_groups
= table_read_be_dword(&cmap
, offset
+
1583 FIELD_OFFSET(struct cmap_segmented_coverage
, num_groups
));
1584 const struct cmap_segmented_coverage
*coverage
;
1586 coverage
= table_read_ensure(&cmap
, offset
,
1587 FIELD_OFFSET(struct cmap_segmented_coverage
, groups
[num_groups
]));
1589 for (j
= 0; j
< num_groups
&& k
< max_count
; j
++, k
++)
1591 ranges
[k
].first
= GET_BE_DWORD(coverage
->groups
[j
].startCharCode
);
1592 ranges
[k
].last
= GET_BE_DWORD(coverage
->groups
[j
].endCharCode
);
1597 FIXME("table format %u unhandled.\n", format
);
1601 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, cmap
.context
);
1603 return *count
> max_count
? E_NOT_SUFFICIENT_BUFFER
: S_OK
;
1606 void opentype_get_font_typo_metrics(struct file_stream_desc
*stream_desc
, unsigned int *ascent
, unsigned int *descent
)
1608 struct dwrite_fonttable os2
;
1609 const TT_OS2_V2
*data
;
1611 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
1612 data
= (const TT_OS2_V2
*)os2
.data
;
1614 *ascent
= *descent
= 0;
1616 if (os2
.size
>= FIELD_OFFSET(TT_OS2_V2
, sTypoLineGap
))
1618 SHORT value
= GET_BE_WORD(data
->sTypoDescender
);
1619 *ascent
= GET_BE_WORD(data
->sTypoAscender
);
1620 *descent
= value
< 0 ? -value
: 0;
1624 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
1627 void opentype_get_font_metrics(struct file_stream_desc
*stream_desc
, DWRITE_FONT_METRICS1
*metrics
, DWRITE_CARET_METRICS
*caret
)
1629 struct dwrite_fonttable os2
, head
, post
, hhea
;
1630 const TT_OS2_V2
*tt_os2
;
1631 const TT_HEAD
*tt_head
;
1632 const TT_POST
*tt_post
;
1633 const TT_HHEA
*tt_hhea
;
1635 memset(metrics
, 0, sizeof(*metrics
));
1637 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
1638 opentype_get_font_table(stream_desc
, MS_HEAD_TAG
, &head
);
1639 opentype_get_font_table(stream_desc
, MS_POST_TAG
, &post
);
1640 opentype_get_font_table(stream_desc
, MS_HHEA_TAG
, &hhea
);
1642 tt_head
= (const TT_HEAD
*)head
.data
;
1643 tt_os2
= (const TT_OS2_V2
*)os2
.data
;
1644 tt_post
= (const TT_POST
*)post
.data
;
1645 tt_hhea
= (const TT_HHEA
*)hhea
.data
;
1648 metrics
->designUnitsPerEm
= GET_BE_WORD(tt_head
->unitsPerEm
);
1649 metrics
->glyphBoxLeft
= GET_BE_WORD(tt_head
->xMin
);
1650 metrics
->glyphBoxTop
= GET_BE_WORD(tt_head
->yMax
);
1651 metrics
->glyphBoxRight
= GET_BE_WORD(tt_head
->xMax
);
1652 metrics
->glyphBoxBottom
= GET_BE_WORD(tt_head
->yMin
);
1658 caret
->slopeRise
= GET_BE_WORD(tt_hhea
->caretSlopeRise
);
1659 caret
->slopeRun
= GET_BE_WORD(tt_hhea
->caretSlopeRun
);
1660 caret
->offset
= GET_BE_WORD(tt_hhea
->caretOffset
);
1663 caret
->slopeRise
= 0;
1664 caret
->slopeRun
= 0;
1670 USHORT version
= GET_BE_WORD(tt_os2
->version
);
1672 metrics
->ascent
= GET_BE_WORD(tt_os2
->usWinAscent
);
1673 /* Some fonts have usWinDescent value stored as signed short, which could be wrongly
1674 interpreted as large unsigned value. */
1675 metrics
->descent
= abs((SHORT
)GET_BE_WORD(tt_os2
->usWinDescent
));
1677 /* line gap is estimated using two sets of ascender/descender values and 'hhea' line gap */
1679 SHORT descender
= (SHORT
)GET_BE_WORD(tt_hhea
->descender
);
1682 linegap
= GET_BE_WORD(tt_hhea
->ascender
) + abs(descender
) + GET_BE_WORD(tt_hhea
->linegap
) -
1683 metrics
->ascent
- metrics
->descent
;
1684 metrics
->lineGap
= linegap
> 0 ? linegap
: 0;
1687 metrics
->strikethroughPosition
= GET_BE_WORD(tt_os2
->yStrikeoutPosition
);
1688 metrics
->strikethroughThickness
= GET_BE_WORD(tt_os2
->yStrikeoutSize
);
1689 metrics
->subscriptPositionX
= GET_BE_WORD(tt_os2
->ySubscriptXOffset
);
1690 /* Y offset is stored as positive offset below baseline */
1691 metrics
->subscriptPositionY
= -GET_BE_WORD(tt_os2
->ySubscriptYOffset
);
1692 metrics
->subscriptSizeX
= GET_BE_WORD(tt_os2
->ySubscriptXSize
);
1693 metrics
->subscriptSizeY
= GET_BE_WORD(tt_os2
->ySubscriptYSize
);
1694 metrics
->superscriptPositionX
= GET_BE_WORD(tt_os2
->ySuperscriptXOffset
);
1695 metrics
->superscriptPositionY
= GET_BE_WORD(tt_os2
->ySuperscriptYOffset
);
1696 metrics
->superscriptSizeX
= GET_BE_WORD(tt_os2
->ySuperscriptXSize
);
1697 metrics
->superscriptSizeY
= GET_BE_WORD(tt_os2
->ySuperscriptYSize
);
1699 /* version 2 fields */
1701 metrics
->capHeight
= GET_BE_WORD(tt_os2
->sCapHeight
);
1702 metrics
->xHeight
= GET_BE_WORD(tt_os2
->sxHeight
);
1705 if (GET_BE_WORD(tt_os2
->fsSelection
) & OS2_FSSELECTION_USE_TYPO_METRICS
) {
1706 SHORT descent
= GET_BE_WORD(tt_os2
->sTypoDescender
);
1707 metrics
->ascent
= GET_BE_WORD(tt_os2
->sTypoAscender
);
1708 metrics
->descent
= descent
< 0 ? -descent
: 0;
1709 metrics
->lineGap
= GET_BE_WORD(tt_os2
->sTypoLineGap
);
1710 metrics
->hasTypographicMetrics
= TRUE
;
1714 metrics
->strikethroughPosition
= metrics
->designUnitsPerEm
/ 3;
1716 metrics
->ascent
= GET_BE_WORD(tt_hhea
->ascender
);
1717 metrics
->descent
= abs((SHORT
)GET_BE_WORD(tt_hhea
->descender
));
1722 metrics
->underlinePosition
= GET_BE_WORD(tt_post
->underlinePosition
);
1723 metrics
->underlineThickness
= GET_BE_WORD(tt_post
->underlineThickness
);
1726 if (metrics
->underlineThickness
== 0)
1727 metrics
->underlineThickness
= metrics
->designUnitsPerEm
/ 14;
1728 if (metrics
->strikethroughThickness
== 0)
1729 metrics
->strikethroughThickness
= metrics
->underlineThickness
;
1731 /* estimate missing metrics */
1732 if (metrics
->xHeight
== 0)
1733 metrics
->xHeight
= metrics
->designUnitsPerEm
/ 2;
1734 if (metrics
->capHeight
== 0)
1735 metrics
->capHeight
= metrics
->designUnitsPerEm
* 7 / 10;
1738 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
1740 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, head
.context
);
1742 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, post
.context
);
1744 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, hhea
.context
);
1747 void opentype_get_font_properties(struct file_stream_desc
*stream_desc
, struct dwrite_font_props
*props
)
1749 struct dwrite_fonttable os2
, head
, colr
, cpal
;
1750 BOOL is_symbol
, is_monospaced
;
1751 const TT_OS2_V2
*tt_os2
;
1752 const TT_HEAD
*tt_head
;
1754 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
1755 opentype_get_font_table(stream_desc
, MS_HEAD_TAG
, &head
);
1757 tt_os2
= (const TT_OS2_V2
*)os2
.data
;
1758 tt_head
= (const TT_HEAD
*)head
.data
;
1760 /* default stretch, weight and style to normal */
1761 props
->stretch
= DWRITE_FONT_STRETCH_NORMAL
;
1762 props
->weight
= DWRITE_FONT_WEIGHT_NORMAL
;
1763 props
->style
= DWRITE_FONT_STYLE_NORMAL
;
1764 memset(&props
->panose
, 0, sizeof(props
->panose
));
1765 memset(&props
->fontsig
, 0, sizeof(props
->fontsig
));
1766 memset(&props
->lf
, 0, sizeof(props
->lf
));
1769 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
1772 USHORT version
= GET_BE_WORD(tt_os2
->version
);
1773 USHORT fsSelection
= GET_BE_WORD(tt_os2
->fsSelection
);
1774 USHORT usWeightClass
= GET_BE_WORD(tt_os2
->usWeightClass
);
1775 USHORT usWidthClass
= GET_BE_WORD(tt_os2
->usWidthClass
);
1777 if (usWidthClass
> DWRITE_FONT_STRETCH_UNDEFINED
&& usWidthClass
<= DWRITE_FONT_STRETCH_ULTRA_EXPANDED
)
1778 props
->stretch
= usWidthClass
;
1780 if (usWeightClass
>= 1 && usWeightClass
<= 9)
1781 usWeightClass
*= 100;
1783 if (usWeightClass
> DWRITE_FONT_WEIGHT_ULTRA_BLACK
)
1784 props
->weight
= DWRITE_FONT_WEIGHT_ULTRA_BLACK
;
1785 else if (usWeightClass
> 0)
1786 props
->weight
= usWeightClass
;
1788 if (version
>= 4 && (fsSelection
& OS2_FSSELECTION_OBLIQUE
))
1789 props
->style
= DWRITE_FONT_STYLE_OBLIQUE
;
1790 else if (fsSelection
& OS2_FSSELECTION_ITALIC
)
1791 props
->style
= DWRITE_FONT_STYLE_ITALIC
;
1792 props
->lf
.lfItalic
= !!(fsSelection
& OS2_FSSELECTION_ITALIC
);
1794 memcpy(&props
->panose
, &tt_os2
->panose
, sizeof(props
->panose
));
1797 props
->fontsig
.fsUsb
[0] = GET_BE_DWORD(tt_os2
->ulUnicodeRange1
);
1798 props
->fontsig
.fsUsb
[1] = GET_BE_DWORD(tt_os2
->ulUnicodeRange2
);
1799 props
->fontsig
.fsUsb
[2] = GET_BE_DWORD(tt_os2
->ulUnicodeRange3
);
1800 props
->fontsig
.fsUsb
[3] = GET_BE_DWORD(tt_os2
->ulUnicodeRange4
);
1804 props
->fontsig
.fsCsb
[0] = GET_BE_DWORD(tt_os2
->ulCodePageRange1
);
1805 props
->fontsig
.fsCsb
[1] = GET_BE_DWORD(tt_os2
->ulCodePageRange2
);
1809 USHORT macStyle
= GET_BE_WORD(tt_head
->macStyle
);
1811 if (macStyle
& TT_HEAD_MACSTYLE_CONDENSED
)
1812 props
->stretch
= DWRITE_FONT_STRETCH_CONDENSED
;
1813 else if (macStyle
& TT_HEAD_MACSTYLE_EXTENDED
)
1814 props
->stretch
= DWRITE_FONT_STRETCH_EXPANDED
;
1816 if (macStyle
& TT_HEAD_MACSTYLE_BOLD
)
1817 props
->weight
= DWRITE_FONT_WEIGHT_BOLD
;
1819 if (macStyle
& TT_HEAD_MACSTYLE_ITALIC
) {
1820 props
->style
= DWRITE_FONT_STYLE_ITALIC
;
1821 props
->lf
.lfItalic
= 1;
1825 props
->lf
.lfWeight
= props
->weight
;
1827 /* FONT_IS_SYMBOL */
1828 if (!(is_symbol
= props
->panose
.familyKind
== DWRITE_PANOSE_FAMILY_SYMBOL
))
1830 struct dwrite_fonttable cmap
;
1831 int i
, offset
, num_tables
;
1833 opentype_get_font_table(stream_desc
, MS_CMAP_TAG
, &cmap
);
1837 num_tables
= table_read_be_word(&cmap
, FIELD_OFFSET(struct cmap_header
, num_tables
));
1838 offset
= FIELD_OFFSET(struct cmap_header
, tables
);
1840 for (i
= 0; !is_symbol
&& i
< num_tables
; ++i
)
1842 WORD platform
, encoding
;
1844 platform
= table_read_be_word(&cmap
, offset
+ i
* sizeof(struct cmap_encoding_record
) +
1845 FIELD_OFFSET(struct cmap_encoding_record
, platformID
));
1846 encoding
= table_read_be_word(&cmap
, offset
+ i
* sizeof(struct cmap_encoding_record
) +
1847 FIELD_OFFSET(struct cmap_encoding_record
, encodingID
));
1849 is_symbol
= platform
== OPENTYPE_CMAP_TABLE_PLATFORM_WIN
&&
1850 encoding
== OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL
;
1853 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, cmap
.context
);
1857 props
->flags
|= FONT_IS_SYMBOL
;
1859 /* FONT_IS_MONOSPACED */
1860 if (!(is_monospaced
= props
->panose
.text
.proportion
== DWRITE_PANOSE_PROPORTION_MONOSPACED
))
1862 struct dwrite_fonttable post
;
1864 opentype_get_font_table(stream_desc
, MS_POST_TAG
, &post
);
1868 is_monospaced
= !!table_read_dword(&post
, FIELD_OFFSET(TT_POST
, fixed_pitch
));
1870 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, post
.context
);
1874 props
->flags
|= FONT_IS_MONOSPACED
;
1876 /* FONT_IS_COLORED */
1877 opentype_get_font_table(stream_desc
, MS_COLR_TAG
, &colr
);
1880 opentype_get_font_table(stream_desc
, MS_CPAL_TAG
, &cpal
);
1883 props
->flags
|= FONT_IS_COLORED
;
1884 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, cpal
.context
);
1887 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, colr
.context
);
1890 TRACE("stretch=%d, weight=%d, style %d\n", props
->stretch
, props
->weight
, props
->style
);
1893 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
1895 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, head
.context
);
1898 static UINT
get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform
, USHORT encoding
)
1903 case OPENTYPE_PLATFORM_UNICODE
:
1905 case OPENTYPE_PLATFORM_MAC
:
1908 case TT_NAME_MAC_ENCODING_ROMAN
:
1911 case TT_NAME_MAC_ENCODING_JAPANESE
:
1914 case TT_NAME_MAC_ENCODING_TRAD_CHINESE
:
1917 case TT_NAME_MAC_ENCODING_KOREAN
:
1920 case TT_NAME_MAC_ENCODING_ARABIC
:
1923 case TT_NAME_MAC_ENCODING_HEBREW
:
1926 case TT_NAME_MAC_ENCODING_GREEK
:
1929 case TT_NAME_MAC_ENCODING_RUSSIAN
:
1932 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE
:
1935 case TT_NAME_MAC_ENCODING_THAI
:
1939 FIXME("encoding %u not handled, platform %d.\n", encoding
, platform
);
1943 case OPENTYPE_PLATFORM_WIN
:
1946 case TT_NAME_WINDOWS_ENCODING_SYMBOL
:
1947 case TT_NAME_WINDOWS_ENCODING_UCS2
:
1949 case TT_NAME_WINDOWS_ENCODING_SJIS
:
1952 case TT_NAME_WINDOWS_ENCODING_PRC
:
1955 case TT_NAME_WINDOWS_ENCODING_BIG5
:
1958 case TT_NAME_WINDOWS_ENCODING_WANSUNG
:
1961 case TT_NAME_WINDOWS_ENCODING_JOHAB
:
1965 FIXME("encoding %u not handled, platform %d.\n", encoding
, platform
);
1970 FIXME("unknown platform %d\n", platform
);
1976 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform
, USHORT lang_id
, WCHAR
*locale
, USHORT locale_len
)
1978 static const WCHAR enusW
[] = {'e','n','-','U','S',0};
1981 case OPENTYPE_PLATFORM_MAC
:
1983 const char *locale_name
= NULL
;
1985 if (lang_id
> TT_NAME_MAC_LANGID_AZER_ROMAN
)
1986 WARN("invalid mac lang id %d\n", lang_id
);
1987 else if (!name_mac_langid_to_locale
[lang_id
][0])
1988 FIXME("failed to map mac lang id %d to locale name\n", lang_id
);
1990 locale_name
= name_mac_langid_to_locale
[lang_id
];
1993 MultiByteToWideChar(CP_ACP
, 0, name_mac_langid_to_locale
[lang_id
], -1, locale
, locale_len
);
1995 strcpyW(locale
, enusW
);
1998 case OPENTYPE_PLATFORM_WIN
:
1999 if (!LCIDToLocaleName(MAKELCID(lang_id
, SORT_DEFAULT
), locale
, locale_len
, 0)) {
2000 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id
, SORT_DEFAULT
));
2001 strcpyW(locale
, enusW
);
2004 case OPENTYPE_PLATFORM_UNICODE
:
2005 strcpyW(locale
, enusW
);
2008 FIXME("unknown platform %d\n", platform
);
2012 static BOOL
opentype_decode_namerecord(const TT_NAME_V0
*header
, BYTE
*storage_area
, USHORT recid
, IDWriteLocalizedStrings
*strings
)
2014 const TT_NameRecord
*record
= &header
->nameRecord
[recid
];
2015 USHORT lang_id
, length
, offset
, encoding
, platform
;
2018 platform
= GET_BE_WORD(record
->platformID
);
2019 lang_id
= GET_BE_WORD(record
->languageID
);
2020 length
= GET_BE_WORD(record
->length
);
2021 offset
= GET_BE_WORD(record
->offset
);
2022 encoding
= GET_BE_WORD(record
->encodingID
);
2024 if (lang_id
< 0x8000) {
2025 WCHAR locale
[LOCALE_NAME_MAX_LENGTH
];
2029 codepage
= get_name_record_codepage(platform
, encoding
);
2030 get_name_record_locale(platform
, lang_id
, locale
, ARRAY_SIZE(locale
));
2033 DWORD len
= MultiByteToWideChar(codepage
, 0, (LPSTR
)(storage_area
+ offset
), length
, NULL
, 0);
2034 name_string
= heap_alloc(sizeof(WCHAR
) * (len
+1));
2035 MultiByteToWideChar(codepage
, 0, (LPSTR
)(storage_area
+ offset
), length
, name_string
, len
);
2036 name_string
[len
] = 0;
2041 length
/= sizeof(WCHAR
);
2042 name_string
= heap_strdupnW((LPWSTR
)(storage_area
+ offset
), length
);
2043 for (i
= 0; i
< length
; i
++)
2044 name_string
[i
] = GET_BE_WORD(name_string
[i
]);
2047 TRACE("string %s for locale %s found\n", debugstr_w(name_string
), debugstr_w(locale
));
2048 add_localizedstring(strings
, locale
, name_string
);
2049 heap_free(name_string
);
2053 FIXME("handle NAME format 1\n");
2058 static HRESULT
opentype_get_font_strings_from_id(const void *table_data
, enum OPENTYPE_STRING_ID id
, IDWriteLocalizedStrings
**strings
)
2060 int i
, count
, candidate_mac
, candidate_unicode
;
2061 const TT_NAME_V0
*header
;
2062 BYTE
*storage_area
= 0;
2070 hr
= create_localizedstrings(strings
);
2071 if (FAILED(hr
)) return hr
;
2073 header
= table_data
;
2074 format
= GET_BE_WORD(header
->format
);
2081 FIXME("unsupported NAME format %d\n", format
);
2084 storage_area
= (LPBYTE
)table_data
+ GET_BE_WORD(header
->stringOffset
);
2085 count
= GET_BE_WORD(header
->count
);
2088 candidate_unicode
= candidate_mac
= -1;
2089 for (i
= 0; i
< count
; i
++) {
2090 const TT_NameRecord
*record
= &header
->nameRecord
[i
];
2093 if (GET_BE_WORD(record
->nameID
) != id
)
2096 platform
= GET_BE_WORD(record
->platformID
);
2099 /* Skip Unicode or Mac entries for now, fonts tend to duplicate those
2100 strings as WIN platform entries. If font does not have WIN entry for
2101 this id, we will use Mac or Unicode platform entry while assuming
2103 case OPENTYPE_PLATFORM_UNICODE
:
2104 if (candidate_unicode
== -1)
2105 candidate_unicode
= i
;
2107 case OPENTYPE_PLATFORM_MAC
:
2108 if (candidate_mac
== -1)
2111 case OPENTYPE_PLATFORM_WIN
:
2112 if (opentype_decode_namerecord(header
, storage_area
, i
, *strings
))
2116 FIXME("platform %i not supported\n", platform
);
2123 if (candidate_mac
!= -1)
2124 exists
= opentype_decode_namerecord(header
, storage_area
, candidate_mac
, *strings
);
2125 if (!exists
&& candidate_unicode
!= -1)
2126 exists
= opentype_decode_namerecord(header
, storage_area
, candidate_unicode
, *strings
);
2130 IDWriteLocalizedStrings_Release(*strings
);
2136 sort_localizedstrings(*strings
);
2138 return exists
? S_OK
: E_FAIL
;
2141 static WCHAR
*meta_get_lng_name(WCHAR
*str
, WCHAR
**ctx
)
2143 static const WCHAR delimW
[] = {',',' ',0};
2146 if (!str
) str
= *ctx
;
2147 while (*str
&& strchrW(delimW
, *str
)) str
++;
2148 if (!*str
) return NULL
;
2150 while (*str
&& !strchrW(delimW
, *str
)) str
++;
2151 if (*str
) *str
++ = 0;
2157 static HRESULT
opentype_get_font_strings_from_meta(const struct file_stream_desc
*stream_desc
,
2158 DWRITE_INFORMATIONAL_STRING_ID id
, IDWriteLocalizedStrings
**ret
)
2160 static const WCHAR emptyW
[] = { 0 };
2161 const struct meta_data_map
*maps
;
2162 IDWriteLocalizedStrings
*strings
;
2163 struct dwrite_fonttable meta
;
2164 DWORD version
, i
, count
, tag
;
2171 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG
:
2174 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG
:
2178 WARN("Unexpected id %d.\n", id
);
2182 if (FAILED(hr
= create_localizedstrings(&strings
)))
2185 opentype_get_font_table(stream_desc
, MS_META_TAG
, &meta
);
2189 version
= table_read_be_dword(&meta
, 0);
2192 WARN("Unexpected meta table version %d.\n", version
);
2196 count
= table_read_be_dword(&meta
, FIELD_OFFSET(struct meta_header
, data_maps_count
));
2197 if (!(maps
= table_read_ensure(&meta
, FIELD_OFFSET(struct meta_header
, maps
),
2198 count
* sizeof(struct meta_data_map
))))
2201 for (i
= 0; i
< count
; ++i
)
2205 if (maps
[i
].tag
== tag
&& maps
[i
].length
)
2207 DWORD length
= GET_BE_DWORD(maps
[i
].length
), j
;
2209 if ((data
= table_read_ensure(&meta
, GET_BE_DWORD(maps
[i
].offset
), length
)))
2211 WCHAR
*ptrW
= heap_alloc((length
+ 1) * sizeof(WCHAR
)), *ctx
, *token
;
2219 /* Data is stored in comma separated list, ASCII range only. */
2220 for (j
= 0; j
< length
; ++j
)
2224 token
= meta_get_lng_name(ptrW
, &ctx
);
2228 add_localizedstring(strings
, emptyW
, token
);
2229 token
= meta_get_lng_name(NULL
, &ctx
);
2237 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, meta
.context
);
2240 if (IDWriteLocalizedStrings_GetCount(strings
))
2243 IDWriteLocalizedStrings_Release(strings
);
2248 HRESULT
opentype_get_font_info_strings(const struct file_stream_desc
*stream_desc
, DWRITE_INFORMATIONAL_STRING_ID id
,
2249 IDWriteLocalizedStrings
**strings
)
2251 struct dwrite_fonttable name
;
2255 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG
:
2256 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG
:
2257 opentype_get_font_strings_from_meta(stream_desc
, id
, strings
);
2260 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2261 opentype_get_font_strings_from_id(name
.data
, dwriteid_to_opentypeid
[id
], strings
);
2263 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2269 /* FamilyName locating order is WWS Family Name -> Preferred Family Name -> Family Name. If font claims to
2270 have 'Preferred Family Name' in WWS format, then WWS name is not used. */
2271 HRESULT
opentype_get_font_familyname(struct file_stream_desc
*stream_desc
, IDWriteLocalizedStrings
**names
)
2273 struct dwrite_fonttable os2
, name
;
2274 const TT_OS2_V2
*tt_os2
;
2275 const void *name_table
;
2278 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
2279 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2281 tt_os2
= (const TT_OS2_V2
*)os2
.data
;
2282 name_table
= (const void *)name
.data
;
2286 /* if Preferred Family doesn't conform to WWS model try WWS name */
2287 if (tt_os2
&& !(GET_BE_WORD(tt_os2
->fsSelection
) & OS2_FSSELECTION_WWS
))
2288 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_WWS_FAMILY_NAME
, names
);
2293 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME
, names
);
2295 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_FAMILY_NAME
, names
);
2298 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2300 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2305 /* FaceName locating order is WWS Face Name -> Preferred Face Name -> Face Name. If font claims to
2306 have 'Preferred Face Name' in WWS format, then WWS name is not used. */
2307 HRESULT
opentype_get_font_facename(struct file_stream_desc
*stream_desc
, WCHAR
*lfname
, IDWriteLocalizedStrings
**names
)
2309 struct dwrite_fonttable os2
, name
;
2310 IDWriteLocalizedStrings
*lfnames
;
2311 const TT_OS2_V2
*tt_os2
;
2312 const void *name_table
;
2315 opentype_get_font_table(stream_desc
, MS_OS2_TAG
, &os2
);
2316 opentype_get_font_table(stream_desc
, MS_NAME_TAG
, &name
);
2318 tt_os2
= (const TT_OS2_V2
*)os2
.data
;
2319 name_table
= name
.data
;
2323 /* if Preferred Family doesn't conform to WWS model try WWS name */
2324 if (tt_os2
&& !(GET_BE_WORD(tt_os2
->fsSelection
) & OS2_FSSELECTION_WWS
))
2325 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_WWS_SUBFAMILY_NAME
, names
);
2330 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME
, names
);
2332 hr
= opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_SUBFAMILY_NAME
, names
);
2334 /* User locale is preferred, with fallback to en-us. */
2336 if (SUCCEEDED(opentype_get_font_strings_from_id(name_table
, OPENTYPE_STRING_FAMILY_NAME
, &lfnames
))) {
2337 static const WCHAR enusW
[] = {'e','n','-','u','s',0};
2338 WCHAR localeW
[LOCALE_NAME_MAX_LENGTH
];
2343 if (GetSystemDefaultLocaleName(localeW
, ARRAY_SIZE(localeW
)))
2344 IDWriteLocalizedStrings_FindLocaleName(lfnames
, localeW
, &index
, &exists
);
2347 IDWriteLocalizedStrings_FindLocaleName(lfnames
, enusW
, &index
, &exists
);
2353 IDWriteLocalizedStrings_GetStringLength(lfnames
, index
, &length
);
2354 nameW
= heap_alloc((length
+ 1) * sizeof(WCHAR
));
2357 IDWriteLocalizedStrings_GetString(lfnames
, index
, nameW
, length
+ 1);
2358 lstrcpynW(lfname
, nameW
, LF_FACESIZE
);
2363 IDWriteLocalizedStrings_Release(lfnames
);
2367 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, os2
.context
);
2369 IDWriteFontFileStream_ReleaseFileFragment(stream_desc
->stream
, name
.context
);
2374 static inline const struct ot_script
*opentype_get_script(const struct ot_script_list
*scriptlist
, UINT32 scripttag
)
2378 for (j
= 0; j
< GET_BE_WORD(scriptlist
->script_count
); j
++) {
2379 const char *tag
= scriptlist
->scripts
[j
].tag
;
2380 if (scripttag
== DWRITE_MAKE_OPENTYPE_TAG(tag
[0], tag
[1], tag
[2], tag
[3]))
2381 return (struct ot_script
*)((BYTE
*)scriptlist
+ GET_BE_WORD(scriptlist
->scripts
[j
].script
));
2387 static inline const struct ot_langsys
*opentype_get_langsys(const struct ot_script
*script
, UINT32 languagetag
)
2391 for (j
= 0; j
< GET_BE_WORD(script
->langsys_count
); j
++) {
2392 const char *tag
= script
->langsys
[j
].tag
;
2393 if (languagetag
== DWRITE_MAKE_OPENTYPE_TAG(tag
[0], tag
[1], tag
[2], tag
[3]))
2394 return (struct ot_langsys
*)((BYTE
*)script
+ GET_BE_WORD(script
->langsys
[j
].langsys
));
2400 static void opentype_add_font_features(const struct gpos_gsub_header
*header
, const struct ot_langsys
*langsys
,
2401 UINT32 max_tagcount
, UINT32
*count
, DWRITE_FONT_FEATURE_TAG
*tags
)
2403 const struct ot_feature_list
*features
= (const struct ot_feature_list
*)((const BYTE
*)header
+ GET_BE_WORD(header
->feature_list
));
2406 for (j
= 0; j
< GET_BE_WORD(langsys
->feature_count
); j
++) {
2407 const struct ot_feature_record
*feature
= &features
->features
[langsys
->feature_index
[j
]];
2409 if (*count
< max_tagcount
)
2410 tags
[*count
] = GET_BE_DWORD(feature
->tag
);
2416 HRESULT
opentype_get_typographic_features(IDWriteFontFace
*fontface
, UINT32 scripttag
, UINT32 languagetag
, UINT32 max_tagcount
,
2417 UINT32
*count
, DWRITE_FONT_FEATURE_TAG
*tags
)
2419 UINT32 tables
[2] = { MS_GSUB_TAG
, MS_GPOS_TAG
};
2424 for (i
= 0; i
< ARRAY_SIZE(tables
); i
++) {
2425 const struct ot_script_list
*scriptlist
;
2426 const struct gpos_gsub_header
*header
;
2427 const struct ot_script
*script
;
2434 hr
= IDWriteFontFace_TryGetFontTable(fontface
, tables
[i
], &ptr
, &size
, &context
, &exists
);
2441 header
= (const struct gpos_gsub_header
*)ptr
;
2442 scriptlist
= (const struct ot_script_list
*)((const BYTE
*)header
+ GET_BE_WORD(header
->script_list
));
2444 script
= opentype_get_script(scriptlist
, scripttag
);
2446 const struct ot_langsys
*langsys
= opentype_get_langsys(script
, languagetag
);
2448 opentype_add_font_features(header
, langsys
, max_tagcount
, count
, tags
);
2451 IDWriteFontFace_ReleaseFontTable(fontface
, context
);
2454 return *count
> max_tagcount
? E_NOT_SUFFICIENT_BUFFER
: S_OK
;
2457 static unsigned int find_vdmx_group(const struct vdmx_header
*hdr
)
2460 const struct vdmx_ratio
*ratios
= (struct vdmx_ratio
*)(hdr
+ 1);
2461 BYTE dev_x_ratio
= 1, dev_y_ratio
= 1;
2462 unsigned int group_offset
= 0;
2464 num_ratios
= GET_BE_WORD(hdr
->num_ratios
);
2466 for (i
= 0; i
< num_ratios
; i
++) {
2468 if (!ratios
[i
].bCharSet
) continue;
2470 if ((ratios
[i
].xRatio
== 0 && ratios
[i
].yStartRatio
== 0 &&
2471 ratios
[i
].yEndRatio
== 0) ||
2472 (ratios
[i
].xRatio
== dev_x_ratio
&& ratios
[i
].yStartRatio
<= dev_y_ratio
&&
2473 ratios
[i
].yEndRatio
>= dev_y_ratio
))
2475 group_offset
= GET_BE_WORD(*((WORD
*)(ratios
+ num_ratios
) + i
));
2480 return group_offset
;
2483 BOOL
opentype_get_vdmx_size(const struct dwrite_fonttable
*vdmx
, INT emsize
, UINT16
*ascent
, UINT16
*descent
)
2485 unsigned int num_ratios
, num_recs
, group_offset
, i
;
2486 const struct vdmx_header
*header
;
2487 const struct vdmx_group
*group
;
2492 num_ratios
= table_read_be_word(vdmx
, FIELD_OFFSET(struct vdmx_header
, num_ratios
));
2493 num_recs
= table_read_be_word(vdmx
, FIELD_OFFSET(struct vdmx_header
, num_recs
));
2495 header
= table_read_ensure(vdmx
, 0, sizeof(*header
) + num_ratios
* sizeof(struct vdmx_ratio
) +
2496 num_recs
* sizeof(*group
));
2501 group_offset
= find_vdmx_group(header
);
2505 num_recs
= table_read_be_word(vdmx
, group_offset
);
2506 group
= table_read_ensure(vdmx
, group_offset
, FIELD_OFFSET(struct vdmx_group
, entries
[num_recs
]));
2511 if (emsize
< group
->startsz
|| emsize
>= group
->endsz
)
2514 for (i
= 0; i
< num_recs
; ++i
)
2516 WORD ppem
= GET_BE_WORD(group
->entries
[i
].yPelHeight
);
2517 if (ppem
> emsize
) {
2518 FIXME("interpolate %d\n", emsize
);
2522 if (ppem
== emsize
) {
2523 *ascent
= (SHORT
)GET_BE_WORD(group
->entries
[i
].yMax
);
2524 *descent
= -(SHORT
)GET_BE_WORD(group
->entries
[i
].yMin
);
2532 unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable
*gasp
, float emsize
)
2534 unsigned int version
, num_ranges
, i
;
2535 const struct gasp_header
*table
;
2541 num_ranges
= table_read_be_word(gasp
, FIELD_OFFSET(struct gasp_header
, num_ranges
));
2543 table
= table_read_ensure(gasp
, 0, FIELD_OFFSET(struct gasp_header
, ranges
[num_ranges
]));
2547 version
= GET_BE_WORD(table
->version
);
2550 ERR("Unsupported gasp table format version %u.\n", version
);
2554 for (i
= 0; i
< num_ranges
; ++i
)
2556 flags
= GET_BE_WORD(table
->ranges
[i
].flags
);
2557 if (emsize
<= GET_BE_WORD(table
->ranges
[i
].max_ppem
)) break;
2564 unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable
*cpal
)
2566 return table_read_be_word(cpal
, FIELD_OFFSET(struct cpal_header_0
, num_palettes
));
2569 unsigned int opentype_get_cpal_paletteentrycount(const struct dwrite_fonttable
*cpal
)
2571 return table_read_be_word(cpal
, FIELD_OFFSET(struct cpal_header_0
, num_palette_entries
));
2574 HRESULT
opentype_get_cpal_entries(const struct dwrite_fonttable
*cpal
, unsigned int palette
,
2575 unsigned int first_entry_index
, unsigned int entry_count
, DWRITE_COLOR_F
*entries
)
2577 unsigned int num_palettes
, num_palette_entries
, i
;
2578 const struct cpal_color_record
*records
;
2579 const struct cpal_header_0
*header
;
2581 header
= table_read_ensure(cpal
, 0, sizeof(*header
));
2583 if (!cpal
->exists
|| !header
)
2584 return DWRITE_E_NOCOLOR
;
2586 num_palettes
= GET_BE_WORD(header
->num_palettes
);
2587 if (palette
>= num_palettes
)
2588 return DWRITE_E_NOCOLOR
;
2590 header
= table_read_ensure(cpal
, 0, FIELD_OFFSET(struct cpal_header_0
, color_record_indices
[palette
]));
2592 return DWRITE_E_NOCOLOR
;
2594 num_palette_entries
= GET_BE_WORD(header
->num_palette_entries
);
2595 if (first_entry_index
+ entry_count
> num_palette_entries
)
2596 return E_INVALIDARG
;
2598 records
= table_read_ensure(cpal
, GET_BE_DWORD(header
->offset_first_color_record
),
2599 sizeof(*records
) * GET_BE_WORD(header
->num_color_records
));
2601 return DWRITE_E_NOCOLOR
;
2603 first_entry_index
+= GET_BE_WORD(header
->color_record_indices
[palette
]);
2605 for (i
= 0; i
< entry_count
; i
++) {
2606 entries
[i
].u1
.r
= records
[first_entry_index
+ i
].red
/ 255.0f
;
2607 entries
[i
].u2
.g
= records
[first_entry_index
+ i
].green
/ 255.0f
;
2608 entries
[i
].u3
.b
= records
[first_entry_index
+ i
].blue
/ 255.0f
;
2609 entries
[i
].u4
.a
= records
[first_entry_index
+ i
].alpha
/ 255.0f
;
2615 static int colr_compare_gid(const void *g
, const void *r
)
2617 const struct colr_baseglyph_record
*record
= r
;
2618 UINT16 glyph
= *(UINT16
*)g
, GID
= GET_BE_WORD(record
->glyph
);
2623 else if (glyph
< GID
)
2629 HRESULT
opentype_get_colr_glyph(const struct dwrite_fonttable
*colr
, UINT16 glyph
, struct dwrite_colorglyph
*ret
)
2631 unsigned int num_baseglyph_records
, offset_baseglyph_records
;
2632 const struct colr_baseglyph_record
*record
;
2633 const struct colr_layer_record
*layer
;
2634 const struct colr_header
*header
;
2636 memset(ret
, 0, sizeof(*ret
));
2638 ret
->palette_index
= 0xffff;
2640 header
= table_read_ensure(colr
, 0, sizeof(*header
));
2644 num_baseglyph_records
= GET_BE_WORD(header
->num_baseglyph_records
);
2645 offset_baseglyph_records
= GET_BE_DWORD(header
->offset_baseglyph_records
);
2646 if (!table_read_ensure(colr
, offset_baseglyph_records
, num_baseglyph_records
* sizeof(*record
)))
2651 record
= bsearch(&glyph
, colr
->data
+ offset_baseglyph_records
, num_baseglyph_records
,
2652 sizeof(*record
), colr_compare_gid
);
2656 ret
->first_layer
= GET_BE_WORD(record
->first_layer_index
);
2657 ret
->num_layers
= GET_BE_WORD(record
->num_layers
);
2659 if ((layer
= table_read_ensure(colr
, GET_BE_DWORD(header
->offset_layer_records
),
2660 (ret
->first_layer
+ ret
->layer
) * sizeof(*layer
))))
2662 layer
+= ret
->first_layer
+ ret
->layer
;
2663 ret
->glyph
= GET_BE_WORD(layer
->glyph
);
2664 ret
->palette_index
= GET_BE_WORD(layer
->palette_index
);
2670 void opentype_colr_next_glyph(const struct dwrite_fonttable
*colr
, struct dwrite_colorglyph
*glyph
)
2672 const struct colr_layer_record
*layer
;
2673 const struct colr_header
*header
;
2675 /* iterated all the way through */
2676 if (glyph
->layer
== glyph
->num_layers
)
2679 if (!(header
= table_read_ensure(colr
, 0, sizeof(*header
))))
2684 if ((layer
= table_read_ensure(colr
, GET_BE_DWORD(header
->offset_layer_records
),
2685 (glyph
->first_layer
+ glyph
->layer
) * sizeof(*layer
))))
2687 layer
+= glyph
->first_layer
+ glyph
->layer
;
2688 glyph
->glyph
= GET_BE_WORD(layer
->glyph
);
2689 glyph
->palette_index
= GET_BE_WORD(layer
->palette_index
);
2693 BOOL
opentype_has_vertical_variants(IDWriteFontFace5
*fontface
)
2695 const struct gpos_gsub_header
*header
;
2696 const struct ot_feature_list
*featurelist
;
2697 const struct ot_lookup_list
*lookup_list
;
2698 BOOL exists
= FALSE
, ret
= FALSE
;
2705 hr
= IDWriteFontFace5_TryGetFontTable(fontface
, MS_GSUB_TAG
, &data
, &size
, &context
, &exists
);
2706 if (FAILED(hr
) || !exists
)
2710 featurelist
= (struct ot_feature_list
*)((BYTE
*)header
+ GET_BE_WORD(header
->feature_list
));
2711 lookup_list
= (const struct ot_lookup_list
*)((BYTE
*)header
+ GET_BE_WORD(header
->lookup_list
));
2713 for (i
= 0; i
< GET_BE_WORD(featurelist
->feature_count
); i
++) {
2714 if (featurelist
->features
[i
].tag
== DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING
) {
2715 const struct ot_feature
*feature
= (const struct ot_feature
*)((BYTE
*)featurelist
+ GET_BE_WORD(featurelist
->features
[i
].offset
));
2716 UINT16 lookup_count
= GET_BE_WORD(feature
->lookup_count
), index
, count
, type
;
2717 const GSUB_SingleSubstFormat2
*subst2
;
2718 const struct ot_lookup_table
*lookup_table
;
2721 if (lookup_count
== 0)
2724 for (j
= 0; j
< lookup_count
; ++j
) {
2725 /* check if lookup is empty */
2726 index
= GET_BE_WORD(feature
->lookuplist_index
[j
]);
2727 lookup_table
= (const struct ot_lookup_table
*)((BYTE
*)lookup_list
+ GET_BE_WORD(lookup_list
->lookup
[index
]));
2729 type
= GET_BE_WORD(lookup_table
->lookup_type
);
2730 if (type
!= GSUB_LOOKUP_SINGLE_SUBST
&& type
!= GSUB_LOOKUP_EXTENSION_SUBST
)
2733 count
= GET_BE_WORD(lookup_table
->subtable_count
);
2737 offset
= GET_BE_WORD(lookup_table
->subtable
[0]);
2738 if (type
== GSUB_LOOKUP_EXTENSION_SUBST
) {
2739 const GSUB_ExtensionPosFormat1
*ext
= (const GSUB_ExtensionPosFormat1
*)((const BYTE
*)lookup_table
+ offset
);
2740 if (GET_BE_WORD(ext
->SubstFormat
) == 1)
2741 offset
+= GET_BE_DWORD(ext
->ExtensionOffset
);
2743 FIXME("Unhandled Extension Substitution Format %u\n", GET_BE_WORD(ext
->SubstFormat
));
2746 subst2
= (const GSUB_SingleSubstFormat2
*)((BYTE
*)lookup_table
+ offset
);
2747 index
= GET_BE_WORD(subst2
->SubstFormat
);
2749 FIXME("Validate Single Substitution Format 1\n");
2750 else if (index
== 2) {
2751 /* SimSun-ExtB has 0 glyph count for this substitution */
2752 if (GET_BE_WORD(subst2
->GlyphCount
) > 0) {
2758 WARN("Unknown Single Substitution Format, %u\n", index
);
2763 IDWriteFontFace5_ReleaseFontTable(fontface
, context
);
2768 static BOOL
opentype_has_font_table(IDWriteFontFace5
*fontface
, UINT32 tag
)
2770 BOOL exists
= FALSE
;
2776 hr
= IDWriteFontFace5_TryGetFontTable(fontface
, tag
, &data
, &size
, &context
, &exists
);
2781 IDWriteFontFace5_ReleaseFontTable(fontface
, context
);
2786 static unsigned int opentype_get_sbix_formats(IDWriteFontFace5
*fontface
)
2788 unsigned int num_strikes
, num_glyphs
, i
, j
, ret
= 0;
2789 const struct sbix_header
*sbix_header
;
2790 struct dwrite_fonttable table
;
2792 memset(&table
, 0, sizeof(table
));
2793 table
.exists
= TRUE
;
2795 if (!get_fontface_table(fontface
, MS_MAXP_TAG
, &table
))
2798 num_glyphs
= table_read_be_word(&table
, FIELD_OFFSET(struct maxp
, num_glyphs
));
2800 IDWriteFontFace5_ReleaseFontTable(fontface
, table
.context
);
2802 memset(&table
, 0, sizeof(table
));
2803 table
.exists
= TRUE
;
2805 if (!get_fontface_table(fontface
, MS_SBIX_TAG
, &table
))
2808 num_strikes
= table_read_be_dword(&table
, FIELD_OFFSET(struct sbix_header
, num_strikes
));
2809 sbix_header
= table_read_ensure(&table
, 0, FIELD_OFFSET(struct sbix_header
, strike_offset
[num_strikes
]));
2813 for (i
= 0; i
< num_strikes
; ++i
)
2815 unsigned int strike_offset
= GET_BE_DWORD(sbix_header
->strike_offset
[i
]);
2816 const struct sbix_strike
*strike
= table_read_ensure(&table
, strike_offset
,
2817 FIELD_OFFSET(struct sbix_strike
, glyphdata_offsets
[num_glyphs
+ 1]));
2822 for (j
= 0; j
< num_glyphs
; j
++)
2824 unsigned int offset
= GET_BE_DWORD(strike
->glyphdata_offsets
[j
]);
2825 unsigned int next_offset
= GET_BE_DWORD(strike
->glyphdata_offsets
[j
+ 1]);
2826 const struct sbix_glyph_data
*glyph_data
;
2828 if (offset
== next_offset
)
2831 glyph_data
= table_read_ensure(&table
, strike_offset
+ offset
, sizeof(*glyph_data
));
2835 switch (glyph_data
->graphic_type
)
2838 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PNG
;
2841 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_JPEG
;
2844 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_TIFF
;
2847 FIXME("unexpected bitmap format %s\n", debugstr_tag(GET_BE_DWORD(glyph_data
->graphic_type
)));
2853 IDWriteFontFace5_ReleaseFontTable(fontface
, table
.context
);
2858 static unsigned int opentype_get_cblc_formats(IDWriteFontFace5
*fontface
)
2860 const unsigned int format_mask
= DWRITE_GLYPH_IMAGE_FORMATS_PNG
|
2861 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8
;
2862 const struct cblc_bitmapsize_table
*sizes
;
2863 struct dwrite_fonttable cblc
= { 0 };
2864 unsigned int num_sizes
, i
, ret
= 0;
2865 const struct cblc_header
*header
;
2868 if (!get_fontface_table(fontface
, MS_CBLC_TAG
, &cblc
))
2871 num_sizes
= table_read_be_dword(&cblc
, FIELD_OFFSET(struct cblc_header
, num_sizes
));
2872 sizes
= table_read_ensure(&cblc
, sizeof(*header
), num_sizes
* sizeof(*sizes
));
2876 for (i
= 0; i
< num_sizes
; ++i
)
2878 BYTE bpp
= sizes
[i
].bit_depth
;
2880 if ((ret
& format_mask
) == format_mask
)
2883 if (bpp
== 1 || bpp
== 2 || bpp
== 4 || bpp
== 8)
2884 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PNG
;
2886 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8
;
2890 IDWriteFontFace5_ReleaseFontTable(fontface
, cblc
.context
);
2895 UINT32
opentype_get_glyph_image_formats(IDWriteFontFace5
*fontface
)
2897 UINT32 ret
= DWRITE_GLYPH_IMAGE_FORMATS_NONE
;
2899 if (opentype_has_font_table(fontface
, MS_GLYF_TAG
))
2900 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
;
2902 if (opentype_has_font_table(fontface
, MS_CFF__TAG
) ||
2903 opentype_has_font_table(fontface
, MS_CFF2_TAG
))
2904 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_CFF
;
2906 if (opentype_has_font_table(fontface
, MS_COLR_TAG
))
2907 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_COLR
;
2909 if (opentype_has_font_table(fontface
, MS_SVG__TAG
))
2910 ret
|= DWRITE_GLYPH_IMAGE_FORMATS_SVG
;
2912 if (opentype_has_font_table(fontface
, MS_SBIX_TAG
))
2913 ret
|= opentype_get_sbix_formats(fontface
);
2915 if (opentype_has_font_table(fontface
, MS_CBLC_TAG
))
2916 ret
|= opentype_get_cblc_formats(fontface
);
2921 DWRITE_CONTAINER_TYPE
opentype_analyze_container_type(void const *data
, UINT32 data_size
)
2925 if (data_size
< sizeof(DWORD
))
2926 return DWRITE_CONTAINER_TYPE_UNKNOWN
;
2928 /* Both WOFF and WOFF2 start with 4 bytes signature. */
2929 signature
= *(DWORD
*)data
;
2934 return DWRITE_CONTAINER_TYPE_WOFF
;
2936 return DWRITE_CONTAINER_TYPE_WOFF2
;
2938 return DWRITE_CONTAINER_TYPE_UNKNOWN
;
2942 void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache
*cache
)
2944 cache
->font
->grab_font_table(cache
->context
, MS_GPOS_TAG
, &cache
->gpos
.table
.data
, &cache
->gpos
.table
.size
,
2945 &cache
->gpos
.table
.context
);
2947 if (cache
->gpos
.table
.data
)
2949 cache
->gpos
.script_list
= table_read_be_word(&cache
->gpos
.table
,
2950 FIELD_OFFSET(struct gpos_gsub_header
, script_list
));
2951 cache
->gpos
.feature_list
= table_read_be_word(&cache
->gpos
.table
,
2952 FIELD_OFFSET(struct gpos_gsub_header
, feature_list
));
2953 cache
->gpos
.lookup_list
= table_read_be_word(&cache
->gpos
.table
,
2954 FIELD_OFFSET(struct gpos_gsub_header
, lookup_list
));
2957 cache
->font
->grab_font_table(cache
->context
, MS_GDEF_TAG
, &cache
->gdef
.table
.data
, &cache
->gdef
.table
.size
,
2958 &cache
->gdef
.table
.context
);
2960 if (cache
->gdef
.table
.data
)
2961 cache
->gdef
.classdef
= table_read_be_word(&cache
->gdef
.table
, FIELD_OFFSET(struct gdef_header
, classdef
));
2964 DWORD
opentype_layout_find_script(const struct scriptshaping_cache
*cache
, DWORD kind
, DWORD script
,
2965 unsigned int *script_index
)
2970 *script_index
= ~0u;
2972 if (kind
!= MS_GPOS_TAG
)
2975 script_count
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
);
2979 for (i
= 0; i
< script_count
; i
++)
2981 DWORD tag
= table_read_dword(&cache
->gpos
.table
, cache
->gpos
.script_list
+
2982 FIELD_OFFSET(struct ot_script_list
, scripts
) + i
* sizeof(struct ot_script_record
));
2996 DWORD
opentype_layout_find_language(const struct scriptshaping_cache
*cache
, DWORD kind
, DWORD language
,
2997 unsigned int script_index
, unsigned int *language_index
)
2999 WORD table_offset
, lang_count
;
3002 *language_index
= ~0u;
3004 if (kind
!= MS_GPOS_TAG
)
3007 table_offset
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
+
3008 FIELD_OFFSET(struct ot_script_list
, scripts
) + script_index
* sizeof(struct ot_script_record
) +
3009 FIELD_OFFSET(struct ot_script_record
, script
));
3013 lang_count
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
+ table_offset
+
3014 FIELD_OFFSET(struct ot_script
, langsys_count
));
3015 for (i
= 0; i
< lang_count
; i
++)
3017 DWORD tag
= table_read_dword(&cache
->gpos
.table
, cache
->gpos
.script_list
+ table_offset
+
3018 FIELD_OFFSET(struct ot_script
, langsys
) + i
* sizeof(struct ot_langsys_record
));
3020 if (tag
== language
)
3022 *language_index
= i
;
3027 /* Try 'defaultLangSys' if it's set. */
3028 if (table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
+ table_offset
))
3034 static int gdef_class_compare_format2(const void *g
, const void *r
)
3036 const struct ot_gdef_class_range
*range
= r
;
3037 UINT16 glyph
= *(UINT16
*)g
;
3039 if (glyph
< GET_BE_WORD(range
->start_glyph
))
3041 else if (glyph
> GET_BE_WORD(range
->end_glyph
))
3047 static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable
*table
,
3048 unsigned int offset
, UINT16 glyph
)
3050 WORD format
= table_read_be_word(table
, offset
), count
;
3051 unsigned int glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3055 const struct ot_gdef_classdef_format1
*format1
;
3057 count
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gdef_classdef_format1
, glyph_count
));
3058 format1
= table_read_ensure(table
, offset
, FIELD_OFFSET(struct ot_gdef_classdef_format1
, classes
[count
]));
3061 WORD start_glyph
= GET_BE_WORD(format1
->start_glyph
);
3062 if (glyph
>= start_glyph
&& (glyph
- start_glyph
) < count
)
3064 glyph_class
= GET_BE_WORD(format1
->classes
[glyph
- start_glyph
]);
3065 if (glyph_class
> GDEF_CLASS_MAX
)
3066 glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3070 else if (format
== 2)
3072 const struct ot_gdef_classdef_format2
*format2
;
3074 count
= table_read_be_word(table
, offset
+ FIELD_OFFSET(struct ot_gdef_classdef_format2
, range_count
));
3075 format2
= table_read_ensure(table
, offset
, FIELD_OFFSET(struct ot_gdef_classdef_format2
, ranges
[count
]));
3078 const struct ot_gdef_class_range
*range
= bsearch(&glyph
, format2
->ranges
, count
,
3079 sizeof(struct ot_gdef_class_range
), gdef_class_compare_format2
);
3080 glyph_class
= range
&& glyph
<= GET_BE_WORD(range
->end_glyph
) ?
3081 GET_BE_WORD(range
->glyph_class
) : GDEF_CLASS_UNCLASSIFIED
;
3082 if (glyph_class
> GDEF_CLASS_MAX
)
3083 glyph_class
= GDEF_CLASS_UNCLASSIFIED
;
3087 WARN("Unknown GDEF format %u.\n", format
);
3092 struct coverage_compare_format1_context
3095 const UINT16
*table_base
;
3096 unsigned int *coverage_index
;
3099 static int coverage_compare_format1(const void *left
, const void *right
)
3101 const struct coverage_compare_format1_context
*context
= left
;
3102 UINT16 glyph
= GET_BE_WORD(*(UINT16
*)right
);
3105 ret
= context
->glyph
- glyph
;
3107 *context
->coverage_index
= (UINT16
*)right
- context
->table_base
;
3112 static int coverage_compare_format2(const void *g
, const void *r
)
3114 const struct ot_coverage_range
*range
= r
;
3115 UINT16 glyph
= *(UINT16
*)g
;
3117 if (glyph
< GET_BE_WORD(range
->start_glyph
))
3119 else if (glyph
> GET_BE_WORD(range
->end_glyph
))
3125 static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable
*table
, DWORD coverage
,
3128 WORD format
= table_read_be_word(table
, coverage
), count
;
3130 count
= table_read_be_word(table
, coverage
+ 2);
3134 const struct ot_coverage_format1
*format1
= table_read_ensure(table
, coverage
,
3135 FIELD_OFFSET(struct ot_coverage_format1
, glyphs
[count
]));
3136 struct coverage_compare_format1_context context
;
3137 unsigned int coverage_index
= GLYPH_NOT_COVERED
;
3141 context
.glyph
= glyph
;
3142 context
.table_base
= format1
->glyphs
;
3143 context
.coverage_index
= &coverage_index
;
3145 bsearch(&context
, format1
->glyphs
, count
, sizeof(glyph
), coverage_compare_format1
);
3148 return coverage_index
;
3150 else if (format
== 2)
3152 const struct ot_coverage_format2
*format2
= table_read_ensure(table
, coverage
,
3153 FIELD_OFFSET(struct ot_coverage_format2
, ranges
[count
]));
3156 const struct ot_coverage_range
*range
= bsearch(&glyph
, format2
->ranges
, count
,
3157 sizeof(struct ot_coverage_range
), coverage_compare_format2
);
3158 return range
&& glyph
<= GET_BE_WORD(range
->end_glyph
) ?
3159 GET_BE_WORD(range
->startcoverage_index
) + glyph
- GET_BE_WORD(range
->start_glyph
) :
3164 WARN("Unknown coverage format %u.\n", format
);
3169 static inline unsigned int dwrite_popcount(unsigned int x
)
3171 #ifdef HAVE___BUILTIN_POPCOUNT
3172 return __builtin_popcount(x
);
3174 x
-= x
>> 1 & 0x55555555;
3175 x
= (x
& 0x33333333) + (x
>> 2 & 0x33333333);
3176 return ((x
+ (x
>> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
3180 static float opentype_scale_gpos_be_value(WORD value
, float emsize
, UINT16 upem
)
3182 return (short)GET_BE_WORD(value
) * emsize
/ upem
;
3185 static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context
*context
, unsigned int offset
)
3187 const struct scriptshaping_cache
*cache
= context
->cache
;
3188 unsigned int start_size
, end_size
, format
, value_word
;
3189 unsigned int index
, ppem
, mask
;
3195 start_size
= table_read_be_word(&cache
->gpos
.table
, offset
);
3196 end_size
= table_read_be_word(&cache
->gpos
.table
, offset
+ FIELD_OFFSET(struct ot_gpos_device_table
, end_size
));
3198 ppem
= context
->emsize
;
3199 if (ppem
< start_size
|| ppem
> end_size
)
3202 format
= table_read_be_word(&cache
->gpos
.table
, offset
+ FIELD_OFFSET(struct ot_gpos_device_table
, format
));
3204 if (format
< 1 || format
> 3)
3207 index
= ppem
- start_size
;
3209 value_word
= table_read_be_word(&cache
->gpos
.table
, offset
+
3210 FIELD_OFFSET(struct ot_gpos_device_table
, values
[index
>> (4 - format
)]));
3211 mask
= 0xffff >> (16 - (1 << format
));
3213 value
= (value_word
>> ((index
% (4 - format
)) * (1 << format
))) & mask
;
3215 if ((unsigned int)value
>= ((mask
+ 1) >> 1))
3221 static void opentype_layout_apply_gpos_value(struct scriptshaping_context
*context
, unsigned int table_offset
,
3222 WORD value_format
, const WORD
*values
, unsigned int glyph
)
3224 const struct scriptshaping_cache
*cache
= context
->cache
;
3225 DWRITE_GLYPH_OFFSET
*offset
= &context
->offsets
[glyph
];
3226 float *advance
= &context
->advances
[glyph
];
3231 if (value_format
& GPOS_VALUE_X_PLACEMENT
)
3233 offset
->advanceOffset
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3236 if (value_format
& GPOS_VALUE_Y_PLACEMENT
)
3238 offset
->ascenderOffset
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3241 if (value_format
& GPOS_VALUE_X_ADVANCE
)
3243 *advance
+= opentype_scale_gpos_be_value(*values
, context
->emsize
, cache
->upem
);
3246 if (value_format
& GPOS_VALUE_Y_ADVANCE
)
3250 if (value_format
& GPOS_VALUE_X_PLACEMENT_DEVICE
)
3252 offset
->advanceOffset
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3255 if (value_format
& GPOS_VALUE_Y_PLACEMENT_DEVICE
)
3257 offset
->ascenderOffset
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3260 if (value_format
& GPOS_VALUE_X_ADVANCE_DEVICE
)
3262 *advance
+= opentype_layout_gpos_get_dev_value(context
, table_offset
+ GET_BE_WORD(*values
));
3265 if (value_format
& GPOS_VALUE_Y_ADVANCE_DEVICE
)
3271 static unsigned int opentype_layout_get_gpos_subtable(const struct scriptshaping_cache
*cache
,
3272 unsigned int lookup_offset
, unsigned int subtable
)
3274 WORD lookup_type
= table_read_be_word(&cache
->gpos
.table
, lookup_offset
);
3275 unsigned int subtable_offset
= table_read_be_word(&cache
->gpos
.table
, lookup_offset
+
3276 FIELD_OFFSET(struct ot_lookup_table
, subtable
[subtable
]));
3277 if (lookup_type
== GPOS_LOOKUP_EXTENSION_POSITION
)
3279 const struct ot_gpos_extensionpos_format1
*format1
= table_read_ensure(&cache
->gpos
.table
,
3280 lookup_offset
+ subtable_offset
, sizeof(*format1
));
3281 subtable_offset
+= GET_BE_DWORD(format1
->extension_offset
);
3284 return lookup_offset
+ subtable_offset
;
3289 unsigned int offset
;
3290 unsigned int subtable_count
;
3294 struct glyph_iterator
3296 const struct scriptshaping_context
*context
;
3302 static void glyph_iterator_init(const struct scriptshaping_context
*context
, unsigned int flags
, unsigned int pos
,
3303 unsigned int len
, struct glyph_iterator
*iter
)
3305 iter
->context
= context
;
3306 iter
->flags
= flags
;
3311 static BOOL
glyph_iterator_match(const struct glyph_iterator
*iter
)
3313 struct scriptshaping_cache
*cache
= iter
->context
->cache
;
3315 if (cache
->gdef
.classdef
)
3317 unsigned int glyph_class
= opentype_layout_get_glyph_class(&cache
->gdef
.table
, cache
->gdef
.classdef
,
3318 iter
->context
->u
.pos
.glyphs
[iter
->pos
]);
3319 if ((1 << glyph_class
) & iter
->flags
& LOOKUP_FLAG_IGNORE_MASK
)
3326 static BOOL
glyph_iterator_next(struct glyph_iterator
*iter
)
3328 while (iter
->pos
+ iter
->len
< iter
->context
->glyph_count
)
3331 if (glyph_iterator_match(iter
))
3341 static BOOL
glyph_iterator_prev(struct glyph_iterator
*iter
)
3346 while (iter
->pos
> iter
->len
- 1)
3349 if (glyph_iterator_match(iter
))
3359 static BOOL
opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context
*context
,
3360 struct glyph_iterator
*iter
, const struct lookup
*lookup
)
3362 struct scriptshaping_cache
*cache
= context
->cache
;
3363 WORD format
, value_format
, value_len
, coverage
;
3366 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
3368 unsigned int subtable_offset
= opentype_layout_get_gpos_subtable(cache
, lookup
->offset
, i
);
3369 unsigned int coverage_index
;
3371 format
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
);
3373 coverage
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3374 FIELD_OFFSET(struct ot_gpos_singlepos_format1
, coverage
));
3375 value_format
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3376 FIELD_OFFSET(struct ot_gpos_singlepos_format1
, value_format
));
3377 value_len
= dwrite_popcount(value_format
);
3381 const struct ot_gpos_singlepos_format1
*format1
= table_read_ensure(&cache
->gpos
.table
, subtable_offset
,
3382 FIELD_OFFSET(struct ot_gpos_singlepos_format1
, value
[value_len
]));
3384 coverage_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+ coverage
,
3385 context
->u
.pos
.glyphs
[iter
->pos
]);
3386 if (coverage_index
== GLYPH_NOT_COVERED
)
3389 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format
, format1
->value
, iter
->pos
);
3392 else if (format
== 2)
3394 WORD value_count
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3395 FIELD_OFFSET(struct ot_gpos_singlepos_format2
, value_count
));
3396 const struct ot_gpos_singlepos_format2
*format2
= table_read_ensure(&cache
->gpos
.table
, subtable_offset
,
3397 FIELD_OFFSET(struct ot_gpos_singlepos_format2
, values
) + value_count
* value_len
* sizeof(WORD
));
3399 coverage_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+ coverage
,
3400 context
->u
.pos
.glyphs
[iter
->pos
]);
3401 if (coverage_index
== GLYPH_NOT_COVERED
|| coverage_index
>= value_count
)
3404 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format
,
3405 &format2
->values
[coverage_index
* value_len
], iter
->pos
);
3409 WARN("Unknown single adjustment format %u.\n", format
);
3415 static int gpos_pair_adjustment_compare_format1(const void *g
, const void *r
)
3417 const struct ot_gpos_pairvalue
*pairvalue
= r
;
3418 UINT16 second_glyph
= GET_BE_WORD(pairvalue
->second_glyph
);
3419 return *(UINT16
*)g
- second_glyph
;
3422 static BOOL
opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context
*context
,
3423 struct glyph_iterator
*iter
, const struct lookup
*lookup
)
3425 struct scriptshaping_cache
*cache
= context
->cache
;
3426 unsigned int i
, first_glyph
, second_glyph
;
3427 struct glyph_iterator iter_pair
;
3428 WORD format
, coverage
;
3430 glyph_iterator_init(context
, iter
->flags
, iter
->pos
, 1, &iter_pair
);
3431 if (!glyph_iterator_next(&iter_pair
))
3434 if (context
->is_rtl
)
3436 first_glyph
= iter_pair
.pos
;
3437 second_glyph
= iter
->pos
;
3441 first_glyph
= iter
->pos
;
3442 second_glyph
= iter_pair
.pos
;
3445 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
3447 unsigned int subtable_offset
= opentype_layout_get_gpos_subtable(cache
, lookup
->offset
, i
);
3448 WORD value_format1
, value_format2
, value_len1
, value_len2
;
3449 unsigned int coverage_index
;
3451 format
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
);
3453 coverage
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3454 FIELD_OFFSET(struct ot_gpos_pairpos_format1
, coverage
));
3458 coverage_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3459 coverage
, context
->u
.pos
.glyphs
[first_glyph
]);
3460 if (coverage_index
== GLYPH_NOT_COVERED
)
3465 const struct ot_gpos_pairpos_format1
*format1
;
3466 WORD pairset_count
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3467 FIELD_OFFSET(struct ot_gpos_pairpos_format1
, pairset_count
));
3468 unsigned int pairvalue_len
, pairset_offset
;
3469 const struct ot_gpos_pairset
*pairset
;
3470 const WORD
*pairvalue
;
3471 WORD pairvalue_count
;
3473 if (!pairset_count
|| coverage_index
>= pairset_count
)
3476 format1
= table_read_ensure(&cache
->gpos
.table
, subtable_offset
,
3477 FIELD_OFFSET(struct ot_gpos_pairpos_format1
, pairsets
[pairset_count
]));
3481 /* Ordered paired values. */
3482 pairvalue_count
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3483 GET_BE_WORD(format1
->pairsets
[coverage_index
]));
3484 if (!pairvalue_count
)
3487 /* Structure length is variable, but does not change across the subtable. */
3488 value_format1
= GET_BE_WORD(format1
->value_format1
) & 0xff;
3489 value_format2
= GET_BE_WORD(format1
->value_format2
) & 0xff;
3491 value_len1
= dwrite_popcount(value_format1
);
3492 value_len2
= dwrite_popcount(value_format2
);
3493 pairvalue_len
= FIELD_OFFSET(struct ot_gpos_pairvalue
, data
) + value_len1
* sizeof(WORD
) +
3494 value_len2
* sizeof(WORD
);
3496 pairset_offset
= subtable_offset
+ GET_BE_WORD(format1
->pairsets
[coverage_index
]);
3497 pairset
= table_read_ensure(&cache
->gpos
.table
, subtable_offset
+ pairset_offset
,
3498 pairvalue_len
* pairvalue_count
);
3502 pairvalue
= bsearch(&context
->u
.pos
.glyphs
[second_glyph
], pairset
->pairvalues
, pairvalue_count
,
3503 pairvalue_len
, gpos_pair_adjustment_compare_format1
);
3507 pairvalue
+= 1; /* Skip SecondGlyph. */
3508 opentype_layout_apply_gpos_value(context
, pairset_offset
, value_format1
, pairvalue
, first_glyph
);
3509 opentype_layout_apply_gpos_value(context
, pairset_offset
, value_format2
, pairvalue
+ value_len1
,
3512 iter
->pos
= iter_pair
.pos
;
3518 else if (format
== 2)
3520 const struct ot_gpos_pairpos_format2
*format2
;
3521 WORD class1_count
, class2_count
;
3522 unsigned int class1
, class2
;
3524 value_format1
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3525 FIELD_OFFSET(struct ot_gpos_pairpos_format2
, value_format1
)) & 0xff;
3526 value_format2
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3527 FIELD_OFFSET(struct ot_gpos_pairpos_format2
, value_format2
)) & 0xff;
3529 class1_count
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3530 FIELD_OFFSET(struct ot_gpos_pairpos_format2
, class1_count
));
3531 class2_count
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3532 FIELD_OFFSET(struct ot_gpos_pairpos_format2
, class2_count
));
3534 value_len1
= dwrite_popcount(value_format1
);
3535 value_len2
= dwrite_popcount(value_format2
);
3537 format2
= table_read_ensure(&cache
->gpos
.table
, subtable_offset
,
3538 FIELD_OFFSET(struct ot_gpos_pairpos_format2
,
3539 values
[class1_count
* class2_count
* (value_len1
+ value_len2
)]));
3543 class1
= opentype_layout_get_glyph_class(&cache
->gpos
.table
, subtable_offset
+ GET_BE_WORD(format2
->class_def1
),
3544 context
->u
.pos
.glyphs
[first_glyph
]);
3545 class2
= opentype_layout_get_glyph_class(&cache
->gpos
.table
, subtable_offset
+ GET_BE_WORD(format2
->class_def2
),
3546 context
->u
.pos
.glyphs
[second_glyph
]);
3548 if (class1
< class1_count
&& class2
< class2_count
)
3550 const WCHAR
*values
= &format2
->values
[(class1
* class2_count
+ class2
) * (value_len1
+ value_len2
)];
3551 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format1
, values
, first_glyph
);
3552 opentype_layout_apply_gpos_value(context
, subtable_offset
, value_format2
, values
+ value_len1
,
3555 iter
->pos
= iter_pair
.pos
;
3564 WARN("Unknown pair adjustment format %u.\n", format
);
3572 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context
*context
, unsigned int anchor_offset
,
3573 unsigned int glyph_index
, float *x
, float *y
)
3575 const struct scriptshaping_cache
*cache
= context
->cache
;
3577 WORD format
= table_read_be_word(&cache
->gpos
.table
, anchor_offset
);
3583 const struct ot_gpos_anchor_format1
*format1
= table_read_ensure(&cache
->gpos
.table
, anchor_offset
,
3588 *x
= opentype_scale_gpos_be_value(format1
->x_coord
, context
->emsize
, cache
->upem
);
3589 *y
= opentype_scale_gpos_be_value(format1
->y_coord
, context
->emsize
, cache
->upem
);
3592 else if (format
== 2)
3594 const struct ot_gpos_anchor_format2
*format2
= table_read_ensure(&cache
->gpos
.table
, anchor_offset
,
3599 if (context
->measuring_mode
!= DWRITE_MEASURING_MODE_NATURAL
)
3600 FIXME("Use outline anchor point for glyph %u.\n", context
->u
.pos
.glyphs
[glyph_index
]);
3602 *x
= opentype_scale_gpos_be_value(format2
->x_coord
, context
->emsize
, cache
->upem
);
3603 *y
= opentype_scale_gpos_be_value(format2
->y_coord
, context
->emsize
, cache
->upem
);
3606 else if (format
== 3)
3608 const struct ot_gpos_anchor_format3
*format3
= table_read_ensure(&cache
->gpos
.table
, anchor_offset
,
3613 *x
= opentype_scale_gpos_be_value(format3
->x_coord
, context
->emsize
, cache
->upem
);
3614 *y
= opentype_scale_gpos_be_value(format3
->y_coord
, context
->emsize
, cache
->upem
);
3616 if (context
->measuring_mode
!= DWRITE_MEASURING_MODE_NATURAL
)
3618 if (format3
->x_dev_offset
)
3619 *x
+= opentype_layout_gpos_get_dev_value(context
, anchor_offset
+ GET_BE_WORD(format3
->x_dev_offset
));
3620 if (format3
->y_dev_offset
)
3621 *y
+= opentype_layout_gpos_get_dev_value(context
, anchor_offset
+ GET_BE_WORD(format3
->y_dev_offset
));
3626 WARN("Unknown anchor format %u.\n", format
);
3629 static BOOL
opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context
*context
,
3630 struct glyph_iterator
*iter
, const struct lookup
*lookup
)
3632 struct scriptshaping_cache
*cache
= context
->cache
;
3635 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
3637 unsigned int subtable_offset
= opentype_layout_get_gpos_subtable(cache
, lookup
->offset
, i
);
3640 format
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
);
3644 WORD coverage_offset
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3645 FIELD_OFFSET(struct ot_gpos_cursive_format1
, coverage
));
3646 unsigned int glyph_index
, entry_count
, entry_anchor
, exit_anchor
;
3647 float entry_x
, entry_y
, exit_x
, exit_y
, delta
;
3648 struct glyph_iterator prev_iter
;
3650 if (!coverage_offset
)
3653 entry_count
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3654 FIELD_OFFSET(struct ot_gpos_cursive_format1
, count
));
3656 glyph_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3657 coverage_offset
, context
->u
.pos
.glyphs
[iter
->pos
]);
3658 if (glyph_index
== GLYPH_NOT_COVERED
|| glyph_index
>= entry_count
)
3661 entry_anchor
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3662 FIELD_OFFSET(struct ot_gpos_cursive_format1
, anchors
[glyph_index
* 2]));
3666 glyph_iterator_init(context
, iter
->flags
, iter
->pos
, 1, &prev_iter
);
3667 if (!glyph_iterator_prev(&prev_iter
))
3670 glyph_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3671 coverage_offset
, context
->u
.pos
.glyphs
[prev_iter
.pos
]);
3672 if (glyph_index
== GLYPH_NOT_COVERED
|| glyph_index
>= entry_count
)
3675 exit_anchor
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
+
3676 FIELD_OFFSET(struct ot_gpos_cursive_format1
, anchors
[glyph_index
* 2 + 1]));
3680 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ exit_anchor
, prev_iter
.pos
, &exit_x
, &exit_y
);
3681 opentype_layout_gpos_get_anchor(context
, subtable_offset
+ entry_anchor
, iter
->pos
, &entry_x
, &entry_y
);
3683 if (context
->is_rtl
)
3685 delta
= exit_x
+ context
->offsets
[prev_iter
.pos
].advanceOffset
;
3686 context
->advances
[prev_iter
.pos
] -= delta
;
3687 context
->advances
[iter
->pos
] = entry_x
+ context
->offsets
[iter
->pos
].advanceOffset
;
3688 context
->offsets
[prev_iter
.pos
].advanceOffset
-= delta
;
3692 delta
= entry_x
+ context
->offsets
[iter
->pos
].advanceOffset
;
3693 context
->advances
[prev_iter
.pos
] = exit_x
+ context
->offsets
[prev_iter
.pos
].advanceOffset
;
3694 context
->advances
[iter
->pos
] -= delta
;
3695 context
->offsets
[iter
->pos
].advanceOffset
-= delta
;
3698 if (lookup
->flags
& LOOKUP_FLAG_RTL
)
3699 context
->offsets
[prev_iter
.pos
].ascenderOffset
= entry_y
- exit_y
;
3701 context
->offsets
[iter
->pos
].ascenderOffset
= exit_y
- entry_y
;
3706 WARN("Unknown cursive attachment format %u.\n", format
);
3713 static BOOL
opentype_layout_apply_gpos_mark_to_base_attachment(const struct scriptshaping_context
*context
,
3714 struct glyph_iterator
*iter
, const struct lookup
*lookup
)
3716 struct scriptshaping_cache
*cache
= context
->cache
;
3720 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
3722 unsigned int subtable_offset
= opentype_layout_get_gpos_subtable(cache
, lookup
->offset
, i
);
3724 format
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
);
3728 const struct ot_gpos_mark_to_base_format1
*format1
= table_read_ensure(&cache
->gpos
.table
, subtable_offset
,
3730 unsigned int mark_class_count
, count
, mark_array_offset
, base_array_offset
;
3731 const struct ot_gpos_mark_array
*mark_array
;
3732 const struct ot_gpos_base_array
*base_array
;
3733 float mark_x
, mark_y
, base_x
, base_y
;
3734 unsigned int base_index
, mark_index
;
3735 struct glyph_iterator base_iter
;
3736 unsigned int base_anchor
;
3741 mark_array_offset
= subtable_offset
+ GET_BE_WORD(format1
->mark_array
);
3742 if (!(count
= table_read_be_word(&cache
->gpos
.table
, mark_array_offset
)))
3745 mark_array
= table_read_ensure(&cache
->gpos
.table
, mark_array_offset
,
3746 FIELD_OFFSET(struct ot_gpos_mark_array
, records
[count
]));
3750 base_array_offset
= subtable_offset
+ GET_BE_WORD(format1
->base_array
);
3751 if (!(count
= table_read_be_word(&cache
->gpos
.table
, base_array_offset
)))
3754 base_array
= table_read_ensure(&cache
->gpos
.table
, base_array_offset
,
3755 FIELD_OFFSET(struct ot_gpos_base_array
, offsets
[count
* GET_BE_WORD(format1
->mark_class_count
)]));
3759 mark_class_count
= GET_BE_WORD(format1
->mark_class_count
);
3761 mark_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3762 GET_BE_WORD(format1
->mark_coverage
), context
->u
.pos
.glyphs
[iter
->pos
]);
3764 if (mark_index
== GLYPH_NOT_COVERED
|| mark_index
>= GET_BE_WORD(mark_array
->count
))
3767 /* Look back for first base glyph. */
3768 glyph_iterator_init(context
, LOOKUP_FLAG_IGNORE_MARKS
, iter
->pos
, 1, &base_iter
);
3769 if (!glyph_iterator_prev(&base_iter
))
3772 base_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3773 GET_BE_WORD(format1
->base_coverage
), context
->u
.pos
.glyphs
[base_iter
.pos
]);
3774 if (base_index
== GLYPH_NOT_COVERED
|| base_index
>= GET_BE_WORD(base_array
->count
))
3777 base_anchor
= GET_BE_WORD(base_array
->offsets
[base_index
* mark_class_count
+
3778 GET_BE_WORD(mark_array
->records
[mark_index
].mark_class
)]);
3780 opentype_layout_gpos_get_anchor(context
, mark_array_offset
+
3781 GET_BE_WORD(mark_array
->records
[mark_index
].mark_anchor
), iter
->pos
, &mark_x
, &mark_y
);
3782 opentype_layout_gpos_get_anchor(context
, base_array_offset
+ base_anchor
, base_iter
.pos
, &base_x
, &base_y
);
3784 context
->offsets
[iter
->pos
].advanceOffset
= (context
->is_rtl
? -1.0f
: 1.0f
) * (base_x
- mark_x
);
3785 context
->offsets
[iter
->pos
].ascenderOffset
= base_y
- mark_y
;
3790 WARN("Unknown mark-to-base format %u.\n", format
);
3796 static BOOL
opentype_layout_apply_gpos_mark_to_lig_attachment(const struct scriptshaping_context
*context
,
3797 struct glyph_iterator
*iter
, const struct lookup
*lookup
)
3799 struct scriptshaping_cache
*cache
= context
->cache
;
3803 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
3805 unsigned int subtable_offset
= opentype_layout_get_gpos_subtable(cache
, lookup
->offset
, i
);
3807 format
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
);
3811 const struct ot_gpos_mark_to_lig_format1
*format1
= table_read_ensure(&cache
->gpos
.table
,
3812 subtable_offset
, sizeof(*format1
));
3813 unsigned int mark_index
, lig_index
;
3814 struct glyph_iterator lig_iter
;
3819 mark_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3820 GET_BE_WORD(format1
->mark_coverage
), context
->u
.pos
.glyphs
[iter
->pos
]);
3821 if (mark_index
== GLYPH_NOT_COVERED
)
3824 glyph_iterator_init(context
, LOOKUP_FLAG_IGNORE_MARKS
, iter
->pos
, 1, &lig_iter
);
3825 if (!glyph_iterator_prev(&lig_iter
))
3828 lig_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3829 GET_BE_WORD(format1
->lig_coverage
), context
->u
.pos
.glyphs
[lig_iter
.pos
]);
3830 if (lig_index
== GLYPH_NOT_COVERED
)
3833 FIXME("Unimplemented.\n");
3836 WARN("Unknown mark-to-ligature format %u.\n", format
);
3842 static BOOL
opentype_layout_apply_gpos_mark_to_mark_attachment(const struct scriptshaping_context
*context
,
3843 struct glyph_iterator
*iter
, const struct lookup
*lookup
)
3845 struct scriptshaping_cache
*cache
= context
->cache
;
3849 for (i
= 0; i
< lookup
->subtable_count
; ++i
)
3851 unsigned int subtable_offset
= opentype_layout_get_gpos_subtable(cache
, lookup
->offset
, i
);
3853 format
= table_read_be_word(&cache
->gpos
.table
, subtable_offset
);
3857 const struct ot_gpos_mark_to_mark_format1
*format1
= table_read_ensure(&cache
->gpos
.table
,
3858 subtable_offset
, sizeof(*format1
));
3859 unsigned int count
, mark1_array_offset
, mark2_array_offset
, mark_class_count
;
3860 unsigned int mark1_index
, mark2_index
, mark2_anchor
;
3861 const struct ot_gpos_mark_array
*mark1_array
;
3862 const struct ot_gpos_base_array
*mark2_array
;
3863 float mark1_x
, mark1_y
, mark2_x
, mark2_y
;
3864 struct glyph_iterator mark_iter
;
3869 mark1_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3870 GET_BE_WORD(format1
->mark1_coverage
), context
->u
.pos
.glyphs
[iter
->pos
]);
3872 mark1_array_offset
= subtable_offset
+ GET_BE_WORD(format1
->mark1_array
);
3873 if (!(count
= table_read_be_word(&cache
->gpos
.table
, mark1_array_offset
)))
3876 mark1_array
= table_read_ensure(&cache
->gpos
.table
, mark1_array_offset
,
3877 FIELD_OFFSET(struct ot_gpos_mark_array
, records
[count
]));
3881 if (mark1_index
== GLYPH_NOT_COVERED
|| mark1_index
>= count
)
3884 glyph_iterator_init(context
, lookup
->flags
& ~LOOKUP_FLAG_IGNORE_MASK
, iter
->pos
, 1, &mark_iter
);
3885 if (!glyph_iterator_prev(&mark_iter
))
3888 if (!context
->u
.pos
.glyph_props
[mark_iter
.pos
].isDiacritic
)
3891 mark2_array_offset
= subtable_offset
+ GET_BE_WORD(format1
->mark2_array
);
3892 if (!(count
= table_read_be_word(&cache
->gpos
.table
, mark2_array_offset
)))
3895 mark_class_count
= GET_BE_WORD(format1
->mark_class_count
);
3897 mark2_array
= table_read_ensure(&cache
->gpos
.table
, mark2_array_offset
,
3898 FIELD_OFFSET(struct ot_gpos_base_array
, offsets
[count
* mark_class_count
]));
3902 mark2_index
= opentype_layout_is_glyph_covered(&cache
->gpos
.table
, subtable_offset
+
3903 GET_BE_WORD(format1
->mark2_coverage
), context
->u
.pos
.glyphs
[mark_iter
.pos
]);
3905 if (mark2_index
== GLYPH_NOT_COVERED
|| mark2_index
>= count
)
3908 mark2_anchor
= GET_BE_WORD(mark2_array
->offsets
[mark2_index
* mark_class_count
+
3909 GET_BE_WORD(mark1_array
->records
[mark1_index
].mark_class
)]);
3910 opentype_layout_gpos_get_anchor(context
, mark1_array_offset
+
3911 GET_BE_WORD(mark1_array
->records
[mark1_index
].mark_anchor
), iter
->pos
, &mark1_x
, &mark1_y
);
3912 opentype_layout_gpos_get_anchor(context
, mark2_array_offset
+ mark2_anchor
, mark_iter
.pos
,
3913 &mark2_x
, &mark2_y
);
3915 context
->offsets
[iter
->pos
].advanceOffset
= mark2_x
- mark1_x
;
3916 context
->offsets
[iter
->pos
].ascenderOffset
= mark2_y
- mark1_y
;
3921 WARN("Unknown mark-to-mark format %u.\n", format
);
3927 static BOOL
opentype_layout_apply_gpos_contextual_positioning(const struct scriptshaping_context
*context
,
3928 struct glyph_iterator
*iter
, const struct lookup
*lookup
)
3933 static BOOL
opentype_layout_apply_gpos_chaining_contextual_positioning(const struct scriptshaping_context
*context
,
3934 struct glyph_iterator
*iter
, const struct lookup
*lookup
)
3939 static void opentype_layout_apply_gpos_lookup(struct scriptshaping_context
*context
, int lookup_index
)
3941 struct scriptshaping_cache
*cache
= context
->cache
;
3942 const struct ot_lookup_table
*lookup_table
;
3943 struct glyph_iterator iter
;
3944 struct lookup lookup
;
3947 lookup
.offset
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.lookup_list
+
3948 FIELD_OFFSET(struct ot_lookup_list
, lookup
[lookup_index
]));
3952 lookup
.offset
+= cache
->gpos
.lookup_list
;
3954 if (!(lookup_table
= table_read_ensure(&cache
->gpos
.table
, lookup
.offset
, sizeof(*lookup_table
))))
3957 lookup
.subtable_count
= GET_BE_WORD(lookup_table
->subtable_count
);
3958 if (!lookup
.subtable_count
)
3961 lookup_type
= GET_BE_WORD(lookup_table
->lookup_type
);
3962 if (lookup_type
== GPOS_LOOKUP_EXTENSION_POSITION
)
3964 const struct ot_gpos_extensionpos_format1
*extension
= table_read_ensure(&cache
->gpos
.table
,
3965 lookup
.offset
+ GET_BE_WORD(lookup_table
->subtable
[0]), sizeof(*extension
));
3971 format
= GET_BE_WORD(extension
->format
);
3974 WARN("Unexpected extension table format %u.\n", format
);
3978 lookup_type
= GET_BE_WORD(extension
->lookup_type
);
3980 lookup
.flags
= GET_BE_WORD(lookup_table
->flags
);
3982 glyph_iterator_init(context
, lookup
.flags
, 0, context
->glyph_count
, &iter
);
3984 while (iter
.pos
< context
->glyph_count
)
3988 if (!glyph_iterator_match(&iter
))
3994 switch (lookup_type
)
3996 case GPOS_LOOKUP_SINGLE_ADJUSTMENT
:
3997 ret
= opentype_layout_apply_gpos_single_adjustment(context
, &iter
, &lookup
);
3999 case GPOS_LOOKUP_PAIR_ADJUSTMENT
:
4000 ret
= opentype_layout_apply_gpos_pair_adjustment(context
, &iter
, &lookup
);
4002 case GPOS_LOOKUP_CURSIVE_ATTACHMENT
:
4003 ret
= opentype_layout_apply_gpos_cursive_attachment(context
, &iter
, &lookup
);
4005 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT
:
4006 ret
= opentype_layout_apply_gpos_mark_to_base_attachment(context
, &iter
, &lookup
);
4008 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT
:
4009 ret
= opentype_layout_apply_gpos_mark_to_lig_attachment(context
, &iter
, &lookup
);
4011 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT
:
4012 ret
= opentype_layout_apply_gpos_mark_to_mark_attachment(context
, &iter
, &lookup
);
4014 case GPOS_LOOKUP_CONTEXTUAL_POSITION
:
4015 ret
= opentype_layout_apply_gpos_contextual_positioning(context
, &iter
, &lookup
);
4017 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION
:
4018 ret
= opentype_layout_apply_gpos_chaining_contextual_positioning(context
, &iter
, &lookup
);
4020 case GPOS_LOOKUP_EXTENSION_POSITION
:
4021 WARN("Recursive extension lookup.\n");
4025 WARN("Unknown lookup type %u.\n", lookup_type
);
4029 /* Some lookups update position after making changes. */
4042 static int lookups_sorting_compare(const void *left
, const void *right
)
4044 return *(int *)left
- *(int *)right
;
4047 void opentype_layout_apply_gpos_features(struct scriptshaping_context
*context
,
4048 unsigned int script_index
, unsigned int language_index
, const struct shaping_features
*features
)
4050 WORD table_offset
, langsys_offset
, script_feature_count
, total_feature_count
, total_lookup_count
;
4051 struct scriptshaping_cache
*cache
= context
->cache
;
4052 const struct ot_feature_list
*feature_list
;
4053 struct lookups lookups
= { 0 };
4054 unsigned int i
, j
, l
;
4056 /* ScriptTable offset. */
4057 table_offset
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
+
4058 FIELD_OFFSET(struct ot_script_list
, scripts
) + script_index
* sizeof(struct ot_script_record
) +
4059 FIELD_OFFSET(struct ot_script_record
, script
));
4063 if (language_index
== ~0u)
4064 langsys_offset
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
+ table_offset
);
4066 langsys_offset
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
+ table_offset
+
4067 FIELD_OFFSET(struct ot_script
, langsys
) + language_index
* sizeof(struct ot_langsys_record
) +
4068 FIELD_OFFSET(struct ot_langsys_record
, langsys
));
4070 script_feature_count
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
+ table_offset
+
4071 langsys_offset
+ FIELD_OFFSET(struct ot_langsys
, feature_count
));
4072 if (!script_feature_count
)
4075 total_feature_count
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.feature_list
);
4076 if (!total_feature_count
)
4079 total_lookup_count
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.lookup_list
);
4080 if (!total_lookup_count
)
4083 feature_list
= table_read_ensure(&cache
->gpos
.table
, cache
->gpos
.feature_list
,
4084 FIELD_OFFSET(struct ot_feature_list
, features
[total_feature_count
]));
4088 /* Collect lookups for all given features. */
4089 for (i
= 0; i
< features
->count
; ++i
)
4091 for (j
= 0; j
< script_feature_count
; ++j
)
4093 WORD feature_index
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.script_list
+ table_offset
+
4094 langsys_offset
+ FIELD_OFFSET(struct ot_langsys
, feature_index
[j
]));
4095 if (feature_index
>= total_feature_count
)
4098 if (feature_list
->features
[feature_index
].tag
== features
->tags
[i
])
4100 WORD feature_offset
= GET_BE_WORD(feature_list
->features
[feature_index
].offset
);
4103 lookup_count
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.feature_list
+ feature_offset
+
4104 FIELD_OFFSET(struct ot_feature
, lookup_count
));
4108 if (!dwrite_array_reserve((void **)&lookups
.indexes
, &lookups
.capacity
, lookups
.count
+ lookup_count
,
4109 sizeof(*lookups
.indexes
)))
4111 heap_free(lookups
.indexes
);
4115 for (l
= 0; l
< lookup_count
; ++l
)
4117 WORD lookup_index
= table_read_be_word(&cache
->gpos
.table
, cache
->gpos
.feature_list
+
4118 feature_offset
+ FIELD_OFFSET(struct ot_feature
, lookuplist_index
[l
]));
4120 if (lookup_index
>= total_lookup_count
)
4123 lookups
.indexes
[lookups
.count
++] = lookup_index
;
4130 qsort(lookups
.indexes
, lookups
.count
, sizeof(*lookups
.indexes
), lookups_sorting_compare
);
4132 for (l
= 0; l
< lookups
.count
; ++l
)
4134 /* Skip duplicates. */
4135 if (l
&& lookups
.indexes
[l
] == lookups
.indexes
[l
- 1])
4138 opentype_layout_apply_gpos_lookup(context
, lookups
.indexes
[l
]);
4141 heap_free(lookups
.indexes
);