wined3d: Pass a wined3d_device_context to wined3d_cs_emit_blt_sub_resource().
[wine/zf.git] / dlls / dwrite / opentype.c
blob717550acf38bb2da1923e6b938f6a84298abbe40
1 /*
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
21 #define COBJMACROS
22 #define NONAMELESSUNION
24 #include "dwrite_private.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
28 #define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d')
29 #define MS_HHEA_TAG DWRITE_MAKE_OPENTYPE_TAG('h','h','e','a')
30 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
31 #define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2')
32 #define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t')
33 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
34 #define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
35 #define MS_NAME_TAG DWRITE_MAKE_OPENTYPE_TAG('n','a','m','e')
36 #define MS_GLYF_TAG DWRITE_MAKE_OPENTYPE_TAG('g','l','y','f')
37 #define MS_CFF__TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F',' ')
38 #define MS_CFF2_TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F','2')
39 #define MS_CPAL_TAG DWRITE_MAKE_OPENTYPE_TAG('C','P','A','L')
40 #define MS_COLR_TAG DWRITE_MAKE_OPENTYPE_TAG('C','O','L','R')
41 #define MS_SVG__TAG DWRITE_MAKE_OPENTYPE_TAG('S','V','G',' ')
42 #define MS_SBIX_TAG DWRITE_MAKE_OPENTYPE_TAG('s','b','i','x')
43 #define MS_MAXP_TAG DWRITE_MAKE_OPENTYPE_TAG('m','a','x','p')
44 #define MS_CBLC_TAG DWRITE_MAKE_OPENTYPE_TAG('C','B','L','C')
45 #define MS_CMAP_TAG DWRITE_MAKE_OPENTYPE_TAG('c','m','a','p')
46 #define MS_META_TAG DWRITE_MAKE_OPENTYPE_TAG('m','e','t','a')
47 #define MS_KERN_TAG DWRITE_MAKE_OPENTYPE_TAG('k','e','r','n')
49 /* 'sbix' formats */
50 #define MS_PNG__TAG DWRITE_MAKE_OPENTYPE_TAG('p','n','g',' ')
51 #define MS_JPG__TAG DWRITE_MAKE_OPENTYPE_TAG('j','p','g',' ')
52 #define MS_TIFF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','i','f','f')
54 #define MS_WOFF_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','F')
55 #define MS_WOF2_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','2')
57 /* 'meta' tags */
58 #define MS_DLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('d','l','n','g')
59 #define MS_SLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('s','l','n','g')
61 #ifdef WORDS_BIGENDIAN
62 #define GET_BE_WORD(x) (x)
63 #define GET_BE_DWORD(x) (x)
64 #else
65 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
66 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
67 #endif
69 #define GLYPH_CONTEXT_MAX_LENGTH 64
70 #define SHAPE_MAX_NESTING_LEVEL 6
72 typedef struct {
73 CHAR TTCTag[4];
74 DWORD Version;
75 DWORD numFonts;
76 DWORD OffsetTable[1];
77 } TTC_Header_V1;
79 typedef struct {
80 DWORD version;
81 WORD numTables;
82 WORD searchRange;
83 WORD entrySelector;
84 WORD rangeShift;
85 } TTC_SFNT_V1;
87 typedef struct {
88 DWORD tag;
89 DWORD checkSum;
90 DWORD offset;
91 DWORD length;
92 } TT_TableRecord;
94 struct cmap_encoding_record
96 WORD platformID;
97 WORD encodingID;
98 DWORD offset;
101 struct cmap_header
103 WORD version;
104 WORD num_tables;
105 struct cmap_encoding_record tables[1];
108 enum OPENTYPE_CMAP_TABLE_FORMAT
110 OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING = 4,
111 OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE = 12
114 enum opentype_cmap_table_platform
116 OPENTYPE_CMAP_TABLE_PLATFORM_WIN = 3,
119 enum opentype_cmap_table_encoding
121 OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL = 0,
122 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_BMP = 1,
123 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_FULL = 10,
126 /* PANOSE is 10 bytes in size, need to pack the structure properly */
127 #include "pshpack2.h"
128 struct tt_head
130 USHORT majorVersion;
131 USHORT minorVersion;
132 ULONG revision;
133 ULONG checksumadj;
134 ULONG magic;
135 USHORT flags;
136 USHORT unitsPerEm;
137 ULONGLONG created;
138 ULONGLONG modified;
139 SHORT xMin;
140 SHORT yMin;
141 SHORT xMax;
142 SHORT yMax;
143 USHORT macStyle;
144 USHORT lowestRecPPEM;
145 SHORT direction_hint;
146 SHORT index_format;
147 SHORT glyphdata_format;
150 enum tt_head_macstyle
152 TT_HEAD_MACSTYLE_BOLD = 1 << 0,
153 TT_HEAD_MACSTYLE_ITALIC = 1 << 1,
154 TT_HEAD_MACSTYLE_UNDERLINE = 1 << 2,
155 TT_HEAD_MACSTYLE_OUTLINE = 1 << 3,
156 TT_HEAD_MACSTYLE_SHADOW = 1 << 4,
157 TT_HEAD_MACSTYLE_CONDENSED = 1 << 5,
158 TT_HEAD_MACSTYLE_EXTENDED = 1 << 6,
161 struct tt_post
163 ULONG Version;
164 ULONG italicAngle;
165 SHORT underlinePosition;
166 SHORT underlineThickness;
167 ULONG fixed_pitch;
168 ULONG minmemType42;
169 ULONG maxmemType42;
170 ULONG minmemType1;
171 ULONG maxmemType1;
174 struct tt_os2
176 USHORT version;
177 SHORT xAvgCharWidth;
178 USHORT usWeightClass;
179 USHORT usWidthClass;
180 SHORT fsType;
181 SHORT ySubscriptXSize;
182 SHORT ySubscriptYSize;
183 SHORT ySubscriptXOffset;
184 SHORT ySubscriptYOffset;
185 SHORT ySuperscriptXSize;
186 SHORT ySuperscriptYSize;
187 SHORT ySuperscriptXOffset;
188 SHORT ySuperscriptYOffset;
189 SHORT yStrikeoutSize;
190 SHORT yStrikeoutPosition;
191 SHORT sFamilyClass;
192 PANOSE panose;
193 ULONG ulUnicodeRange1;
194 ULONG ulUnicodeRange2;
195 ULONG ulUnicodeRange3;
196 ULONG ulUnicodeRange4;
197 CHAR achVendID[4];
198 USHORT fsSelection;
199 USHORT usFirstCharIndex;
200 USHORT usLastCharIndex;
201 /* According to the Apple spec, original version didn't have the below fields,
202 * version numbers were taken from the OpenType spec.
204 /* version 0 (TrueType 1.5) */
205 USHORT sTypoAscender;
206 USHORT sTypoDescender;
207 USHORT sTypoLineGap;
208 USHORT usWinAscent;
209 USHORT usWinDescent;
210 /* version 1 (TrueType 1.66) */
211 ULONG ulCodePageRange1;
212 ULONG ulCodePageRange2;
213 /* version 2 (OpenType 1.2) */
214 SHORT sxHeight;
215 SHORT sCapHeight;
216 USHORT usDefaultChar;
217 USHORT usBreakChar;
218 USHORT usMaxContext;
221 struct tt_hhea
223 USHORT majorVersion;
224 USHORT minorVersion;
225 SHORT ascender;
226 SHORT descender;
227 SHORT linegap;
228 USHORT advanceWidthMax;
229 SHORT minLeftSideBearing;
230 SHORT minRightSideBearing;
231 SHORT xMaxExtent;
232 SHORT caretSlopeRise;
233 SHORT caretSlopeRun;
234 SHORT caretOffset;
235 SHORT reserved[4];
236 SHORT metricDataFormat;
237 USHORT numberOfHMetrics;
240 struct sbix_header
242 WORD version;
243 WORD flags;
244 DWORD num_strikes;
245 DWORD strike_offset[1];
248 struct sbix_strike
250 WORD ppem;
251 WORD ppi;
252 DWORD glyphdata_offsets[1];
255 struct sbix_glyph_data
257 WORD originOffsetX;
258 WORD originOffsetY;
259 DWORD graphic_type;
260 BYTE data[1];
263 struct maxp
265 DWORD version;
266 WORD num_glyphs;
269 struct cblc_header
271 WORD major_version;
272 WORD minor_version;
273 DWORD num_sizes;
276 typedef struct {
277 BYTE res[12];
278 } sbitLineMetrics;
280 struct cblc_bitmapsize_table
282 DWORD indexSubTableArrayOffset;
283 DWORD indexTablesSize;
284 DWORD numberofIndexSubTables;
285 DWORD colorRef;
286 sbitLineMetrics hori;
287 sbitLineMetrics vert;
288 WORD startGlyphIndex;
289 WORD endGlyphIndex;
290 BYTE ppemX;
291 BYTE ppemY;
292 BYTE bit_depth;
293 BYTE flags;
296 struct gasp_range
298 WORD max_ppem;
299 WORD flags;
302 struct gasp_header
304 WORD version;
305 WORD num_ranges;
306 struct gasp_range ranges[1];
309 enum OS2_FSSELECTION {
310 OS2_FSSELECTION_ITALIC = 1 << 0,
311 OS2_FSSELECTION_UNDERSCORE = 1 << 1,
312 OS2_FSSELECTION_NEGATIVE = 1 << 2,
313 OS2_FSSELECTION_OUTLINED = 1 << 3,
314 OS2_FSSELECTION_STRIKEOUT = 1 << 4,
315 OS2_FSSELECTION_BOLD = 1 << 5,
316 OS2_FSSELECTION_REGULAR = 1 << 6,
317 OS2_FSSELECTION_USE_TYPO_METRICS = 1 << 7,
318 OS2_FSSELECTION_WWS = 1 << 8,
319 OS2_FSSELECTION_OBLIQUE = 1 << 9
322 struct name_record
324 WORD platformID;
325 WORD encodingID;
326 WORD languageID;
327 WORD nameID;
328 WORD length;
329 WORD offset;
332 struct name_header
334 WORD format;
335 WORD count;
336 WORD stringOffset;
337 struct name_record records[1];
340 struct vdmx_header
342 WORD version;
343 WORD num_recs;
344 WORD num_ratios;
347 struct vdmx_ratio
349 BYTE bCharSet;
350 BYTE xRatio;
351 BYTE yStartRatio;
352 BYTE yEndRatio;
355 struct vdmx_vtable
357 WORD yPelHeight;
358 SHORT yMax;
359 SHORT yMin;
362 struct vdmx_group
364 WORD recs;
365 BYTE startsz;
366 BYTE endsz;
367 struct vdmx_vtable entries[1];
370 struct ot_feature_record
372 DWORD tag;
373 WORD offset;
376 struct ot_feature_list
378 WORD feature_count;
379 struct ot_feature_record features[1];
382 struct ot_langsys
384 WORD lookup_order; /* Reserved */
385 WORD required_feature_index;
386 WORD feature_count;
387 WORD feature_index[1];
390 struct ot_langsys_record
392 CHAR tag[4];
393 WORD langsys;
396 struct ot_script
398 WORD default_langsys;
399 WORD langsys_count;
400 struct ot_langsys_record langsys[1];
403 struct ot_script_record
405 CHAR tag[4];
406 WORD script;
409 struct ot_script_list
411 WORD script_count;
412 struct ot_script_record scripts[1];
415 enum ot_gdef_class
417 GDEF_CLASS_UNCLASSIFIED = 0,
418 GDEF_CLASS_BASE = 1,
419 GDEF_CLASS_LIGATURE = 2,
420 GDEF_CLASS_MARK = 3,
421 GDEF_CLASS_COMPONENT = 4,
422 GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
425 struct gdef_header
427 DWORD version;
428 UINT16 classdef;
429 UINT16 attach_list;
430 UINT16 ligcaret_list;
431 UINT16 markattach_classdef;
432 UINT16 markglyphsetdef;
435 struct ot_gdef_classdef_format1
437 WORD format;
438 WORD start_glyph;
439 WORD glyph_count;
440 WORD classes[1];
443 struct ot_gdef_class_range
445 WORD start_glyph;
446 WORD end_glyph;
447 WORD glyph_class;
450 struct ot_gdef_classdef_format2
452 WORD format;
453 WORD range_count;
454 struct ot_gdef_class_range ranges[1];
457 struct gpos_gsub_header
459 DWORD version;
460 WORD script_list;
461 WORD feature_list;
462 WORD lookup_list;
465 enum gsub_gpos_lookup_flags
467 LOOKUP_FLAG_RTL = 0x1, /* Only used for GPOS cursive attachments. */
469 LOOKUP_FLAG_IGNORE_BASE = 0x2,
470 LOOKUP_FLAG_IGNORE_LIGATURES = 0x4,
471 LOOKUP_FLAG_IGNORE_MARKS = 0x8,
472 LOOKUP_FLAG_IGNORE_MASK = 0xe, /* Combined LOOKUP_FLAG_IGNORE_* flags. */
474 LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x10,
475 LOOKUP_FLAG_MARK_ATTACHMENT_TYPE = 0xff00,
478 enum attach_type
480 GLYPH_ATTACH_NONE = 0,
481 GLYPH_ATTACH_MARK,
482 GLYPH_ATTACH_CURSIVE,
485 enum glyph_prop_flags
487 GLYPH_PROP_BASE = LOOKUP_FLAG_IGNORE_BASE,
488 GLYPH_PROP_LIGATURE = LOOKUP_FLAG_IGNORE_LIGATURES,
489 GLYPH_PROP_MARK = LOOKUP_FLAG_IGNORE_MARKS,
491 GLYPH_PROP_ZWNJ = 0x10,
492 GLYPH_PROP_ZWJ = 0x20,
493 GLYPH_PROP_IGNORABLE = 0x40,
494 GLYPH_PROP_HIDDEN = 0x80,
496 GLYPH_PROP_MARK_ATTACH_CLASS_MASK = 0xff00, /* Used with LOOKUP_FLAG_MARK_ATTACHMENT_TYPE. */
497 GLYPH_PROP_ATTACH_TYPE_MASK = 0xff0000,
500 enum gpos_lookup_type
502 GPOS_LOOKUP_SINGLE_ADJUSTMENT = 1,
503 GPOS_LOOKUP_PAIR_ADJUSTMENT = 2,
504 GPOS_LOOKUP_CURSIVE_ATTACHMENT = 3,
505 GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT = 4,
506 GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT = 5,
507 GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT = 6,
508 GPOS_LOOKUP_CONTEXTUAL_POSITION = 7,
509 GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION = 8,
510 GPOS_LOOKUP_EXTENSION_POSITION = 9,
513 enum gsub_lookup_type
515 GSUB_LOOKUP_SINGLE_SUBST = 1,
516 GSUB_LOOKUP_MULTIPLE_SUBST = 2,
517 GSUB_LOOKUP_ALTERNATE_SUBST = 3,
518 GSUB_LOOKUP_LIGATURE_SUBST = 4,
519 GSUB_LOOKUP_CONTEXTUAL_SUBST = 5,
520 GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST = 6,
521 GSUB_LOOKUP_EXTENSION_SUBST = 7,
522 GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST = 8,
525 enum gpos_value_format
527 GPOS_VALUE_X_PLACEMENT = 0x1,
528 GPOS_VALUE_Y_PLACEMENT = 0x2,
529 GPOS_VALUE_X_ADVANCE = 0x4,
530 GPOS_VALUE_Y_ADVANCE = 0x8,
531 GPOS_VALUE_X_PLACEMENT_DEVICE = 0x10,
532 GPOS_VALUE_Y_PLACEMENT_DEVICE = 0x20,
533 GPOS_VALUE_X_ADVANCE_DEVICE = 0x40,
534 GPOS_VALUE_Y_ADVANCE_DEVICE = 0x80,
537 enum OPENTYPE_PLATFORM_ID
539 OPENTYPE_PLATFORM_UNICODE = 0,
540 OPENTYPE_PLATFORM_MAC,
541 OPENTYPE_PLATFORM_ISO,
542 OPENTYPE_PLATFORM_WIN,
543 OPENTYPE_PLATFORM_CUSTOM
546 struct ot_gsubgpos_extension_format1
548 UINT16 format;
549 UINT16 lookup_type;
550 DWORD extension_offset;
553 struct ot_gsub_singlesubst_format1
555 UINT16 format;
556 UINT16 coverage;
557 short delta;
560 struct ot_gsub_singlesubst_format2
562 UINT16 format;
563 UINT16 coverage;
564 UINT16 count;
565 UINT16 substitutes[1];
568 struct ot_gsub_multsubst_format1
570 UINT16 format;
571 UINT16 coverage;
572 UINT16 seq_count;
573 UINT16 seq[1];
576 struct ot_gsub_altsubst_format1
578 UINT16 format;
579 UINT16 coverage;
580 UINT16 count;
581 UINT16 sets[1];
584 struct ot_gsub_ligsubst_format1
586 UINT16 format;
587 UINT16 coverage;
588 UINT16 lig_set_count;
589 UINT16 lig_sets[1];
592 struct ot_gsub_ligset
594 UINT16 count;
595 UINT16 offsets[1];
598 struct ot_gsub_lig
600 UINT16 lig_glyph;
601 UINT16 comp_count;
602 UINT16 components[1];
605 struct ot_gsubgpos_context_format1
607 UINT16 format;
608 UINT16 coverage;
609 UINT16 ruleset_count;
610 UINT16 rulesets[1];
613 struct ot_gsubgpos_ruleset
615 UINT16 count;
616 UINT16 offsets[1];
619 struct ot_feature
621 WORD feature_params;
622 WORD lookup_count;
623 WORD lookuplist_index[1];
626 struct ot_lookup_list
628 WORD lookup_count;
629 WORD lookup[1];
632 struct ot_lookup_table
634 WORD lookup_type;
635 WORD flags;
636 WORD subtable_count;
637 WORD subtable[1];
640 #define GLYPH_NOT_COVERED (~0u)
642 struct ot_coverage_format1
644 WORD format;
645 WORD glyph_count;
646 WORD glyphs[1];
649 struct ot_coverage_range
651 WORD start_glyph;
652 WORD end_glyph;
653 WORD startcoverage_index;
656 struct ot_coverage_format2
658 WORD format;
659 WORD range_count;
660 struct ot_coverage_range ranges[1];
663 struct ot_gpos_device_table
665 WORD start_size;
666 WORD end_size;
667 WORD format;
668 WORD values[1];
671 struct ot_gpos_singlepos_format1
673 WORD format;
674 WORD coverage;
675 WORD value_format;
676 WORD value[1];
679 struct ot_gpos_singlepos_format2
681 WORD format;
682 WORD coverage;
683 WORD value_format;
684 WORD value_count;
685 WORD values[1];
688 struct ot_gpos_pairvalue
690 WORD second_glyph;
691 BYTE data[1];
694 struct ot_gpos_pairset
696 WORD pairvalue_count;
697 struct ot_gpos_pairvalue pairvalues[1];
700 struct ot_gpos_pairpos_format1
702 WORD format;
703 WORD coverage;
704 WORD value_format1;
705 WORD value_format2;
706 WORD pairset_count;
707 WORD pairsets[1];
710 struct ot_gpos_pairpos_format2
712 WORD format;
713 WORD coverage;
714 WORD value_format1;
715 WORD value_format2;
716 WORD class_def1;
717 WORD class_def2;
718 WORD class1_count;
719 WORD class2_count;
720 WORD values[1];
723 struct ot_gpos_anchor_format1
725 WORD format;
726 short x_coord;
727 short y_coord;
730 struct ot_gpos_anchor_format2
732 WORD format;
733 short x_coord;
734 short y_coord;
735 WORD anchor_point;
738 struct ot_gpos_anchor_format3
740 WORD format;
741 short x_coord;
742 short y_coord;
743 WORD x_dev_offset;
744 WORD y_dev_offset;
747 struct ot_gpos_cursive_format1
749 WORD format;
750 WORD coverage;
751 WORD count;
752 WORD anchors[1];
755 struct ot_gpos_mark_record
757 WORD mark_class;
758 WORD mark_anchor;
761 struct ot_gpos_mark_array
763 WORD count;
764 struct ot_gpos_mark_record records[1];
767 struct ot_gpos_base_array
769 WORD count;
770 WORD offsets[1];
773 struct ot_gpos_mark_to_base_format1
775 WORD format;
776 WORD mark_coverage;
777 WORD base_coverage;
778 WORD mark_class_count;
779 WORD mark_array;
780 WORD base_array;
783 struct ot_gpos_mark_to_lig_format1
785 WORD format;
786 WORD mark_coverage;
787 WORD lig_coverage;
788 WORD mark_class_count;
789 WORD mark_array;
790 WORD lig_array;
793 struct ot_gpos_mark_to_mark_format1
795 WORD format;
796 WORD mark1_coverage;
797 WORD mark2_coverage;
798 WORD mark_class_count;
799 WORD mark1_array;
800 WORD mark2_array;
803 struct kern_header
805 WORD version;
806 WORD table_count;
809 struct kern_subtable_header
811 WORD version;
812 WORD length;
813 WORD coverage;
816 #include "poppack.h"
818 enum TT_NAME_WINDOWS_ENCODING_ID
820 TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
821 TT_NAME_WINDOWS_ENCODING_UNICODE_BMP,
822 TT_NAME_WINDOWS_ENCODING_SJIS,
823 TT_NAME_WINDOWS_ENCODING_PRC,
824 TT_NAME_WINDOWS_ENCODING_BIG5,
825 TT_NAME_WINDOWS_ENCODING_WANSUNG,
826 TT_NAME_WINDOWS_ENCODING_JOHAB,
827 TT_NAME_WINDOWS_ENCODING_RESERVED1,
828 TT_NAME_WINDOWS_ENCODING_RESERVED2,
829 TT_NAME_WINDOWS_ENCODING_RESERVED3,
830 TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
833 enum TT_NAME_MAC_ENCODING_ID
835 TT_NAME_MAC_ENCODING_ROMAN = 0,
836 TT_NAME_MAC_ENCODING_JAPANESE,
837 TT_NAME_MAC_ENCODING_TRAD_CHINESE,
838 TT_NAME_MAC_ENCODING_KOREAN,
839 TT_NAME_MAC_ENCODING_ARABIC,
840 TT_NAME_MAC_ENCODING_HEBREW,
841 TT_NAME_MAC_ENCODING_GREEK,
842 TT_NAME_MAC_ENCODING_RUSSIAN,
843 TT_NAME_MAC_ENCODING_RSYMBOL,
844 TT_NAME_MAC_ENCODING_DEVANAGARI,
845 TT_NAME_MAC_ENCODING_GURMUKHI,
846 TT_NAME_MAC_ENCODING_GUJARATI,
847 TT_NAME_MAC_ENCODING_ORIYA,
848 TT_NAME_MAC_ENCODING_BENGALI,
849 TT_NAME_MAC_ENCODING_TAMIL,
850 TT_NAME_MAC_ENCODING_TELUGU,
851 TT_NAME_MAC_ENCODING_KANNADA,
852 TT_NAME_MAC_ENCODING_MALAYALAM,
853 TT_NAME_MAC_ENCODING_SINHALESE,
854 TT_NAME_MAC_ENCODING_BURMESE,
855 TT_NAME_MAC_ENCODING_KHMER,
856 TT_NAME_MAC_ENCODING_THAI,
857 TT_NAME_MAC_ENCODING_LAOTIAN,
858 TT_NAME_MAC_ENCODING_GEORGIAN,
859 TT_NAME_MAC_ENCODING_ARMENIAN,
860 TT_NAME_MAC_ENCODING_SIMPL_CHINESE,
861 TT_NAME_MAC_ENCODING_TIBETAN,
862 TT_NAME_MAC_ENCODING_MONGOLIAN,
863 TT_NAME_MAC_ENCODING_GEEZ,
864 TT_NAME_MAC_ENCODING_SLAVIC,
865 TT_NAME_MAC_ENCODING_VIETNAMESE,
866 TT_NAME_MAC_ENCODING_SINDHI,
867 TT_NAME_MAC_ENCODING_UNINTERPRETED
870 enum TT_NAME_MAC_LANGUAGE_ID
872 TT_NAME_MAC_LANGID_ENGLISH = 0,
873 TT_NAME_MAC_LANGID_FRENCH,
874 TT_NAME_MAC_LANGID_GERMAN,
875 TT_NAME_MAC_LANGID_ITALIAN,
876 TT_NAME_MAC_LANGID_DUTCH,
877 TT_NAME_MAC_LANGID_SWEDISH,
878 TT_NAME_MAC_LANGID_SPANISH,
879 TT_NAME_MAC_LANGID_DANISH,
880 TT_NAME_MAC_LANGID_PORTUGUESE,
881 TT_NAME_MAC_LANGID_NORWEGIAN,
882 TT_NAME_MAC_LANGID_HEBREW,
883 TT_NAME_MAC_LANGID_JAPANESE,
884 TT_NAME_MAC_LANGID_ARABIC,
885 TT_NAME_MAC_LANGID_FINNISH,
886 TT_NAME_MAC_LANGID_GREEK,
887 TT_NAME_MAC_LANGID_ICELANDIC,
888 TT_NAME_MAC_LANGID_MALTESE,
889 TT_NAME_MAC_LANGID_TURKISH,
890 TT_NAME_MAC_LANGID_CROATIAN,
891 TT_NAME_MAC_LANGID_TRAD_CHINESE,
892 TT_NAME_MAC_LANGID_URDU,
893 TT_NAME_MAC_LANGID_HINDI,
894 TT_NAME_MAC_LANGID_THAI,
895 TT_NAME_MAC_LANGID_KOREAN,
896 TT_NAME_MAC_LANGID_LITHUANIAN,
897 TT_NAME_MAC_LANGID_POLISH,
898 TT_NAME_MAC_LANGID_HUNGARIAN,
899 TT_NAME_MAC_LANGID_ESTONIAN,
900 TT_NAME_MAC_LANGID_LATVIAN,
901 TT_NAME_MAC_LANGID_SAMI,
902 TT_NAME_MAC_LANGID_FAROESE,
903 TT_NAME_MAC_LANGID_FARSI,
904 TT_NAME_MAC_LANGID_RUSSIAN,
905 TT_NAME_MAC_LANGID_SIMPL_CHINESE,
906 TT_NAME_MAC_LANGID_FLEMISH,
907 TT_NAME_MAC_LANGID_GAELIC,
908 TT_NAME_MAC_LANGID_ALBANIAN,
909 TT_NAME_MAC_LANGID_ROMANIAN,
910 TT_NAME_MAC_LANGID_CZECH,
911 TT_NAME_MAC_LANGID_SLOVAK,
912 TT_NAME_MAC_LANGID_SLOVENIAN,
913 TT_NAME_MAC_LANGID_YIDDISH,
914 TT_NAME_MAC_LANGID_SERBIAN,
915 TT_NAME_MAC_LANGID_MACEDONIAN,
916 TT_NAME_MAC_LANGID_BULGARIAN,
917 TT_NAME_MAC_LANGID_UKRAINIAN,
918 TT_NAME_MAC_LANGID_BYELORUSSIAN,
919 TT_NAME_MAC_LANGID_UZBEK,
920 TT_NAME_MAC_LANGID_KAZAKH,
921 TT_NAME_MAC_LANGID_AZERB_CYR,
922 TT_NAME_MAC_LANGID_AZERB_ARABIC,
923 TT_NAME_MAC_LANGID_ARMENIAN,
924 TT_NAME_MAC_LANGID_GEORGIAN,
925 TT_NAME_MAC_LANGID_MOLDAVIAN,
926 TT_NAME_MAC_LANGID_KIRGHIZ,
927 TT_NAME_MAC_LANGID_TAJIKI,
928 TT_NAME_MAC_LANGID_TURKMEN,
929 TT_NAME_MAC_LANGID_MONGOLIAN,
930 TT_NAME_MAC_LANGID_MONGOLIAN_CYR,
931 TT_NAME_MAC_LANGID_PASHTO,
932 TT_NAME_MAC_LANGID_KURDISH,
933 TT_NAME_MAC_LANGID_KASHMIRI,
934 TT_NAME_MAC_LANGID_SINDHI,
935 TT_NAME_MAC_LANGID_TIBETAN,
936 TT_NAME_MAC_LANGID_NEPALI,
937 TT_NAME_MAC_LANGID_SANSKRIT,
938 TT_NAME_MAC_LANGID_MARATHI,
939 TT_NAME_MAC_LANGID_BENGALI,
940 TT_NAME_MAC_LANGID_ASSAMESE,
941 TT_NAME_MAC_LANGID_GUJARATI,
942 TT_NAME_MAC_LANGID_PUNJABI,
943 TT_NAME_MAC_LANGID_ORIYA,
944 TT_NAME_MAC_LANGID_MALAYALAM,
945 TT_NAME_MAC_LANGID_KANNADA,
946 TT_NAME_MAC_LANGID_TAMIL,
947 TT_NAME_MAC_LANGID_TELUGU,
948 TT_NAME_MAC_LANGID_SINHALESE,
949 TT_NAME_MAC_LANGID_BURMESE,
950 TT_NAME_MAC_LANGID_KHMER,
951 TT_NAME_MAC_LANGID_LAO,
952 TT_NAME_MAC_LANGID_VIETNAMESE,
953 TT_NAME_MAC_LANGID_INDONESIAN,
954 TT_NAME_MAC_LANGID_TAGALOG,
955 TT_NAME_MAC_LANGID_MALAY_ROMAN,
956 TT_NAME_MAC_LANGID_MALAY_ARABIC,
957 TT_NAME_MAC_LANGID_AMHARIC,
958 TT_NAME_MAC_LANGID_TIGRINYA,
959 TT_NAME_MAC_LANGID_GALLA,
960 TT_NAME_MAC_LANGID_SOMALI,
961 TT_NAME_MAC_LANGID_SWAHILI,
962 TT_NAME_MAC_LANGID_KINYARWANDA,
963 TT_NAME_MAC_LANGID_RUNDI,
964 TT_NAME_MAC_LANGID_NYANJA,
965 TT_NAME_MAC_LANGID_MALAGASY,
966 TT_NAME_MAC_LANGID_ESPERANTO,
967 TT_NAME_MAC_LANGID_WELSH = 128,
968 TT_NAME_MAC_LANGID_BASQUE,
969 TT_NAME_MAC_LANGID_CATALAN,
970 TT_NAME_MAC_LANGID_LATIN,
971 TT_NAME_MAC_LANGID_QUECHUA,
972 TT_NAME_MAC_LANGID_GUARANI,
973 TT_NAME_MAC_LANGID_AYMARA,
974 TT_NAME_MAC_LANGID_TATAR,
975 TT_NAME_MAC_LANGID_UIGHUR,
976 TT_NAME_MAC_LANGID_DZONGKHA,
977 TT_NAME_MAC_LANGID_JAVANESE,
978 TT_NAME_MAC_LANGID_SUNDANESE,
979 TT_NAME_MAC_LANGID_GALICIAN,
980 TT_NAME_MAC_LANGID_AFRIKAANS,
981 TT_NAME_MAC_LANGID_BRETON,
982 TT_NAME_MAC_LANGID_INUKTITUT,
983 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC,
984 TT_NAME_MAC_LANGID_MANX_GAELIC,
985 TT_NAME_MAC_LANGID_IRISH_GAELIC,
986 TT_NAME_MAC_LANGID_TONGAN,
987 TT_NAME_MAC_LANGID_GREEK_POLYTONIC,
988 TT_NAME_MAC_LANGID_GREENLANDIC,
989 TT_NAME_MAC_LANGID_AZER_ROMAN
992 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
993 static const char name_mac_langid_to_locale[][10] = {
994 "en-US",
995 "fr-FR",
996 "de-DE",
997 "it-IT",
998 "nl-NL",
999 "sv-SE",
1000 "es-ES",
1001 "da-DA",
1002 "pt-PT",
1003 "no-NO",
1004 "he-IL",
1005 "ja-JP",
1006 "ar-AR",
1007 "fi-FI",
1008 "el-GR",
1009 "is-IS",
1010 "mt-MT",
1011 "tr-TR",
1012 "hr-HR",
1013 "zh-HK",
1014 "ur-PK",
1015 "hi-IN",
1016 "th-TH",
1017 "ko-KR",
1018 "lt-LT",
1019 "pl-PL",
1020 "hu-HU",
1021 "et-EE",
1022 "lv-LV",
1023 "se-NO",
1024 "fo-FO",
1025 "fa-IR",
1026 "ru-RU",
1027 "zh-CN",
1028 "nl-BE",
1029 "gd-GB",
1030 "sq-AL",
1031 "ro-RO",
1032 "cs-CZ",
1033 "sk-SK",
1034 "sl-SI",
1036 "sr-Latn",
1037 "mk-MK",
1038 "bg-BG",
1039 "uk-UA",
1040 "be-BY",
1041 "uz-Latn",
1042 "kk-KZ",
1043 "az-Cyrl-AZ",
1044 "az-AZ",
1045 "hy-AM",
1046 "ka-GE",
1049 "tg-TJ",
1050 "tk-TM",
1051 "mn-Mong",
1052 "mn-MN",
1053 "ps-AF",
1054 "ku-Arab",
1056 "sd-Arab",
1057 "bo-CN",
1058 "ne-NP",
1059 "sa-IN",
1060 "mr-IN",
1061 "bn-IN",
1062 "as-IN",
1063 "gu-IN",
1064 "pa-Arab",
1065 "or-IN",
1066 "ml-IN",
1067 "kn-IN",
1068 "ta-LK",
1069 "te-IN",
1070 "si-LK",
1072 "km-KH",
1073 "lo-LA",
1074 "vi-VN",
1075 "id-ID",
1077 "ms-MY",
1078 "ms-Arab",
1079 "am-ET",
1080 "ti-ET",
1083 "sw-KE",
1084 "rw-RW",
1122 "cy-GB",
1123 "eu-ES",
1124 "ca-ES",
1129 "tt-RU",
1130 "ug-CN",
1134 "gl-ES",
1135 "af-ZA",
1136 "br-FR",
1137 "iu-Latn-CA",
1138 "gd-GB",
1140 "ga-IE",
1143 "kl-GL",
1144 "az-Latn"
1147 enum OPENTYPE_STRING_ID
1149 OPENTYPE_STRING_COPYRIGHT_NOTICE = 0,
1150 OPENTYPE_STRING_FAMILY_NAME,
1151 OPENTYPE_STRING_SUBFAMILY_NAME,
1152 OPENTYPE_STRING_UNIQUE_IDENTIFIER,
1153 OPENTYPE_STRING_FULL_FONTNAME,
1154 OPENTYPE_STRING_VERSION_STRING,
1155 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1156 OPENTYPE_STRING_TRADEMARK,
1157 OPENTYPE_STRING_MANUFACTURER,
1158 OPENTYPE_STRING_DESIGNER,
1159 OPENTYPE_STRING_DESCRIPTION,
1160 OPENTYPE_STRING_VENDOR_URL,
1161 OPENTYPE_STRING_DESIGNER_URL,
1162 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1163 OPENTYPE_STRING_LICENSE_INFO_URL,
1164 OPENTYPE_STRING_RESERVED_ID15,
1165 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1166 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1167 OPENTYPE_STRING_COMPATIBLE_FULLNAME,
1168 OPENTYPE_STRING_SAMPLE_TEXT,
1169 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1170 OPENTYPE_STRING_WWS_FAMILY_NAME,
1171 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
1174 static const UINT16 dwriteid_to_opentypeid[DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME + 1] =
1176 (UINT16)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
1177 OPENTYPE_STRING_COPYRIGHT_NOTICE,
1178 OPENTYPE_STRING_VERSION_STRING,
1179 OPENTYPE_STRING_TRADEMARK,
1180 OPENTYPE_STRING_MANUFACTURER,
1181 OPENTYPE_STRING_DESIGNER,
1182 OPENTYPE_STRING_DESIGNER_URL,
1183 OPENTYPE_STRING_DESCRIPTION,
1184 OPENTYPE_STRING_VENDOR_URL,
1185 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1186 OPENTYPE_STRING_LICENSE_INFO_URL,
1187 OPENTYPE_STRING_FAMILY_NAME,
1188 OPENTYPE_STRING_SUBFAMILY_NAME,
1189 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1190 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1191 OPENTYPE_STRING_SAMPLE_TEXT,
1192 OPENTYPE_STRING_FULL_FONTNAME,
1193 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1194 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1195 OPENTYPE_STRING_WWS_FAMILY_NAME,
1198 /* CPAL table */
1199 struct cpal_header_0
1201 USHORT version;
1202 USHORT num_palette_entries;
1203 USHORT num_palettes;
1204 USHORT num_color_records;
1205 ULONG offset_first_color_record;
1206 USHORT color_record_indices[1];
1209 struct cpal_color_record
1211 BYTE blue;
1212 BYTE green;
1213 BYTE red;
1214 BYTE alpha;
1217 /* COLR table */
1218 struct colr_header
1220 USHORT version;
1221 USHORT num_baseglyph_records;
1222 ULONG offset_baseglyph_records;
1223 ULONG offset_layer_records;
1224 USHORT num_layer_records;
1227 struct colr_baseglyph_record
1229 USHORT glyph;
1230 USHORT first_layer_index;
1231 USHORT num_layers;
1234 struct colr_layer_record
1236 USHORT glyph;
1237 USHORT palette_index;
1240 struct meta_data_map
1242 DWORD tag;
1243 DWORD offset;
1244 DWORD length;
1247 struct meta_header
1249 DWORD version;
1250 DWORD flags;
1251 DWORD reserved;
1252 DWORD data_maps_count;
1253 struct meta_data_map maps[1];
1256 static const void *table_read_ensure(const struct dwrite_fonttable *table, unsigned int offset, unsigned int size)
1258 if (size > table->size || offset > table->size - size)
1259 return NULL;
1261 return table->data + offset;
1264 static WORD table_read_be_word(const struct dwrite_fonttable *table, unsigned int offset)
1266 const WORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1267 return ptr ? GET_BE_WORD(*ptr) : 0;
1270 static DWORD table_read_be_dword(const struct dwrite_fonttable *table, unsigned int offset)
1272 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1273 return ptr ? GET_BE_DWORD(*ptr) : 0;
1276 static DWORD table_read_dword(const struct dwrite_fonttable *table, unsigned int offset)
1278 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1279 return ptr ? *ptr : 0;
1282 BOOL is_face_type_supported(DWRITE_FONT_FACE_TYPE type)
1284 return (type == DWRITE_FONT_FACE_TYPE_CFF) ||
1285 (type == DWRITE_FONT_FACE_TYPE_TRUETYPE) ||
1286 (type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) ||
1287 (type == DWRITE_FONT_FACE_TYPE_RAW_CFF);
1290 typedef HRESULT (*dwrite_fontfile_analyzer)(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1291 DWRITE_FONT_FACE_TYPE *face_type);
1293 static HRESULT opentype_ttc_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1294 DWRITE_FONT_FACE_TYPE *face_type)
1296 static const DWORD ttctag = MS_TTCF_TAG;
1297 const TTC_Header_V1 *header;
1298 void *context;
1299 HRESULT hr;
1301 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(header), &context);
1302 if (FAILED(hr))
1303 return hr;
1305 if (!memcmp(header->TTCTag, &ttctag, sizeof(ttctag))) {
1306 *font_count = GET_BE_DWORD(header->numFonts);
1307 *file_type = DWRITE_FONT_FILE_TYPE_OPENTYPE_COLLECTION;
1308 *face_type = DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION;
1311 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1313 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1316 static HRESULT opentype_ttf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1317 DWRITE_FONT_FACE_TYPE *face_type)
1319 const DWORD *header;
1320 void *context;
1321 HRESULT hr;
1323 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1324 if (FAILED(hr))
1325 return hr;
1327 if (GET_BE_DWORD(*header) == 0x10000) {
1328 *font_count = 1;
1329 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
1330 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
1333 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1335 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1338 static HRESULT opentype_otf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1339 DWRITE_FONT_FACE_TYPE *face_type)
1341 const DWORD *header;
1342 void *context;
1343 HRESULT hr;
1345 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1346 if (FAILED(hr))
1347 return hr;
1349 if (GET_BE_DWORD(*header) == MS_OTTO_TAG) {
1350 *font_count = 1;
1351 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
1352 *face_type = DWRITE_FONT_FACE_TYPE_CFF;
1355 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1357 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1360 static HRESULT opentype_type1_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1361 DWRITE_FONT_FACE_TYPE *face_type)
1363 #include "pshpack1.h"
1364 /* Specified in Adobe TechNote #5178 */
1365 struct pfm_header {
1366 WORD dfVersion;
1367 DWORD dfSize;
1368 char data0[95];
1369 DWORD dfDevice;
1370 char data1[12];
1372 #include "poppack.h"
1373 struct type1_header {
1374 WORD tag;
1375 char data[14];
1377 const struct type1_header *header;
1378 void *context;
1379 HRESULT hr;
1381 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1382 if (FAILED(hr))
1383 return hr;
1385 /* tag is followed by plain text section */
1386 if (header->tag == 0x8001 &&
1387 (!memcmp(header->data, "%!PS-AdobeFont", 14) ||
1388 !memcmp(header->data, "%!FontType", 10))) {
1389 *font_count = 1;
1390 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFB;
1391 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1394 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1396 /* let's see if it's a .pfm metrics file */
1397 if (*file_type == DWRITE_FONT_FILE_TYPE_UNKNOWN) {
1398 const struct pfm_header *pfm_header;
1399 UINT64 filesize;
1400 DWORD offset;
1401 BOOL header_checked;
1403 hr = IDWriteFontFileStream_GetFileSize(stream, &filesize);
1404 if (FAILED(hr))
1405 return hr;
1407 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&pfm_header, 0, sizeof(*pfm_header), &context);
1408 if (FAILED(hr))
1409 return hr;
1411 offset = pfm_header->dfDevice;
1412 header_checked = pfm_header->dfVersion == 0x100 && pfm_header->dfSize == filesize;
1413 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1415 /* as a last test check static string in PostScript information section */
1416 if (header_checked) {
1417 static const char postscript[] = "PostScript";
1418 char *devtype_name;
1420 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&devtype_name, offset, sizeof(postscript), &context);
1421 if (FAILED(hr))
1422 return hr;
1424 if (!memcmp(devtype_name, postscript, sizeof(postscript))) {
1425 *font_count = 1;
1426 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFM;
1427 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1430 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1434 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1437 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, BOOL *supported, DWRITE_FONT_FILE_TYPE *file_type,
1438 DWRITE_FONT_FACE_TYPE *face_type, UINT32 *face_count)
1440 static dwrite_fontfile_analyzer fontfile_analyzers[] = {
1441 opentype_ttf_analyzer,
1442 opentype_otf_analyzer,
1443 opentype_ttc_analyzer,
1444 opentype_type1_analyzer,
1445 NULL
1447 dwrite_fontfile_analyzer *analyzer = fontfile_analyzers;
1448 DWRITE_FONT_FACE_TYPE face;
1449 HRESULT hr;
1451 if (!face_type)
1452 face_type = &face;
1454 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
1455 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
1456 *face_count = 0;
1458 while (*analyzer) {
1459 hr = (*analyzer)(stream, face_count, file_type, face_type);
1460 if (FAILED(hr))
1461 return hr;
1463 if (hr == S_OK)
1464 break;
1466 analyzer++;
1469 *supported = is_face_type_supported(*face_type);
1470 return S_OK;
1473 HRESULT opentype_try_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag, const void **table_data,
1474 void **table_context, UINT32 *table_size, BOOL *found)
1476 void *table_directory_context, *sfnt_context;
1477 TT_TableRecord *table_record = NULL;
1478 TTC_SFNT_V1 *font_header = NULL;
1479 UINT32 table_offset = 0;
1480 UINT16 table_count;
1481 HRESULT hr;
1483 if (found) *found = FALSE;
1484 if (table_size) *table_size = 0;
1486 *table_data = NULL;
1487 *table_context = NULL;
1489 if (stream_desc->face_type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) {
1490 const TTC_Header_V1 *ttc_header;
1491 void * ttc_context;
1492 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
1493 if (SUCCEEDED(hr)) {
1494 if (stream_desc->face_index >= GET_BE_DWORD(ttc_header->numFonts))
1495 hr = E_INVALIDARG;
1496 else {
1497 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[stream_desc->face_index]);
1498 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
1500 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, ttc_context);
1503 else
1504 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
1506 if (FAILED(hr))
1507 return hr;
1509 table_count = GET_BE_WORD(font_header->numTables);
1510 table_offset += sizeof(*font_header);
1512 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, sfnt_context);
1514 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void **)&table_record, table_offset,
1515 table_count * sizeof(*table_record), &table_directory_context);
1516 if (hr == S_OK) {
1517 UINT16 i;
1519 for (i = 0; i < table_count; i++) {
1520 if (table_record->tag == tag) {
1521 UINT32 offset = GET_BE_DWORD(table_record->offset);
1522 UINT32 length = GET_BE_DWORD(table_record->length);
1524 if (found)
1525 *found = TRUE;
1526 if (table_size)
1527 *table_size = length;
1528 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, table_data, offset,
1529 length, table_context);
1530 break;
1532 table_record++;
1535 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, table_directory_context);
1538 return hr;
1541 static HRESULT opentype_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag,
1542 struct dwrite_fonttable *table)
1544 return opentype_try_get_font_table(stream_desc, tag, (const void **)&table->data, &table->context, &table->size, &table->exists);
1547 /**********
1548 * CMAP
1549 **********/
1551 static UINT16 opentype_cmap_format0_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1553 const UINT8 *glyphs = cmap->data;
1554 return (ch < 0xff) ? glyphs[ch] : 0;
1557 static unsigned int opentype_cmap_format0_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1558 DWRITE_UNICODE_RANGE *ranges)
1560 if (count > 0)
1562 ranges->first = 0;
1563 ranges->last = 255;
1566 return 1;
1569 struct cmap_format4_compare_context
1571 const struct dwrite_cmap *cmap;
1572 unsigned int ch;
1575 static int __cdecl cmap_format4_compare_range(const void *a, const void *b)
1577 const struct cmap_format4_compare_context *key = a;
1578 const UINT16 *end = b;
1579 unsigned int idx;
1581 if (key->ch > GET_BE_WORD(*end))
1582 return 1;
1584 idx = end - key->cmap->u.format4.ends;
1585 if (key->ch < GET_BE_WORD(key->cmap->u.format4.starts[idx]))
1586 return -1;
1588 return 0;
1591 static UINT16 opentype_cmap_format4_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1593 struct cmap_format4_compare_context key = { .cmap = cmap, .ch = ch };
1594 unsigned int glyph, idx, range_offset;
1595 const UINT16 *end_found;
1597 /* Look up range. */
1598 end_found = bsearch(&key, cmap->u.format4.ends, cmap->u.format4.seg_count, sizeof(*cmap->u.format4.ends),
1599 cmap_format4_compare_range);
1600 if (!end_found)
1601 return 0;
1603 idx = end_found - cmap->u.format4.ends;
1605 range_offset = GET_BE_WORD(cmap->u.format4.id_range_offset[idx]);
1607 if (!range_offset)
1609 glyph = ch + GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1611 else
1613 unsigned int index = range_offset / 2 + (ch - GET_BE_WORD(cmap->u.format4.starts[idx])) + idx - cmap->u.format4.seg_count;
1614 if (index >= cmap->u.format4.glyph_id_array_len)
1615 return 0;
1616 glyph = GET_BE_WORD(cmap->u.format4.glyph_id_array[index]);
1617 if (!glyph)
1618 return 0;
1619 glyph += GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1622 return glyph & 0xffff;
1625 static unsigned int opentype_cmap_format4_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1626 DWRITE_UNICODE_RANGE *ranges)
1628 unsigned int i;
1630 count = min(count, cmap->u.format4.seg_count);
1632 for (i = 0; i < count; ++i)
1634 ranges[i].first = GET_BE_WORD(cmap->u.format4.starts[i]);
1635 ranges[i].last = GET_BE_WORD(cmap->u.format4.ends[i]);
1638 return cmap->u.format4.seg_count;
1641 static UINT16 opentype_cmap_format6_10_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1643 const UINT16 *glyphs = cmap->data;
1644 if (ch < cmap->u.format6_10.first || ch > cmap->u.format6_10.last) return 0;
1645 return glyphs[ch - cmap->u.format6_10.first];
1648 static unsigned int opentype_cmap_format6_10_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1649 DWRITE_UNICODE_RANGE *ranges)
1651 if (count > 0)
1653 ranges->first = cmap->u.format6_10.first;
1654 ranges->last = cmap->u.format6_10.last;
1657 return 1;
1660 static int __cdecl cmap_format12_13_compare_group(const void *a, const void *b)
1662 const unsigned int *ch = a;
1663 const UINT32 *group = b;
1665 if (*ch > GET_BE_DWORD(group[1]))
1666 return 1;
1668 if (*ch < GET_BE_DWORD(group[0]))
1669 return -1;
1671 return 0;
1674 static UINT16 opentype_cmap_format12_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1676 const UINT32 *groups = cmap->data;
1677 const UINT32 *group_found;
1679 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1680 cmap_format12_13_compare_group)))
1681 return 0;
1683 return GET_BE_DWORD(group_found[0]) <= GET_BE_DWORD(group_found[1]) ?
1684 GET_BE_DWORD(group_found[2]) + (ch - GET_BE_DWORD(group_found[0])) : 0;
1687 static unsigned int opentype_cmap_format12_13_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1688 DWRITE_UNICODE_RANGE *ranges)
1690 unsigned int i, group_count = cmap->u.format12_13.group_count;
1691 const UINT32 *groups = cmap->data;
1693 count = min(count, group_count);
1695 for (i = 0; i < count; ++i)
1697 ranges[i].first = GET_BE_DWORD(groups[3 * i]);
1698 ranges[i].last = GET_BE_DWORD(groups[3 * i + 1]);
1701 return group_count;
1704 static UINT16 opentype_cmap_format13_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1706 const UINT32 *groups = cmap->data;
1707 const UINT32 *group_found;
1709 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1710 cmap_format12_13_compare_group)))
1711 return 0;
1713 return GET_BE_DWORD(group_found[2]);
1716 static UINT16 opentype_cmap_dummy_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1718 return 0;
1721 static unsigned int opentype_cmap_dummy_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1722 DWRITE_UNICODE_RANGE *ranges)
1724 return 0;
1727 UINT16 opentype_cmap_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1729 UINT16 glyph;
1731 if (!cmap->get_glyph) return 0;
1732 glyph = cmap->get_glyph(cmap, ch);
1733 if (!glyph && cmap->symbol && ch <= 0xff)
1734 glyph = cmap->get_glyph(cmap, ch + 0xf000);
1735 return glyph;
1738 static int __cdecl cmap_header_compare(const void *a, const void *b)
1740 const UINT16 *key = a;
1741 const UINT16 *record = b;
1743 /* Platform. */
1744 if (key[0] < GET_BE_WORD(record[0])) return -1;
1745 if (key[0] > GET_BE_WORD(record[0])) return 1;
1746 /* Encoding. */
1747 if (key[1] < GET_BE_WORD(record[1])) return -1;
1748 if (key[1] > GET_BE_WORD(record[1])) return 1;
1750 return 0;
1753 void dwrite_cmap_init(struct dwrite_cmap *cmap, IDWriteFontFile *file, unsigned int face_index,
1754 DWRITE_FONT_FACE_TYPE face_type)
1756 static const UINT16 encodings[][2] =
1758 { 3, 0 }, /* MS Symbol encoding is preferred. */
1759 { 3, 10 },
1760 { 0, 6 },
1761 { 0, 4 },
1762 { 3, 1 },
1763 { 0, 3 },
1764 { 0, 2 },
1765 { 0, 1 },
1766 { 0, 0 },
1768 const struct cmap_encoding_record *records, *found_record = NULL;
1769 unsigned int length, offset, format, count, f, i, num_records;
1770 struct file_stream_desc stream_desc;
1771 struct dwrite_fonttable table;
1772 const UINT16 *pair = NULL;
1773 HRESULT hr;
1775 if (cmap->data) return;
1777 /* For fontface stream is already available and preset. */
1778 if (!cmap->stream && FAILED(hr = get_filestream_from_file(file, &cmap->stream)))
1780 WARN("Failed to get file stream, hr %#x.\n", hr);
1781 goto failed;
1784 stream_desc.stream = cmap->stream;
1785 stream_desc.face_type = face_type;
1786 stream_desc.face_index = face_index;
1788 opentype_get_font_table(&stream_desc, MS_CMAP_TAG, &table);
1789 if (!table.exists)
1790 goto failed;
1791 cmap->table_context = table.context;
1793 num_records = table_read_be_word(&table, 2);
1794 records = table_read_ensure(&table, 4, sizeof(*records) * num_records);
1796 for (i = 0; i < ARRAY_SIZE(encodings); ++i)
1798 pair = encodings[i];
1799 if ((found_record = bsearch(pair, records, num_records, sizeof(*records), cmap_header_compare)))
1800 break;
1803 if (!found_record)
1805 WARN("No suitable cmap table were found.\n");
1806 goto failed;
1809 /* Symbol encoding. */
1810 cmap->symbol = pair[0] == 3 && pair[1] == 0;
1811 offset = GET_BE_DWORD(found_record->offset);
1813 format = table_read_be_word(&table, offset);
1815 switch (format)
1817 case 0:
1818 cmap->data = table_read_ensure(&table, offset + 6, 256);
1819 cmap->get_glyph = opentype_cmap_format0_get_glyph;
1820 cmap->get_ranges = opentype_cmap_format0_get_ranges;
1821 break;
1822 case 4:
1823 length = table_read_be_word(&table, offset + 2);
1824 cmap->u.format4.seg_count = count = table_read_be_word(&table, offset + 6) / 2;
1825 cmap->u.format4.ends = table_read_ensure(&table, offset + 14, count * 2);
1826 cmap->u.format4.starts = cmap->u.format4.ends + count + 1;
1827 cmap->u.format4.id_delta = cmap->u.format4.starts + count;
1828 cmap->u.format4.id_range_offset = cmap->u.format4.id_delta + count;
1829 cmap->u.format4.glyph_id_array = cmap->data = cmap->u.format4.id_range_offset + count;
1830 cmap->u.format4.glyph_id_array_len = (length - 16 - 8 * count) / 2;
1831 cmap->get_glyph = opentype_cmap_format4_get_glyph;
1832 cmap->get_ranges = opentype_cmap_format4_get_ranges;
1833 break;
1834 case 6:
1835 case 10:
1836 /* Format 10 uses 4 byte fields. */
1837 f = format == 6 ? 1 : 2;
1838 cmap->u.format6_10.first = table_read_be_word(&table, offset + f * 6);
1839 count = table_read_be_word(&table, offset + f * 8);
1840 cmap->u.format6_10.last = cmap->u.format6_10.first + count;
1841 cmap->data = table_read_ensure(&table, offset + f * 10, count * 2);
1842 cmap->get_glyph = opentype_cmap_format6_10_get_glyph;
1843 cmap->get_ranges = opentype_cmap_format6_10_get_ranges;
1844 break;
1845 case 12:
1846 case 13:
1847 cmap->u.format12_13.group_count = count = table_read_be_dword(&table, offset + 12);
1848 cmap->data = table_read_ensure(&table, offset + 16, count * 3 * 4);
1849 cmap->get_glyph = format == 12 ? opentype_cmap_format12_get_glyph : opentype_cmap_format13_get_glyph;
1850 cmap->get_ranges = opentype_cmap_format12_13_get_ranges;
1851 break;
1852 default:
1853 WARN("Unhandled subtable format %u.\n", format);
1856 failed:
1858 if (!cmap->data)
1860 /* Dummy implementation, returns 0 unconditionally. */
1861 cmap->data = cmap;
1862 cmap->get_glyph = opentype_cmap_dummy_get_glyph;
1863 cmap->get_ranges = opentype_cmap_dummy_get_ranges;
1867 void dwrite_cmap_release(struct dwrite_cmap *cmap)
1869 if (cmap->stream)
1871 IDWriteFontFileStream_ReleaseFileFragment(cmap->stream, cmap->table_context);
1872 IDWriteFontFileStream_Release(cmap->stream);
1874 cmap->data = NULL;
1875 cmap->stream = NULL;
1878 HRESULT opentype_cmap_get_unicode_ranges(const struct dwrite_cmap *cmap, unsigned int max_count, DWRITE_UNICODE_RANGE *ranges,
1879 unsigned int *count)
1881 if (!cmap->data)
1882 return E_FAIL;
1884 *count = cmap->get_ranges(cmap, max_count, ranges);
1886 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
1889 void opentype_get_font_typo_metrics(struct file_stream_desc *stream_desc, unsigned int *ascent, unsigned int *descent)
1891 struct dwrite_fonttable os2;
1893 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1895 *ascent = *descent = 0;
1897 if (os2.size >= FIELD_OFFSET(struct tt_os2, sTypoLineGap))
1899 SHORT value = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoDescender));
1900 *ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoAscender));
1901 *descent = value < 0 ? -value : 0;
1904 if (os2.data)
1905 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
1908 void opentype_get_font_metrics(struct file_stream_desc *stream_desc, DWRITE_FONT_METRICS1 *metrics, DWRITE_CARET_METRICS *caret)
1910 struct dwrite_fonttable os2, head, post, hhea;
1912 memset(metrics, 0, sizeof(*metrics));
1914 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1915 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
1916 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
1917 opentype_get_font_table(stream_desc, MS_HHEA_TAG, &hhea);
1919 if (head.data)
1921 metrics->designUnitsPerEm = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, unitsPerEm));
1922 metrics->glyphBoxLeft = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, xMin));
1923 metrics->glyphBoxTop = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, yMax));
1924 metrics->glyphBoxRight = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, xMax));
1925 metrics->glyphBoxBottom = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, yMin));
1928 if (caret)
1930 if (hhea.data)
1932 caret->slopeRise = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretSlopeRise));
1933 caret->slopeRun = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretSlopeRun));
1934 caret->offset = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretOffset));
1936 else
1937 memset(caret, 0, sizeof(*caret));
1940 if (os2.data)
1942 USHORT version = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, version));
1944 metrics->ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWinAscent));
1945 /* Some fonts have usWinDescent value stored as signed short, which could be wrongly
1946 interpreted as large unsigned value. */
1947 metrics->descent = abs((SHORT)table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWinDescent)));
1949 /* Line gap is estimated using two sets of ascender/descender values and 'hhea' line gap. */
1950 if (hhea.data)
1952 SHORT descender = (SHORT)table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, descender));
1953 INT32 linegap;
1955 linegap = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, ascender)) + abs(descender) +
1956 table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, linegap)) - metrics->ascent - metrics->descent;
1957 metrics->lineGap = linegap > 0 ? linegap : 0;
1960 metrics->strikethroughPosition = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, yStrikeoutPosition));
1961 metrics->strikethroughThickness = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, yStrikeoutSize));
1962 metrics->subscriptPositionX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptXOffset));
1963 /* Y offset is stored as positive offset below baseline */
1964 metrics->subscriptPositionY = -table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptYOffset));
1965 metrics->subscriptSizeX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptXSize));
1966 metrics->subscriptSizeY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptYSize));
1967 metrics->superscriptPositionX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptXOffset));
1968 metrics->superscriptPositionY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptYOffset));
1969 metrics->superscriptSizeX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptXSize));
1970 metrics->superscriptSizeY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptYSize));
1972 /* version 2 fields */
1973 if (version >= 2)
1975 metrics->capHeight = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sCapHeight));
1976 metrics->xHeight = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sxHeight));
1979 if (table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) & OS2_FSSELECTION_USE_TYPO_METRICS)
1981 SHORT descent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoDescender));
1982 metrics->ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoAscender));
1983 metrics->descent = descent < 0 ? -descent : 0;
1984 metrics->lineGap = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoLineGap));
1985 metrics->hasTypographicMetrics = TRUE;
1988 else
1990 metrics->strikethroughPosition = metrics->designUnitsPerEm / 3;
1991 if (hhea.data)
1993 metrics->ascent = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, ascender));
1994 metrics->descent = abs((SHORT)table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, descender)));
1998 if (post.data)
2000 metrics->underlinePosition = table_read_be_word(&post, FIELD_OFFSET(struct tt_post, underlinePosition));
2001 metrics->underlineThickness = table_read_be_word(&post, FIELD_OFFSET(struct tt_post, underlineThickness));
2004 if (metrics->underlineThickness == 0)
2005 metrics->underlineThickness = metrics->designUnitsPerEm / 14;
2006 if (metrics->strikethroughThickness == 0)
2007 metrics->strikethroughThickness = metrics->underlineThickness;
2009 /* estimate missing metrics */
2010 if (metrics->xHeight == 0)
2011 metrics->xHeight = metrics->designUnitsPerEm / 2;
2012 if (metrics->capHeight == 0)
2013 metrics->capHeight = metrics->designUnitsPerEm * 7 / 10;
2015 if (os2.data)
2016 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2017 if (head.data)
2018 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2019 if (post.data)
2020 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2021 if (hhea.data)
2022 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, hhea.context);
2025 void opentype_get_font_properties(struct file_stream_desc *stream_desc, struct dwrite_font_props *props)
2027 struct dwrite_fonttable os2, head, colr, cpal;
2028 BOOL is_symbol, is_monospaced;
2030 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2031 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
2033 /* default stretch, weight and style to normal */
2034 props->stretch = DWRITE_FONT_STRETCH_NORMAL;
2035 props->weight = DWRITE_FONT_WEIGHT_NORMAL;
2036 props->style = DWRITE_FONT_STYLE_NORMAL;
2037 memset(&props->panose, 0, sizeof(props->panose));
2038 memset(&props->fontsig, 0, sizeof(props->fontsig));
2039 memset(&props->lf, 0, sizeof(props->lf));
2040 props->flags = 0;
2042 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
2043 if (os2.data)
2045 USHORT version = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, version));
2046 USHORT fsSelection = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection));
2047 USHORT usWeightClass = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWeightClass));
2048 USHORT usWidthClass = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWidthClass));
2049 const void *panose;
2051 if (usWidthClass > DWRITE_FONT_STRETCH_UNDEFINED && usWidthClass <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
2052 props->stretch = usWidthClass;
2054 if (usWeightClass >= 1 && usWeightClass <= 9)
2055 usWeightClass *= 100;
2057 if (usWeightClass > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
2058 props->weight = DWRITE_FONT_WEIGHT_ULTRA_BLACK;
2059 else if (usWeightClass > 0)
2060 props->weight = usWeightClass;
2062 if (version >= 4 && (fsSelection & OS2_FSSELECTION_OBLIQUE))
2063 props->style = DWRITE_FONT_STYLE_OBLIQUE;
2064 else if (fsSelection & OS2_FSSELECTION_ITALIC)
2065 props->style = DWRITE_FONT_STYLE_ITALIC;
2066 props->lf.lfItalic = !!(fsSelection & OS2_FSSELECTION_ITALIC);
2068 if ((panose = table_read_ensure(&os2, FIELD_OFFSET(struct tt_os2, panose), sizeof(props->panose))))
2069 memcpy(&props->panose, panose, sizeof(props->panose));
2071 /* FONTSIGNATURE */
2072 props->fontsig.fsUsb[0] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange1));
2073 props->fontsig.fsUsb[1] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange2));
2074 props->fontsig.fsUsb[2] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange3));
2075 props->fontsig.fsUsb[3] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange4));
2077 if (version)
2079 props->fontsig.fsCsb[0] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulCodePageRange1));
2080 props->fontsig.fsCsb[1] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulCodePageRange2));
2083 else if (head.data)
2085 USHORT macStyle = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, macStyle));
2087 if (macStyle & TT_HEAD_MACSTYLE_CONDENSED)
2088 props->stretch = DWRITE_FONT_STRETCH_CONDENSED;
2089 else if (macStyle & TT_HEAD_MACSTYLE_EXTENDED)
2090 props->stretch = DWRITE_FONT_STRETCH_EXPANDED;
2092 if (macStyle & TT_HEAD_MACSTYLE_BOLD)
2093 props->weight = DWRITE_FONT_WEIGHT_BOLD;
2095 if (macStyle & TT_HEAD_MACSTYLE_ITALIC) {
2096 props->style = DWRITE_FONT_STYLE_ITALIC;
2097 props->lf.lfItalic = 1;
2101 props->lf.lfWeight = props->weight;
2103 /* FONT_IS_SYMBOL */
2104 if (!(is_symbol = props->panose.familyKind == DWRITE_PANOSE_FAMILY_SYMBOL))
2106 struct dwrite_fonttable cmap;
2107 int i, offset, num_tables;
2109 opentype_get_font_table(stream_desc, MS_CMAP_TAG, &cmap);
2111 if (cmap.data)
2113 num_tables = table_read_be_word(&cmap, FIELD_OFFSET(struct cmap_header, num_tables));
2114 offset = FIELD_OFFSET(struct cmap_header, tables);
2116 for (i = 0; !is_symbol && i < num_tables; ++i)
2118 WORD platform, encoding;
2120 platform = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2121 FIELD_OFFSET(struct cmap_encoding_record, platformID));
2122 encoding = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2123 FIELD_OFFSET(struct cmap_encoding_record, encodingID));
2125 is_symbol = platform == OPENTYPE_CMAP_TABLE_PLATFORM_WIN &&
2126 encoding == OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL;
2129 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cmap.context);
2132 if (is_symbol)
2133 props->flags |= FONT_IS_SYMBOL;
2135 /* FONT_IS_MONOSPACED */
2136 if (!(is_monospaced = props->panose.text.proportion == DWRITE_PANOSE_PROPORTION_MONOSPACED))
2138 struct dwrite_fonttable post;
2140 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
2142 if (post.data)
2144 is_monospaced = !!table_read_dword(&post, FIELD_OFFSET(struct tt_post, fixed_pitch));
2146 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2149 if (is_monospaced)
2150 props->flags |= FONT_IS_MONOSPACED;
2152 /* FONT_IS_COLORED */
2153 opentype_get_font_table(stream_desc, MS_COLR_TAG, &colr);
2154 if (colr.data)
2156 opentype_get_font_table(stream_desc, MS_CPAL_TAG, &cpal);
2157 if (cpal.data)
2159 props->flags |= FONT_IS_COLORED;
2160 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cpal.context);
2163 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, colr.context);
2166 TRACE("stretch %d, weight %d, style %d\n", props->stretch, props->weight, props->style);
2168 if (os2.data)
2169 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2170 if (head.data)
2171 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2174 static UINT get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform, USHORT encoding)
2176 UINT codepage = 0;
2178 switch (platform) {
2179 case OPENTYPE_PLATFORM_UNICODE:
2180 break;
2181 case OPENTYPE_PLATFORM_MAC:
2182 switch (encoding)
2184 case TT_NAME_MAC_ENCODING_ROMAN:
2185 codepage = 10000;
2186 break;
2187 case TT_NAME_MAC_ENCODING_JAPANESE:
2188 codepage = 10001;
2189 break;
2190 case TT_NAME_MAC_ENCODING_TRAD_CHINESE:
2191 codepage = 10002;
2192 break;
2193 case TT_NAME_MAC_ENCODING_KOREAN:
2194 codepage = 10003;
2195 break;
2196 case TT_NAME_MAC_ENCODING_ARABIC:
2197 codepage = 10004;
2198 break;
2199 case TT_NAME_MAC_ENCODING_HEBREW:
2200 codepage = 10005;
2201 break;
2202 case TT_NAME_MAC_ENCODING_GREEK:
2203 codepage = 10006;
2204 break;
2205 case TT_NAME_MAC_ENCODING_RUSSIAN:
2206 codepage = 10007;
2207 break;
2208 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE:
2209 codepage = 10008;
2210 break;
2211 case TT_NAME_MAC_ENCODING_THAI:
2212 codepage = 10021;
2213 break;
2214 default:
2215 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2216 break;
2218 break;
2219 case OPENTYPE_PLATFORM_WIN:
2220 switch (encoding)
2222 case TT_NAME_WINDOWS_ENCODING_SYMBOL:
2223 case TT_NAME_WINDOWS_ENCODING_UNICODE_BMP:
2224 case TT_NAME_WINDOWS_ENCODING_UNICODE_FULL:
2225 break;
2226 case TT_NAME_WINDOWS_ENCODING_SJIS:
2227 codepage = 932;
2228 break;
2229 case TT_NAME_WINDOWS_ENCODING_PRC:
2230 codepage = 936;
2231 break;
2232 case TT_NAME_WINDOWS_ENCODING_BIG5:
2233 codepage = 950;
2234 break;
2235 case TT_NAME_WINDOWS_ENCODING_WANSUNG:
2236 codepage = 20949;
2237 break;
2238 case TT_NAME_WINDOWS_ENCODING_JOHAB:
2239 codepage = 1361;
2240 break;
2241 default:
2242 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2243 break;
2245 break;
2246 default:
2247 FIXME("unknown platform %d\n", platform);
2250 return codepage;
2253 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform, USHORT lang_id, WCHAR *locale, USHORT locale_len)
2255 switch (platform)
2257 case OPENTYPE_PLATFORM_MAC:
2259 const char *locale_name = NULL;
2261 if (lang_id > TT_NAME_MAC_LANGID_AZER_ROMAN)
2262 WARN("invalid mac lang id %d\n", lang_id);
2263 else if (!name_mac_langid_to_locale[lang_id][0])
2264 FIXME("failed to map mac lang id %d to locale name\n", lang_id);
2265 else
2266 locale_name = name_mac_langid_to_locale[lang_id];
2268 if (locale_name)
2269 MultiByteToWideChar(CP_ACP, 0, name_mac_langid_to_locale[lang_id], -1, locale, locale_len);
2270 else
2271 wcscpy(locale, L"en-US");
2272 break;
2274 case OPENTYPE_PLATFORM_WIN:
2275 if (!LCIDToLocaleName(MAKELCID(lang_id, SORT_DEFAULT), locale, locale_len, 0))
2277 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id, SORT_DEFAULT));
2278 wcscpy(locale, L"en-US");
2280 break;
2281 case OPENTYPE_PLATFORM_UNICODE:
2282 wcscpy(locale, L"en-US");
2283 break;
2284 default:
2285 FIXME("unknown platform %d\n", platform);
2289 static BOOL opentype_is_english_namerecord(const struct dwrite_fonttable *table, unsigned int idx)
2291 const struct name_header *header = (const struct name_header *)table->data;
2292 const struct name_record *record;
2294 record = &header->records[idx];
2296 return GET_BE_WORD(record->platformID) == OPENTYPE_PLATFORM_MAC &&
2297 GET_BE_WORD(record->languageID) == TT_NAME_MAC_LANGID_ENGLISH;
2300 static BOOL opentype_decode_namerecord(const struct dwrite_fonttable *table, unsigned int idx,
2301 IDWriteLocalizedStrings *strings)
2303 USHORT lang_id, length, offset, encoding, platform;
2304 const struct name_header *header = (const struct name_header *)table->data;
2305 const struct name_record *record;
2306 unsigned int i, string_offset;
2307 BOOL ret = FALSE;
2308 const void *name;
2310 string_offset = table_read_be_word(table, FIELD_OFFSET(struct name_header, stringOffset));
2312 record = &header->records[idx];
2314 platform = GET_BE_WORD(record->platformID);
2315 lang_id = GET_BE_WORD(record->languageID);
2316 length = GET_BE_WORD(record->length);
2317 offset = GET_BE_WORD(record->offset);
2318 encoding = GET_BE_WORD(record->encodingID);
2320 if (!(name = table_read_ensure(table, string_offset + offset, length)))
2321 return FALSE;
2323 if (lang_id < 0x8000)
2325 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
2326 WCHAR *name_string;
2327 UINT codepage;
2329 codepage = get_name_record_codepage(platform, encoding);
2330 get_name_record_locale(platform, lang_id, locale, ARRAY_SIZE(locale));
2332 if (codepage)
2334 DWORD len = MultiByteToWideChar(codepage, 0, name, length, NULL, 0);
2335 name_string = heap_alloc(sizeof(WCHAR) * (len+1));
2336 MultiByteToWideChar(codepage, 0, name, length, name_string, len);
2337 name_string[len] = 0;
2339 else
2341 length /= sizeof(WCHAR);
2342 name_string = heap_strdupnW(name, length);
2343 for (i = 0; i < length; i++)
2344 name_string[i] = GET_BE_WORD(name_string[i]);
2347 TRACE("string %s for locale %s found\n", debugstr_w(name_string), debugstr_w(locale));
2348 add_localizedstring(strings, locale, name_string);
2349 heap_free(name_string);
2351 ret = !wcscmp(locale, L"en-US");
2353 else
2354 FIXME("handle NAME format 1\n");
2356 return ret;
2359 static HRESULT opentype_get_font_strings_from_id(const struct dwrite_fonttable *table, enum OPENTYPE_STRING_ID id,
2360 IDWriteLocalizedStrings **strings)
2362 int i, count, candidate_mac, candidate_mac_en, candidate_unicode;
2363 const struct name_record *records;
2364 BOOL has_english;
2365 WORD format;
2366 HRESULT hr;
2368 if (!table->data)
2369 return E_FAIL;
2371 if (FAILED(hr = create_localizedstrings(strings)))
2372 return hr;
2374 format = table_read_be_word(table, FIELD_OFFSET(struct name_header, format));
2376 if (format != 0 && format != 1)
2377 FIXME("unsupported NAME format %d\n", format);
2379 count = table_read_be_word(table, FIELD_OFFSET(struct name_header, count));
2381 if (!(records = table_read_ensure(table, FIELD_OFFSET(struct name_header, records),
2382 count * sizeof(struct name_record))))
2384 count = 0;
2387 has_english = FALSE;
2388 candidate_unicode = candidate_mac = candidate_mac_en = -1;
2390 for (i = 0; i < count; i++)
2392 unsigned short platform;
2394 if (GET_BE_WORD(records[i].nameID) != id)
2395 continue;
2397 platform = GET_BE_WORD(records[i].platformID);
2398 switch (platform)
2400 /* Skip Unicode or Mac entries for now, fonts tend to duplicate those
2401 strings as WIN platform entries. If font does not have WIN entry for
2402 this id, we will use Mac or Unicode platform entry while assuming
2403 en-US locale. */
2404 case OPENTYPE_PLATFORM_UNICODE:
2405 if (candidate_unicode == -1)
2406 candidate_unicode = i;
2407 break;
2408 case OPENTYPE_PLATFORM_MAC:
2409 if (candidate_mac == -1)
2410 candidate_mac = i;
2411 if (candidate_mac_en == -1 && opentype_is_english_namerecord(table, i))
2412 candidate_mac_en = i;
2413 break;
2414 case OPENTYPE_PLATFORM_WIN:
2415 has_english |= opentype_decode_namerecord(table, i, *strings);
2416 break;
2417 default:
2418 FIXME("platform %i not supported\n", platform);
2419 break;
2423 if (!get_localizedstrings_count(*strings) && candidate_mac != -1)
2424 has_english |= opentype_decode_namerecord(table, candidate_mac, *strings);
2425 if (!get_localizedstrings_count(*strings) && candidate_unicode != -1)
2426 has_english |= opentype_decode_namerecord(table, candidate_unicode, *strings);
2427 if (!has_english && candidate_mac_en != -1)
2428 opentype_decode_namerecord(table, candidate_mac_en, *strings);
2430 if (!get_localizedstrings_count(*strings))
2432 IDWriteLocalizedStrings_Release(*strings);
2433 *strings = NULL;
2436 if (*strings)
2437 sort_localizedstrings(*strings);
2439 return *strings ? S_OK : E_FAIL;
2442 static WCHAR *meta_get_lng_name(WCHAR *str, WCHAR **ctx)
2444 WCHAR *ret;
2446 if (!str) str = *ctx;
2447 while (*str && wcschr(L", ", *str)) str++;
2448 if (!*str) return NULL;
2449 ret = str++;
2450 while (*str && !wcschr(L", ", *str)) str++;
2451 if (*str) *str++ = 0;
2452 *ctx = str;
2454 return ret;
2457 static HRESULT opentype_get_font_strings_from_meta(const struct file_stream_desc *stream_desc,
2458 DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **ret)
2460 const struct meta_data_map *maps;
2461 IDWriteLocalizedStrings *strings;
2462 struct dwrite_fonttable meta;
2463 DWORD version, i, count, tag;
2464 HRESULT hr;
2466 *ret = NULL;
2468 switch (id)
2470 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2471 tag = MS_DLNG_TAG;
2472 break;
2473 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2474 tag = MS_SLNG_TAG;
2475 break;
2476 default:
2477 WARN("Unexpected id %d.\n", id);
2478 return S_OK;
2481 if (FAILED(hr = create_localizedstrings(&strings)))
2482 return hr;
2484 opentype_get_font_table(stream_desc, MS_META_TAG, &meta);
2486 if (meta.data)
2488 version = table_read_be_dword(&meta, 0);
2489 if (version != 1)
2491 WARN("Unexpected meta table version %d.\n", version);
2492 goto end;
2495 count = table_read_be_dword(&meta, FIELD_OFFSET(struct meta_header, data_maps_count));
2496 if (!(maps = table_read_ensure(&meta, FIELD_OFFSET(struct meta_header, maps),
2497 count * sizeof(struct meta_data_map))))
2498 goto end;
2500 for (i = 0; i < count; ++i)
2502 const char *data;
2504 if (maps[i].tag == tag && maps[i].length)
2506 DWORD length = GET_BE_DWORD(maps[i].length), j;
2508 if ((data = table_read_ensure(&meta, GET_BE_DWORD(maps[i].offset), length)))
2510 WCHAR *ptrW = heap_alloc((length + 1) * sizeof(WCHAR)), *ctx, *token;
2512 if (!ptrW)
2514 hr = E_OUTOFMEMORY;
2515 goto end;
2518 /* Data is stored in comma separated list, ASCII range only. */
2519 for (j = 0; j < length; ++j)
2520 ptrW[j] = data[j];
2521 ptrW[length] = 0;
2523 token = meta_get_lng_name(ptrW, &ctx);
2525 while (token)
2527 add_localizedstring(strings, L"", token);
2528 token = meta_get_lng_name(NULL, &ctx);
2531 heap_free(ptrW);
2535 end:
2536 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, meta.context);
2539 if (IDWriteLocalizedStrings_GetCount(strings))
2540 *ret = strings;
2541 else
2542 IDWriteLocalizedStrings_Release(strings);
2544 return hr;
2547 HRESULT opentype_get_font_info_strings(const struct file_stream_desc *stream_desc, DWRITE_INFORMATIONAL_STRING_ID id,
2548 IDWriteLocalizedStrings **strings)
2550 struct dwrite_fonttable name;
2552 switch (id)
2554 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2555 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2556 opentype_get_font_strings_from_meta(stream_desc, id, strings);
2557 break;
2558 default:
2559 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2560 opentype_get_font_strings_from_id(&name, dwriteid_to_opentypeid[id], strings);
2561 if (name.context)
2562 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2565 return S_OK;
2568 /* FamilyName locating order is WWS Family Name -> Preferred Family Name -> Family Name. If font claims to
2569 have 'Preferred Family Name' in WWS format, then WWS name is not used. */
2570 HRESULT opentype_get_font_familyname(struct file_stream_desc *stream_desc, IDWriteLocalizedStrings **names)
2572 struct dwrite_fonttable os2, name;
2573 UINT16 fsselection;
2574 HRESULT hr;
2576 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2577 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2579 *names = NULL;
2581 /* If Preferred Family doesn't conform to WWS model try WWS name. */
2582 fsselection = os2.data ? table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) : 0;
2583 if (os2.data && !(fsselection & OS2_FSSELECTION_WWS))
2584 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_WWS_FAMILY_NAME, names);
2585 else
2586 hr = E_FAIL;
2588 if (FAILED(hr))
2589 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME, names);
2590 if (FAILED(hr))
2591 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_FAMILY_NAME, names);
2593 if (os2.context)
2594 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2595 if (name.context)
2596 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2598 return hr;
2601 /* FaceName locating order is WWS Face Name -> Preferred Face Name -> Face Name. If font claims to
2602 have 'Preferred Face Name' in WWS format, then WWS name is not used. */
2603 HRESULT opentype_get_font_facename(struct file_stream_desc *stream_desc, WCHAR *lfname, IDWriteLocalizedStrings **names)
2605 struct dwrite_fonttable os2, name;
2606 IDWriteLocalizedStrings *lfnames;
2607 UINT16 fsselection;
2608 HRESULT hr;
2610 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2611 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2613 *names = NULL;
2615 /* if Preferred Family doesn't conform to WWS model try WWS name */
2616 fsselection = os2.data ? table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) : 0;
2617 if (os2.data && !(fsselection & OS2_FSSELECTION_WWS))
2618 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_WWS_SUBFAMILY_NAME, names);
2619 else
2620 hr = E_FAIL;
2622 if (FAILED(hr))
2623 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME, names);
2624 if (FAILED(hr))
2625 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_SUBFAMILY_NAME, names);
2627 /* User locale is preferred, with fallback to en-us. */
2628 *lfname = 0;
2629 if (SUCCEEDED(opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_FAMILY_NAME, &lfnames)))
2631 WCHAR localeW[LOCALE_NAME_MAX_LENGTH];
2632 UINT32 index;
2633 BOOL exists;
2635 exists = FALSE;
2636 if (GetSystemDefaultLocaleName(localeW, ARRAY_SIZE(localeW)))
2637 IDWriteLocalizedStrings_FindLocaleName(lfnames, localeW, &index, &exists);
2639 if (!exists)
2640 IDWriteLocalizedStrings_FindLocaleName(lfnames, L"en-us", &index, &exists);
2642 if (exists) {
2643 UINT32 length = 0;
2644 WCHAR *nameW;
2646 IDWriteLocalizedStrings_GetStringLength(lfnames, index, &length);
2647 nameW = heap_alloc((length + 1) * sizeof(WCHAR));
2648 if (nameW)
2650 *nameW = 0;
2651 IDWriteLocalizedStrings_GetString(lfnames, index, nameW, length + 1);
2652 wcsncpy(lfname, nameW, LF_FACESIZE);
2653 lfname[LF_FACESIZE-1] = 0;
2654 heap_free(nameW);
2658 IDWriteLocalizedStrings_Release(lfnames);
2661 if (os2.context)
2662 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2663 if (name.context)
2664 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2666 return hr;
2669 static const struct ot_langsys *opentype_get_langsys(const struct ot_gsubgpos_table *table, unsigned int script_index,
2670 unsigned int language_index, unsigned int *feature_count)
2672 unsigned int table_offset, langsys_offset;
2673 const struct ot_langsys *langsys = NULL;
2675 *feature_count = 0;
2677 if (!table->table.data || script_index == ~0u)
2678 return NULL;
2680 /* ScriptTable offset. */
2681 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
2682 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
2683 if (!table_offset)
2684 return NULL;
2686 if (language_index == ~0u)
2687 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
2688 else
2689 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
2690 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
2691 FIELD_OFFSET(struct ot_langsys_record, langsys));
2692 langsys_offset += table->script_list + table_offset;
2694 *feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
2695 if (*feature_count)
2696 langsys = table_read_ensure(&table->table, langsys_offset, FIELD_OFFSET(struct ot_langsys, feature_index[*feature_count]));
2697 if (!langsys)
2698 *feature_count = 0;
2700 return langsys;
2703 void opentype_get_typographic_features(struct ot_gsubgpos_table *table, unsigned int script_index,
2704 unsigned int language_index, struct tag_array *t)
2706 unsigned int i, total_feature_count, script_feature_count;
2707 const struct ot_feature_list *feature_list;
2708 const struct ot_langsys *langsys = NULL;
2710 langsys = opentype_get_langsys(table, script_index, language_index, &script_feature_count);
2712 total_feature_count = table_read_be_word(&table->table, table->feature_list);
2713 if (!total_feature_count)
2714 return;
2716 feature_list = table_read_ensure(&table->table, table->feature_list,
2717 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
2718 if (!feature_list)
2719 return;
2721 for (i = 0; i < script_feature_count; ++i)
2723 unsigned int feature_index = GET_BE_WORD(langsys->feature_index[i]);
2724 if (feature_index >= total_feature_count)
2725 continue;
2727 if (!dwrite_array_reserve((void **)&t->tags, &t->capacity, t->count + 1, sizeof(*t->tags)))
2728 return;
2730 t->tags[t->count++] = feature_list->features[feature_index].tag;
2734 static unsigned int find_vdmx_group(const struct vdmx_header *hdr)
2736 WORD num_ratios, i;
2737 const struct vdmx_ratio *ratios = (struct vdmx_ratio *)(hdr + 1);
2738 BYTE dev_x_ratio = 1, dev_y_ratio = 1;
2739 unsigned int group_offset = 0;
2741 num_ratios = GET_BE_WORD(hdr->num_ratios);
2743 for (i = 0; i < num_ratios; i++) {
2745 if (!ratios[i].bCharSet) continue;
2747 if ((ratios[i].xRatio == 0 && ratios[i].yStartRatio == 0 &&
2748 ratios[i].yEndRatio == 0) ||
2749 (ratios[i].xRatio == dev_x_ratio && ratios[i].yStartRatio <= dev_y_ratio &&
2750 ratios[i].yEndRatio >= dev_y_ratio))
2752 group_offset = GET_BE_WORD(*((WORD *)(ratios + num_ratios) + i));
2753 break;
2757 return group_offset;
2760 BOOL opentype_get_vdmx_size(const struct dwrite_fonttable *vdmx, INT emsize, UINT16 *ascent, UINT16 *descent)
2762 unsigned int num_ratios, num_recs, group_offset, i;
2763 const struct vdmx_header *header;
2764 const struct vdmx_group *group;
2766 if (!vdmx->exists)
2767 return FALSE;
2769 num_ratios = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_ratios));
2770 num_recs = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_recs));
2772 header = table_read_ensure(vdmx, 0, sizeof(*header) + num_ratios * sizeof(struct vdmx_ratio) +
2773 num_recs * sizeof(*group));
2775 if (!header)
2776 return FALSE;
2778 group_offset = find_vdmx_group(header);
2779 if (!group_offset)
2780 return FALSE;
2782 num_recs = table_read_be_word(vdmx, group_offset);
2783 group = table_read_ensure(vdmx, group_offset, FIELD_OFFSET(struct vdmx_group, entries[num_recs]));
2785 if (!group)
2786 return FALSE;
2788 if (emsize < group->startsz || emsize >= group->endsz)
2789 return FALSE;
2791 for (i = 0; i < num_recs; ++i)
2793 WORD ppem = GET_BE_WORD(group->entries[i].yPelHeight);
2794 if (ppem > emsize) {
2795 FIXME("interpolate %d\n", emsize);
2796 return FALSE;
2799 if (ppem == emsize) {
2800 *ascent = (SHORT)GET_BE_WORD(group->entries[i].yMax);
2801 *descent = -(SHORT)GET_BE_WORD(group->entries[i].yMin);
2802 return TRUE;
2806 return FALSE;
2809 unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable *gasp, float emsize)
2811 unsigned int version, num_ranges, i;
2812 const struct gasp_header *table;
2813 WORD flags = 0;
2815 if (!gasp->exists)
2816 return 0;
2818 num_ranges = table_read_be_word(gasp, FIELD_OFFSET(struct gasp_header, num_ranges));
2820 table = table_read_ensure(gasp, 0, FIELD_OFFSET(struct gasp_header, ranges[num_ranges]));
2821 if (!table)
2822 return 0;
2824 version = GET_BE_WORD(table->version);
2825 if (version > 1)
2827 ERR("Unsupported gasp table format version %u.\n", version);
2828 goto done;
2831 for (i = 0; i < num_ranges; ++i)
2833 flags = GET_BE_WORD(table->ranges[i].flags);
2834 if (emsize <= GET_BE_WORD(table->ranges[i].max_ppem)) break;
2837 done:
2838 return flags;
2841 unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable *cpal)
2843 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palettes));
2846 unsigned int opentype_get_cpal_paletteentrycount(const struct dwrite_fonttable *cpal)
2848 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palette_entries));
2851 HRESULT opentype_get_cpal_entries(const struct dwrite_fonttable *cpal, unsigned int palette,
2852 unsigned int first_entry_index, unsigned int entry_count, DWRITE_COLOR_F *entries)
2854 unsigned int num_palettes, num_palette_entries, i;
2855 const struct cpal_color_record *records;
2856 const struct cpal_header_0 *header;
2857 struct d3d_color
2859 float r;
2860 float g;
2861 float b;
2862 float a;
2863 } *colors = (void *)entries;
2865 header = table_read_ensure(cpal, 0, sizeof(*header));
2867 if (!cpal->exists || !header)
2868 return DWRITE_E_NOCOLOR;
2870 num_palettes = GET_BE_WORD(header->num_palettes);
2871 if (palette >= num_palettes)
2872 return DWRITE_E_NOCOLOR;
2874 header = table_read_ensure(cpal, 0, FIELD_OFFSET(struct cpal_header_0, color_record_indices[palette]));
2875 if (!header)
2876 return DWRITE_E_NOCOLOR;
2878 num_palette_entries = GET_BE_WORD(header->num_palette_entries);
2879 if (first_entry_index + entry_count > num_palette_entries)
2880 return E_INVALIDARG;
2882 records = table_read_ensure(cpal, GET_BE_DWORD(header->offset_first_color_record),
2883 sizeof(*records) * GET_BE_WORD(header->num_color_records));
2884 if (!records)
2885 return DWRITE_E_NOCOLOR;
2887 first_entry_index += GET_BE_WORD(header->color_record_indices[palette]);
2889 for (i = 0; i < entry_count; ++i)
2891 colors[i].r = records[first_entry_index + i].red / 255.0f;
2892 colors[i].g = records[first_entry_index + i].green / 255.0f;
2893 colors[i].b = records[first_entry_index + i].blue / 255.0f;
2894 colors[i].a = records[first_entry_index + i].alpha / 255.0f;
2897 return S_OK;
2900 static int __cdecl colr_compare_gid(const void *g, const void *r)
2902 const struct colr_baseglyph_record *record = r;
2903 UINT16 glyph = *(UINT16*)g, GID = GET_BE_WORD(record->glyph);
2904 int ret = 0;
2906 if (glyph > GID)
2907 ret = 1;
2908 else if (glyph < GID)
2909 ret = -1;
2911 return ret;
2914 HRESULT opentype_get_colr_glyph(const struct dwrite_fonttable *colr, UINT16 glyph, struct dwrite_colorglyph *ret)
2916 unsigned int num_baseglyph_records, offset_baseglyph_records;
2917 const struct colr_baseglyph_record *record;
2918 const struct colr_layer_record *layer;
2919 const struct colr_header *header;
2921 memset(ret, 0, sizeof(*ret));
2922 ret->glyph = glyph;
2923 ret->palette_index = 0xffff;
2925 header = table_read_ensure(colr, 0, sizeof(*header));
2926 if (!header)
2927 return S_FALSE;
2929 num_baseglyph_records = GET_BE_WORD(header->num_baseglyph_records);
2930 offset_baseglyph_records = GET_BE_DWORD(header->offset_baseglyph_records);
2931 if (!table_read_ensure(colr, offset_baseglyph_records, num_baseglyph_records * sizeof(*record)))
2933 return S_FALSE;
2936 record = bsearch(&glyph, colr->data + offset_baseglyph_records, num_baseglyph_records,
2937 sizeof(*record), colr_compare_gid);
2938 if (!record)
2939 return S_FALSE;
2941 ret->first_layer = GET_BE_WORD(record->first_layer_index);
2942 ret->num_layers = GET_BE_WORD(record->num_layers);
2944 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
2945 (ret->first_layer + ret->layer) * sizeof(*layer))))
2947 layer += ret->first_layer + ret->layer;
2948 ret->glyph = GET_BE_WORD(layer->glyph);
2949 ret->palette_index = GET_BE_WORD(layer->palette_index);
2952 return S_OK;
2955 void opentype_colr_next_glyph(const struct dwrite_fonttable *colr, struct dwrite_colorglyph *glyph)
2957 const struct colr_layer_record *layer;
2958 const struct colr_header *header;
2960 /* iterated all the way through */
2961 if (glyph->layer == glyph->num_layers)
2962 return;
2964 if (!(header = table_read_ensure(colr, 0, sizeof(*header))))
2965 return;
2967 glyph->layer++;
2969 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
2970 (glyph->first_layer + glyph->layer) * sizeof(*layer))))
2972 layer += glyph->first_layer + glyph->layer;
2973 glyph->glyph = GET_BE_WORD(layer->glyph);
2974 glyph->palette_index = GET_BE_WORD(layer->palette_index);
2978 static BOOL opentype_has_font_table(IDWriteFontFace5 *fontface, UINT32 tag)
2980 BOOL exists = FALSE;
2981 const void *data;
2982 void *context;
2983 UINT32 size;
2984 HRESULT hr;
2986 hr = IDWriteFontFace5_TryGetFontTable(fontface, tag, &data, &size, &context, &exists);
2987 if (FAILED(hr))
2988 return FALSE;
2990 if (exists)
2991 IDWriteFontFace5_ReleaseFontTable(fontface, context);
2993 return exists;
2996 static unsigned int opentype_get_sbix_formats(IDWriteFontFace5 *fontface)
2998 unsigned int num_strikes, num_glyphs, i, j, ret = 0;
2999 const struct sbix_header *sbix_header;
3000 struct dwrite_fonttable table;
3002 memset(&table, 0, sizeof(table));
3003 table.exists = TRUE;
3005 if (!get_fontface_table(fontface, MS_MAXP_TAG, &table))
3006 return 0;
3008 num_glyphs = table_read_be_word(&table, FIELD_OFFSET(struct maxp, num_glyphs));
3010 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
3012 memset(&table, 0, sizeof(table));
3013 table.exists = TRUE;
3015 if (!get_fontface_table(fontface, MS_SBIX_TAG, &table))
3016 return 0;
3018 num_strikes = table_read_be_dword(&table, FIELD_OFFSET(struct sbix_header, num_strikes));
3019 sbix_header = table_read_ensure(&table, 0, FIELD_OFFSET(struct sbix_header, strike_offset[num_strikes]));
3021 if (sbix_header)
3023 for (i = 0; i < num_strikes; ++i)
3025 unsigned int strike_offset = GET_BE_DWORD(sbix_header->strike_offset[i]);
3026 const struct sbix_strike *strike = table_read_ensure(&table, strike_offset,
3027 FIELD_OFFSET(struct sbix_strike, glyphdata_offsets[num_glyphs + 1]));
3029 if (!strike)
3030 continue;
3032 for (j = 0; j < num_glyphs; j++)
3034 unsigned int offset = GET_BE_DWORD(strike->glyphdata_offsets[j]);
3035 unsigned int next_offset = GET_BE_DWORD(strike->glyphdata_offsets[j + 1]);
3036 const struct sbix_glyph_data *glyph_data;
3038 if (offset == next_offset)
3039 continue;
3041 glyph_data = table_read_ensure(&table, strike_offset + offset, sizeof(*glyph_data));
3042 if (!glyph_data)
3043 continue;
3045 switch (glyph_data->graphic_type)
3047 case MS_PNG__TAG:
3048 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3049 break;
3050 case MS_JPG__TAG:
3051 ret |= DWRITE_GLYPH_IMAGE_FORMATS_JPEG;
3052 break;
3053 case MS_TIFF_TAG:
3054 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TIFF;
3055 break;
3056 default:
3057 FIXME("unexpected bitmap format %s\n", debugstr_tag(GET_BE_DWORD(glyph_data->graphic_type)));
3063 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
3065 return ret;
3068 static unsigned int opentype_get_cblc_formats(IDWriteFontFace5 *fontface)
3070 const unsigned int format_mask = DWRITE_GLYPH_IMAGE_FORMATS_PNG |
3071 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3072 const struct cblc_bitmapsize_table *sizes;
3073 struct dwrite_fonttable cblc = { 0 };
3074 unsigned int num_sizes, i, ret = 0;
3075 const struct cblc_header *header;
3077 cblc.exists = TRUE;
3078 if (!get_fontface_table(fontface, MS_CBLC_TAG, &cblc))
3079 return 0;
3081 num_sizes = table_read_be_dword(&cblc, FIELD_OFFSET(struct cblc_header, num_sizes));
3082 sizes = table_read_ensure(&cblc, sizeof(*header), num_sizes * sizeof(*sizes));
3084 if (sizes)
3086 for (i = 0; i < num_sizes; ++i)
3088 BYTE bpp = sizes[i].bit_depth;
3090 if ((ret & format_mask) == format_mask)
3091 break;
3093 if (bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8)
3094 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3095 else if (bpp == 32)
3096 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3100 IDWriteFontFace5_ReleaseFontTable(fontface, cblc.context);
3102 return ret;
3105 UINT32 opentype_get_glyph_image_formats(IDWriteFontFace5 *fontface)
3107 UINT32 ret = DWRITE_GLYPH_IMAGE_FORMATS_NONE;
3109 if (opentype_has_font_table(fontface, MS_GLYF_TAG))
3110 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
3112 if (opentype_has_font_table(fontface, MS_CFF__TAG) ||
3113 opentype_has_font_table(fontface, MS_CFF2_TAG))
3114 ret |= DWRITE_GLYPH_IMAGE_FORMATS_CFF;
3116 if (opentype_has_font_table(fontface, MS_COLR_TAG))
3117 ret |= DWRITE_GLYPH_IMAGE_FORMATS_COLR;
3119 if (opentype_has_font_table(fontface, MS_SVG__TAG))
3120 ret |= DWRITE_GLYPH_IMAGE_FORMATS_SVG;
3122 if (opentype_has_font_table(fontface, MS_SBIX_TAG))
3123 ret |= opentype_get_sbix_formats(fontface);
3125 if (opentype_has_font_table(fontface, MS_CBLC_TAG))
3126 ret |= opentype_get_cblc_formats(fontface);
3128 return ret;
3131 DWRITE_CONTAINER_TYPE opentype_analyze_container_type(void const *data, UINT32 data_size)
3133 DWORD signature;
3135 if (data_size < sizeof(DWORD))
3136 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3138 /* Both WOFF and WOFF2 start with 4 bytes signature. */
3139 signature = *(DWORD *)data;
3141 switch (signature)
3143 case MS_WOFF_TAG:
3144 return DWRITE_CONTAINER_TYPE_WOFF;
3145 case MS_WOF2_TAG:
3146 return DWRITE_CONTAINER_TYPE_WOFF2;
3147 default:
3148 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3152 void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache)
3154 cache->font->grab_font_table(cache->context, MS_GSUB_TAG, &cache->gsub.table.data, &cache->gsub.table.size,
3155 &cache->gsub.table.context);
3157 if (cache->gsub.table.data)
3159 cache->gsub.script_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, script_list));
3160 cache->gsub.feature_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3161 cache->gsub.lookup_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3164 cache->font->grab_font_table(cache->context, MS_GPOS_TAG, &cache->gpos.table.data, &cache->gpos.table.size,
3165 &cache->gpos.table.context);
3167 if (cache->gpos.table.data)
3169 cache->gpos.script_list = table_read_be_word(&cache->gpos.table,
3170 FIELD_OFFSET(struct gpos_gsub_header, script_list));
3171 cache->gpos.feature_list = table_read_be_word(&cache->gpos.table,
3172 FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3173 cache->gpos.lookup_list = table_read_be_word(&cache->gpos.table,
3174 FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3177 cache->font->grab_font_table(cache->context, MS_GDEF_TAG, &cache->gdef.table.data, &cache->gdef.table.size,
3178 &cache->gdef.table.context);
3180 if (cache->gdef.table.data)
3182 unsigned int version = table_read_be_dword(&cache->gdef.table, 0);
3184 cache->gdef.classdef = table_read_be_word(&cache->gdef.table, FIELD_OFFSET(struct gdef_header, classdef));
3185 cache->gdef.markattachclassdef = table_read_be_word(&cache->gdef.table,
3186 FIELD_OFFSET(struct gdef_header, markattach_classdef));
3187 if (version >= 0x00010002)
3188 cache->gdef.markglyphsetdef = table_read_be_word(&cache->gdef.table,
3189 FIELD_OFFSET(struct gdef_header, markglyphsetdef));
3193 unsigned int opentype_layout_find_script(const struct scriptshaping_cache *cache, unsigned int kind, DWORD script,
3194 unsigned int *script_index)
3196 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3197 UINT16 script_count;
3198 unsigned int i;
3200 *script_index = ~0u;
3202 script_count = table_read_be_word(&table->table, table->script_list);
3203 if (!script_count)
3204 return 0;
3206 for (i = 0; i < script_count; i++)
3208 unsigned int tag = table_read_dword(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3209 i * sizeof(struct ot_script_record));
3210 if (!tag)
3211 continue;
3213 if (tag == script)
3215 *script_index = i;
3216 return script;
3220 return 0;
3223 unsigned int opentype_layout_find_language(const struct scriptshaping_cache *cache, unsigned int kind, DWORD language,
3224 unsigned int script_index, unsigned int *language_index)
3226 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3227 UINT16 table_offset, lang_count;
3228 unsigned int i;
3230 *language_index = ~0u;
3232 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3233 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
3234 if (!table_offset)
3235 return 0;
3237 lang_count = table_read_be_word(&table->table, table->script_list + table_offset +
3238 FIELD_OFFSET(struct ot_script, langsys_count));
3239 for (i = 0; i < lang_count; i++)
3241 unsigned int tag = table_read_dword(&table->table, table->script_list + table_offset +
3242 FIELD_OFFSET(struct ot_script, langsys) + i * sizeof(struct ot_langsys_record));
3244 if (tag == language)
3246 *language_index = i;
3247 return language;
3251 /* Try 'defaultLangSys' if it's set. */
3252 if (table_read_be_word(&table->table, table->script_list + table_offset))
3253 return ~0u;
3255 return 0;
3258 static int __cdecl gdef_class_compare_format2(const void *g, const void *r)
3260 const struct ot_gdef_class_range *range = r;
3261 UINT16 glyph = *(UINT16 *)g;
3263 if (glyph < GET_BE_WORD(range->start_glyph))
3264 return -1;
3265 else if (glyph > GET_BE_WORD(range->end_glyph))
3266 return 1;
3267 else
3268 return 0;
3271 static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable *table,
3272 unsigned int offset, UINT16 glyph)
3274 WORD format = table_read_be_word(table, offset), count;
3275 unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED;
3277 if (format == 1)
3279 const struct ot_gdef_classdef_format1 *format1;
3281 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
3282 format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
3283 if (format1)
3285 WORD start_glyph = GET_BE_WORD(format1->start_glyph);
3286 if (glyph >= start_glyph && (glyph - start_glyph) < count)
3288 glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
3289 if (glyph_class > GDEF_CLASS_MAX)
3290 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3294 else if (format == 2)
3296 const struct ot_gdef_classdef_format2 *format2;
3298 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
3299 format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
3300 if (format2)
3302 const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
3303 sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
3304 glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
3305 GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
3306 if (glyph_class > GDEF_CLASS_MAX)
3307 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3310 else
3311 WARN("Unknown GDEF format %u.\n", format);
3313 return glyph_class;
3316 static unsigned int opentype_set_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3318 struct scriptshaping_cache *cache = context->cache;
3319 unsigned int glyph_class = 0, props;
3321 if (cache->gdef.classdef)
3323 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.classdef,
3324 context->u.buffer.glyphs[idx]);
3327 switch (glyph_class)
3329 case GDEF_CLASS_BASE:
3330 props = GLYPH_PROP_BASE;
3331 break;
3332 case GDEF_CLASS_LIGATURE:
3333 props = GLYPH_PROP_LIGATURE;
3334 break;
3335 case GDEF_CLASS_MARK:
3336 props = GLYPH_PROP_MARK;
3337 if (cache->gdef.markattachclassdef)
3339 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.markattachclassdef,
3340 context->u.buffer.glyphs[idx]);
3341 props |= glyph_class << 8;
3343 break;
3344 default:
3345 props = 0;
3348 context->glyph_infos[idx].props = props;
3350 return props;
3353 static void opentype_set_subst_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3355 unsigned int glyph_props = opentype_set_glyph_props(context, idx) & LOOKUP_FLAG_IGNORE_MASK;
3356 context->u.subst.glyph_props[idx].isDiacritic = !!(glyph_props == GLYPH_PROP_MARK);
3357 context->u.subst.glyph_props[idx].isZeroWidthSpace = !!(glyph_props == GLYPH_PROP_MARK);
3360 struct coverage_compare_format1_context
3362 UINT16 glyph;
3363 const UINT16 *table_base;
3364 unsigned int *coverage_index;
3367 static int __cdecl coverage_compare_format1(const void *left, const void *right)
3369 const struct coverage_compare_format1_context *context = left;
3370 UINT16 glyph = GET_BE_WORD(*(UINT16 *)right);
3371 int ret;
3373 ret = context->glyph - glyph;
3374 if (!ret)
3375 *context->coverage_index = (UINT16 *)right - context->table_base;
3377 return ret;
3380 static int __cdecl coverage_compare_format2(const void *g, const void *r)
3382 const struct ot_coverage_range *range = r;
3383 UINT16 glyph = *(UINT16 *)g;
3385 if (glyph < GET_BE_WORD(range->start_glyph))
3386 return -1;
3387 else if (glyph > GET_BE_WORD(range->end_glyph))
3388 return 1;
3389 else
3390 return 0;
3393 static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable *table, unsigned int coverage,
3394 UINT16 glyph)
3396 WORD format = table_read_be_word(table, coverage), count;
3398 count = table_read_be_word(table, coverage + 2);
3400 if (format == 1)
3402 const struct ot_coverage_format1 *format1 = table_read_ensure(table, coverage,
3403 FIELD_OFFSET(struct ot_coverage_format1, glyphs[count]));
3404 struct coverage_compare_format1_context context;
3405 unsigned int coverage_index = GLYPH_NOT_COVERED;
3407 if (format1)
3409 context.glyph = glyph;
3410 context.table_base = format1->glyphs;
3411 context.coverage_index = &coverage_index;
3413 bsearch(&context, format1->glyphs, count, sizeof(glyph), coverage_compare_format1);
3416 return coverage_index;
3418 else if (format == 2)
3420 const struct ot_coverage_format2 *format2 = table_read_ensure(table, coverage,
3421 FIELD_OFFSET(struct ot_coverage_format2, ranges[count]));
3422 if (format2)
3424 const struct ot_coverage_range *range = bsearch(&glyph, format2->ranges, count,
3425 sizeof(struct ot_coverage_range), coverage_compare_format2);
3426 return range && glyph <= GET_BE_WORD(range->end_glyph) ?
3427 GET_BE_WORD(range->startcoverage_index) + glyph - GET_BE_WORD(range->start_glyph) :
3428 GLYPH_NOT_COVERED;
3431 else
3432 WARN("Unknown coverage format %u.\n", format);
3434 return -1;
3437 static inline unsigned int dwrite_popcount(unsigned int x)
3439 #if defined(__GNUC__) && (__GNUC__ >= 4)
3440 return __builtin_popcount(x);
3441 #else
3442 x -= x >> 1 & 0x55555555;
3443 x = (x & 0x33333333) + (x >> 2 & 0x33333333);
3444 return ((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
3445 #endif
3448 static float opentype_scale_gpos_be_value(WORD value, float emsize, UINT16 upem)
3450 return (short)GET_BE_WORD(value) * emsize / upem;
3453 static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context *context, unsigned int offset)
3455 const struct dwrite_fonttable *table = &context->table->table;
3456 unsigned int start_size, end_size, format, value_word;
3457 unsigned int index, ppem, mask;
3458 int value;
3460 if (!offset)
3461 return 0;
3463 start_size = table_read_be_word(table, offset);
3464 end_size = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, end_size));
3466 ppem = context->emsize;
3467 if (ppem < start_size || ppem > end_size)
3468 return 0;
3470 format = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, format));
3472 if (format < 1 || format > 3)
3473 return 0;
3475 index = ppem - start_size;
3477 value_word = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, values[index >> (4 - format)]));
3478 mask = 0xffff >> (16 - (1 << format));
3480 value = (value_word >> ((index % (4 - format)) * (1 << format))) & mask;
3482 if ((unsigned int)value >= ((mask + 1) >> 1))
3483 value -= mask + 1;
3485 return value;
3488 static void opentype_layout_apply_gpos_value(struct scriptshaping_context *context, unsigned int table_offset,
3489 WORD value_format, const WORD *values, unsigned int glyph)
3491 const struct scriptshaping_cache *cache = context->cache;
3492 DWRITE_GLYPH_OFFSET *offset = &context->offsets[glyph];
3493 float *advance = &context->advances[glyph];
3495 if (!value_format)
3496 return;
3498 if (value_format & GPOS_VALUE_X_PLACEMENT)
3500 offset->advanceOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3501 values++;
3503 if (value_format & GPOS_VALUE_Y_PLACEMENT)
3505 offset->ascenderOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3506 values++;
3508 if (value_format & GPOS_VALUE_X_ADVANCE)
3510 *advance += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3511 values++;
3513 if (value_format & GPOS_VALUE_Y_ADVANCE)
3515 values++;
3517 if (value_format & GPOS_VALUE_X_PLACEMENT_DEVICE)
3519 offset->advanceOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3520 values++;
3522 if (value_format & GPOS_VALUE_Y_PLACEMENT_DEVICE)
3524 offset->ascenderOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3525 values++;
3527 if (value_format & GPOS_VALUE_X_ADVANCE_DEVICE)
3529 *advance += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3530 values++;
3532 if (value_format & GPOS_VALUE_Y_ADVANCE_DEVICE)
3534 values++;
3538 struct lookup
3540 unsigned short index;
3541 unsigned short type;
3542 unsigned short flags;
3543 unsigned short subtable_count;
3545 unsigned int mask;
3546 unsigned int offset;
3547 unsigned int auto_zwnj : 1;
3548 unsigned int auto_zwj : 1;
3551 static unsigned int opentype_layout_is_subst_context(const struct scriptshaping_context *context)
3553 return context->table == &context->cache->gsub;
3556 static unsigned int opentype_layout_is_pos_context(const struct scriptshaping_context *context)
3558 return context->table == &context->cache->gpos;
3561 static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context *context,
3562 const struct lookup *lookup, unsigned int subtable, unsigned int *lookup_type)
3564 unsigned int subtable_offset = table_read_be_word(&context->table->table, lookup->offset +
3565 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
3566 const struct ot_gsubgpos_extension_format1 *format1;
3568 subtable_offset += lookup->offset;
3570 if ((opentype_layout_is_subst_context(context) && lookup->type != GSUB_LOOKUP_EXTENSION_SUBST) ||
3571 (opentype_layout_is_pos_context(context) && lookup->type != GPOS_LOOKUP_EXTENSION_POSITION))
3573 *lookup_type = lookup->type;
3574 return subtable_offset;
3577 *lookup_type = 0;
3579 if (!(format1 = table_read_ensure(&context->table->table, subtable_offset, sizeof(*format1))))
3580 return 0;
3582 if (GET_BE_WORD(format1->format) != 1)
3584 WARN("Unexpected extension table format %#x.\n", format1->format);
3585 return 0;
3588 *lookup_type = GET_BE_WORD(format1->lookup_type);
3589 return subtable_offset + GET_BE_DWORD(format1->extension_offset);
3592 struct ot_lookup
3594 unsigned int offset;
3595 unsigned int subtable_count;
3596 unsigned int flags;
3599 enum iterator_match
3601 /* First two to fit matching callback result. */
3602 ITER_NO = 0,
3603 ITER_YES = 1,
3604 ITER_MAYBE,
3607 struct match_context;
3608 struct match_data
3610 const struct match_context *mc;
3611 unsigned int subtable_offset;
3614 typedef BOOL (*p_match_func)(UINT16 glyph, UINT16 glyph_data, const struct match_data *match_data);
3616 struct match_context
3618 struct scriptshaping_context *context;
3619 unsigned int backtrack_offset;
3620 unsigned int input_offset;
3621 unsigned int lookahead_offset;
3622 p_match_func match_func;
3623 const struct lookup *lookup;
3626 struct glyph_iterator
3628 struct scriptshaping_context *context;
3629 unsigned int flags;
3630 unsigned int pos;
3631 unsigned int len;
3632 unsigned int mask;
3633 p_match_func match_func;
3634 const UINT16 *glyph_data;
3635 const struct match_data *match_data;
3636 unsigned int ignore_zwnj;
3637 unsigned int ignore_zwj;
3640 static void glyph_iterator_init(struct scriptshaping_context *context, unsigned int flags, unsigned int pos,
3641 unsigned int len, struct glyph_iterator *iter)
3643 iter->context = context;
3644 iter->flags = flags;
3645 iter->pos = pos;
3646 iter->len = len;
3647 iter->mask = ~0u;
3648 iter->match_func = NULL;
3649 iter->match_data = NULL;
3650 iter->glyph_data = NULL;
3651 /* Context matching iterators will get these fixed up. */
3652 iter->ignore_zwnj = !!opentype_layout_is_pos_context(context);
3653 iter->ignore_zwj = context->auto_zwj;
3656 struct ot_gdef_mark_glyph_sets
3658 UINT16 format;
3659 UINT16 count;
3660 DWORD offsets[1];
3663 static BOOL opentype_match_glyph_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3665 return glyph == glyph_data;
3668 static BOOL opentype_match_class_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3670 const struct match_context *mc = data->mc;
3671 UINT16 glyph_class = opentype_layout_get_glyph_class(&mc->context->table->table, data->subtable_offset, glyph);
3672 return glyph_class == glyph_data;
3675 static BOOL opentype_match_coverage_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3677 const struct match_context *mc = data->mc;
3678 return opentype_layout_is_glyph_covered(&mc->context->table->table, data->subtable_offset + glyph_data, glyph)
3679 != GLYPH_NOT_COVERED;
3682 static BOOL opentype_layout_mark_set_covers(const struct scriptshaping_cache *cache, unsigned int set_index,
3683 UINT16 glyph)
3685 unsigned int format, offset = cache->gdef.markglyphsetdef, coverage_offset, set_count;
3687 if (!offset)
3688 return FALSE;
3690 format = table_read_be_word(&cache->gdef.table, offset);
3692 if (format == 1)
3694 set_count = table_read_be_word(&cache->gdef.table, offset + 2);
3695 if (!set_count || set_index >= set_count)
3696 return FALSE;
3698 coverage_offset = table_read_be_dword(&cache->gdef.table, offset + 2 + set_index * sizeof(coverage_offset));
3699 return opentype_layout_is_glyph_covered(&cache->gdef.table, offset + coverage_offset, glyph) != GLYPH_NOT_COVERED;
3701 else
3702 WARN("Unexpected MarkGlyphSets format %#x.\n", format);
3704 return FALSE;
3707 static BOOL lookup_is_glyph_match(const struct scriptshaping_context *context, unsigned int idx, unsigned int match_props)
3709 unsigned int glyph_props = context->glyph_infos[idx].props;
3710 UINT16 glyph = context->u.buffer.glyphs[idx];
3712 if (glyph_props & match_props & LOOKUP_FLAG_IGNORE_MASK)
3713 return FALSE;
3715 if (!(glyph_props & GLYPH_PROP_MARK))
3716 return TRUE;
3718 if (match_props & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
3719 return opentype_layout_mark_set_covers(context->cache, match_props >> 16, glyph);
3721 if (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE)
3722 return (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE) == (glyph_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE);
3724 return TRUE;
3727 static enum iterator_match glyph_iterator_may_skip(const struct glyph_iterator *iter)
3729 unsigned int glyph_props = iter->context->glyph_infos[iter->pos].props & (GLYPH_PROP_IGNORABLE | GLYPH_PROP_HIDDEN);
3731 if (!lookup_is_glyph_match(iter->context, iter->pos, iter->flags))
3732 return ITER_YES;
3734 if (glyph_props == GLYPH_PROP_IGNORABLE && !iter->context->u.buffer.glyph_props[iter->pos].components &&
3735 (iter->ignore_zwnj || !(iter->context->glyph_infos[iter->pos].props & GLYPH_PROP_ZWNJ)) &&
3736 (iter->ignore_zwj || !(iter->context->glyph_infos[iter->pos].props & GLYPH_PROP_ZWJ)))
3738 return ITER_MAYBE;
3741 return ITER_NO;
3744 static enum iterator_match glyph_iterator_may_match(const struct glyph_iterator *iter)
3746 if (!(iter->mask & iter->context->glyph_infos[iter->pos].mask))
3747 return ITER_NO;
3749 /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that
3750 in all matching functions. */
3751 if (iter->match_func)
3752 return !!iter->match_func(iter->context->u.buffer.glyphs[iter->pos], GET_BE_WORD(*iter->glyph_data), iter->match_data);
3754 return ITER_MAYBE;
3757 static BOOL glyph_iterator_next(struct glyph_iterator *iter)
3759 enum iterator_match skip, match;
3761 while (iter->pos + iter->len < iter->context->glyph_count)
3763 ++iter->pos;
3765 skip = glyph_iterator_may_skip(iter);
3766 if (skip == ITER_YES)
3767 continue;
3769 match = glyph_iterator_may_match(iter);
3770 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3772 --iter->len;
3773 if (iter->glyph_data)
3774 ++iter->glyph_data;
3775 return TRUE;
3778 if (skip == ITER_NO)
3779 return FALSE;
3782 return FALSE;
3785 static BOOL glyph_iterator_prev(struct glyph_iterator *iter)
3787 enum iterator_match skip, match;
3789 while (iter->pos > iter->len - 1)
3791 --iter->pos;
3793 skip = glyph_iterator_may_skip(iter);
3794 if (skip == ITER_YES)
3795 continue;
3797 match = glyph_iterator_may_match(iter);
3798 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3800 --iter->len;
3801 if (iter->glyph_data)
3802 ++iter->glyph_data;
3803 return TRUE;
3806 if (skip == ITER_NO)
3807 return FALSE;
3810 return FALSE;
3813 static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context,
3814 const struct lookup *lookup, unsigned int subtable_offset)
3816 const struct dwrite_fonttable *table = &context->table->table;
3817 UINT16 format, value_format, value_len, coverage, glyph;
3819 unsigned int coverage_index;
3821 format = table_read_be_word(table, subtable_offset);
3823 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage));
3824 value_format = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format));
3825 value_len = dwrite_popcount(value_format);
3827 glyph = context->u.pos.glyphs[context->cur];
3829 if (format == 1)
3831 const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(table, subtable_offset,
3832 FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len]));
3834 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3835 if (coverage_index == GLYPH_NOT_COVERED)
3836 return FALSE;
3838 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, context->cur);
3840 else if (format == 2)
3842 WORD value_count = table_read_be_word(table, subtable_offset +
3843 FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count));
3844 const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(table, subtable_offset,
3845 FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD));
3847 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3848 if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count)
3849 return FALSE;
3851 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, &format2->values[coverage_index * value_len],
3852 context->cur);
3854 else
3856 WARN("Unknown single adjustment format %u.\n", format);
3857 return FALSE;
3860 context->cur++;
3862 return TRUE;
3865 static int __cdecl gpos_pair_adjustment_compare_format1(const void *g, const void *r)
3867 const struct ot_gpos_pairvalue *pairvalue = r;
3868 UINT16 second_glyph = GET_BE_WORD(pairvalue->second_glyph);
3869 return *(UINT16 *)g - second_glyph;
3872 static BOOL opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context *context,
3873 const struct lookup *lookup, unsigned int subtable_offset)
3875 const struct dwrite_fonttable *table = &context->table->table;
3876 unsigned int first_glyph, second_glyph;
3877 struct glyph_iterator iter_pair;
3878 WORD format, coverage;
3880 WORD value_format1, value_format2, value_len1, value_len2;
3881 unsigned int coverage_index;
3883 glyph_iterator_init(context, lookup->flags, context->cur, 1, &iter_pair);
3884 if (!glyph_iterator_next(&iter_pair))
3885 return FALSE;
3887 if (context->is_rtl)
3889 first_glyph = iter_pair.pos;
3890 second_glyph = context->cur;
3892 else
3894 first_glyph = context->cur;
3895 second_glyph = iter_pair.pos;
3898 format = table_read_be_word(table, subtable_offset);
3900 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1, coverage));
3901 if (!coverage)
3902 return FALSE;
3904 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, context->u.pos.glyphs[first_glyph]);
3905 if (coverage_index == GLYPH_NOT_COVERED)
3906 return FALSE;
3908 if (format == 1)
3910 const struct ot_gpos_pairpos_format1 *format1;
3911 WORD pairset_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1,
3912 pairset_count));
3913 unsigned int pairvalue_len, pairset_offset;
3914 const struct ot_gpos_pairset *pairset;
3915 const WORD *pairvalue;
3916 WORD pairvalue_count;
3918 if (!pairset_count || coverage_index >= pairset_count)
3919 return FALSE;
3921 format1 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairsets[pairset_count]));
3922 if (!format1)
3923 return FALSE;
3925 /* Ordered paired values. */
3926 pairvalue_count = table_read_be_word(table, subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]));
3927 if (!pairvalue_count)
3928 return FALSE;
3930 /* Structure length is variable, but does not change across the subtable. */
3931 value_format1 = GET_BE_WORD(format1->value_format1) & 0xff;
3932 value_format2 = GET_BE_WORD(format1->value_format2) & 0xff;
3934 value_len1 = dwrite_popcount(value_format1);
3935 value_len2 = dwrite_popcount(value_format2);
3936 pairvalue_len = FIELD_OFFSET(struct ot_gpos_pairvalue, data) + value_len1 * sizeof(WORD) +
3937 value_len2 * sizeof(WORD);
3939 pairset_offset = subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]);
3940 pairset = table_read_ensure(table, subtable_offset + pairset_offset, pairvalue_len * pairvalue_count);
3941 if (!pairset)
3942 return FALSE;
3944 pairvalue = bsearch(&context->u.pos.glyphs[second_glyph], pairset->pairvalues, pairvalue_count,
3945 pairvalue_len, gpos_pair_adjustment_compare_format1);
3946 if (!pairvalue)
3947 return FALSE;
3949 pairvalue += 1; /* Skip SecondGlyph. */
3950 opentype_layout_apply_gpos_value(context, pairset_offset, value_format1, pairvalue, first_glyph);
3951 opentype_layout_apply_gpos_value(context, pairset_offset, value_format2, pairvalue + value_len1,
3952 second_glyph);
3954 context->cur = iter_pair.pos;
3955 if (value_len2)
3956 context->cur++;
3958 else if (format == 2)
3960 const struct ot_gpos_pairpos_format2 *format2;
3961 WORD class1_count, class2_count;
3962 unsigned int class1, class2;
3963 const WCHAR *values;
3965 value_format1 = table_read_be_word(table, subtable_offset +
3966 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format1)) & 0xff;
3967 value_format2 = table_read_be_word(table, subtable_offset +
3968 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format2)) & 0xff;
3970 class1_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class1_count));
3971 class2_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class2_count));
3973 value_len1 = dwrite_popcount(value_format1);
3974 value_len2 = dwrite_popcount(value_format2);
3976 format2 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format2,
3977 values[class1_count * class2_count * (value_len1 + value_len2)]));
3978 if (!format2)
3979 return FALSE;
3981 class1 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def1),
3982 context->u.pos.glyphs[first_glyph]);
3983 class2 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def2),
3984 context->u.pos.glyphs[second_glyph]);
3986 if (!(class1 < class1_count && class2 < class2_count))
3987 return FALSE;
3989 values = &format2->values[(class1 * class2_count + class2) * (value_len1 + value_len2)];
3990 opentype_layout_apply_gpos_value(context, subtable_offset, value_format1, values, first_glyph);
3991 opentype_layout_apply_gpos_value(context, subtable_offset, value_format2, values + value_len1,
3992 second_glyph);
3994 context->cur = iter_pair.pos;
3995 if (value_len2)
3996 context->cur++;
3998 else
4000 WARN("Unknown pair adjustment format %u.\n", format);
4001 return FALSE;
4004 return TRUE;
4007 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context *context, unsigned int anchor_offset,
4008 unsigned int glyph_index, float *x, float *y)
4010 const struct scriptshaping_cache *cache = context->cache;
4011 const struct dwrite_fonttable *table = &context->table->table;
4013 WORD format = table_read_be_word(table, anchor_offset);
4015 *x = *y = 0.0f;
4017 if (format == 1)
4019 const struct ot_gpos_anchor_format1 *format1 = table_read_ensure(table, anchor_offset, sizeof(*format1));
4021 if (format1)
4023 *x = opentype_scale_gpos_be_value(format1->x_coord, context->emsize, cache->upem);
4024 *y = opentype_scale_gpos_be_value(format1->y_coord, context->emsize, cache->upem);
4027 else if (format == 2)
4029 const struct ot_gpos_anchor_format2 *format2 = table_read_ensure(table, anchor_offset, sizeof(*format2));
4031 if (format2)
4033 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
4034 FIXME("Use outline anchor point for glyph %u.\n", context->u.pos.glyphs[glyph_index]);
4036 *x = opentype_scale_gpos_be_value(format2->x_coord, context->emsize, cache->upem);
4037 *y = opentype_scale_gpos_be_value(format2->y_coord, context->emsize, cache->upem);
4040 else if (format == 3)
4042 const struct ot_gpos_anchor_format3 *format3 = table_read_ensure(table, anchor_offset, sizeof(*format3));
4044 if (format3)
4046 *x = opentype_scale_gpos_be_value(format3->x_coord, context->emsize, cache->upem);
4047 *y = opentype_scale_gpos_be_value(format3->y_coord, context->emsize, cache->upem);
4049 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
4051 if (format3->x_dev_offset)
4052 *x += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->x_dev_offset));
4053 if (format3->y_dev_offset)
4054 *y += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->y_dev_offset));
4058 else
4059 WARN("Unknown anchor format %u.\n", format);
4062 static void opentype_set_glyph_attach_type(struct scriptshaping_context *context, unsigned int idx,
4063 enum attach_type attach_type)
4065 context->glyph_infos[idx].props &= ~GLYPH_PROP_ATTACH_TYPE_MASK;
4066 context->glyph_infos[idx].props |= attach_type << 16;
4069 static enum attach_type opentype_get_glyph_attach_type(const struct scriptshaping_context *context, unsigned int idx)
4071 return (context->glyph_infos[idx].props >> 16) & 0xff;
4074 static void opentype_reverse_cursive_offset(struct scriptshaping_context *context, unsigned int i,
4075 unsigned int new_parent)
4077 enum attach_type type = opentype_get_glyph_attach_type(context, i);
4078 int chain = context->glyph_infos[i].attach_chain;
4079 unsigned int j;
4081 if (!chain || type != GLYPH_ATTACH_CURSIVE)
4082 return;
4084 context->glyph_infos[i].attach_chain = 0;
4086 j = (int)i + chain;
4087 if (j == new_parent)
4088 return;
4090 opentype_reverse_cursive_offset(context, j, new_parent);
4092 /* FIXME: handle vertical flow direction */
4093 context->offsets[j].ascenderOffset = -context->offsets[i].ascenderOffset;
4095 context->glyph_infos[j].attach_chain = -chain;
4096 opentype_set_glyph_attach_type(context, j, type);
4099 static BOOL opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context *context,
4100 const struct lookup *lookup, unsigned int subtable_offset)
4102 const struct dwrite_fonttable *table = &context->table->table;
4103 UINT16 format, glyph;
4105 format = table_read_be_word(table, subtable_offset);
4106 glyph = context->u.pos.glyphs[context->cur];
4108 if (format == 1)
4110 WORD coverage_offset = table_read_be_word(table, subtable_offset +
4111 FIELD_OFFSET(struct ot_gpos_cursive_format1, coverage));
4112 unsigned int glyph_index, entry_count, entry_anchor, exit_anchor, child, parent;
4113 float entry_x, entry_y, exit_x, exit_y, delta;
4114 struct glyph_iterator prev_iter;
4115 float y_offset;
4117 if (!coverage_offset)
4118 return FALSE;
4120 entry_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_cursive_format1, count));
4122 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset, glyph);
4123 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4124 return FALSE;
4126 entry_anchor = table_read_be_word(table, subtable_offset +
4127 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2]));
4128 if (!entry_anchor)
4129 return FALSE;
4131 glyph_iterator_init(context, lookup->flags, context->cur, 1, &prev_iter);
4132 if (!glyph_iterator_prev(&prev_iter))
4133 return FALSE;
4135 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset,
4136 context->u.pos.glyphs[prev_iter.pos]);
4137 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4138 return FALSE;
4140 exit_anchor = table_read_be_word(table, subtable_offset +
4141 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2 + 1]));
4142 if (!exit_anchor)
4143 return FALSE;
4145 opentype_layout_gpos_get_anchor(context, subtable_offset + exit_anchor, prev_iter.pos, &exit_x, &exit_y);
4146 opentype_layout_gpos_get_anchor(context, subtable_offset + entry_anchor, context->cur, &entry_x, &entry_y);
4148 if (context->is_rtl)
4150 delta = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4151 context->advances[prev_iter.pos] -= delta;
4152 context->advances[context->cur] = entry_x + context->offsets[context->cur].advanceOffset;
4153 context->offsets[prev_iter.pos].advanceOffset -= delta;
4155 else
4157 delta = entry_x + context->offsets[context->cur].advanceOffset;
4158 context->advances[prev_iter.pos] = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4159 context->advances[context->cur] -= delta;
4160 context->offsets[context->cur].advanceOffset -= delta;
4163 if (lookup->flags & LOOKUP_FLAG_RTL)
4165 y_offset = entry_y - exit_y;
4166 child = prev_iter.pos;
4167 parent = context->cur;
4169 else
4171 y_offset = exit_y - entry_y;
4172 child = context->cur;
4173 parent = prev_iter.pos;
4176 opentype_reverse_cursive_offset(context, child, parent);
4178 context->offsets[child].ascenderOffset = y_offset;
4180 opentype_set_glyph_attach_type(context, child, GLYPH_ATTACH_CURSIVE);
4181 context->glyph_infos[child].attach_chain = (int)parent - (int)child;
4182 context->has_gpos_attachment = 1;
4184 if (context->glyph_infos[parent].attach_chain == -context->glyph_infos[child].attach_chain)
4185 context->glyph_infos[parent].attach_chain = 0;
4187 context->cur++;
4189 else
4191 WARN("Unknown cursive attachment format %u.\n", format);
4192 return FALSE;
4195 return TRUE;
4198 static BOOL opentype_layout_apply_mark_array(struct scriptshaping_context *context, unsigned int subtable_offset,
4199 unsigned int mark_array, unsigned int mark_index, unsigned int glyph_index, unsigned int anchors_matrix,
4200 unsigned int class_count, unsigned int glyph_pos)
4202 const struct dwrite_fonttable *table = &context->table->table;
4203 unsigned int mark_class, mark_count, glyph_count;
4204 const struct ot_gpos_mark_record *record;
4205 float mark_x, mark_y, base_x, base_y;
4206 const UINT16 *anchors;
4208 mark_count = table_read_be_word(table, subtable_offset + mark_array);
4209 if (mark_index >= mark_count) return FALSE;
4211 if (!(record = table_read_ensure(table, subtable_offset + mark_array +
4212 FIELD_OFFSET(struct ot_gpos_mark_array, records[mark_index]), sizeof(*record))))
4214 return FALSE;
4217 mark_class = GET_BE_WORD(record->mark_class);
4218 if (mark_class >= class_count) return FALSE;
4220 glyph_count = table_read_be_word(table, subtable_offset + anchors_matrix);
4221 if (glyph_index >= glyph_count) return FALSE;
4223 /* Anchors data is stored as two dimensional array [glyph_count][class_count], starting with row count field. */
4224 anchors = table_read_ensure(table, subtable_offset + anchors_matrix + 2, glyph_count * class_count * sizeof(*anchors));
4225 if (!anchors) return FALSE;
4227 opentype_layout_gpos_get_anchor(context, subtable_offset + mark_array + GET_BE_WORD(record->mark_anchor),
4228 context->cur, &mark_x, &mark_y);
4229 opentype_layout_gpos_get_anchor(context, subtable_offset + anchors_matrix +
4230 GET_BE_WORD(anchors[glyph_index * class_count + mark_class]), glyph_pos, &base_x, &base_y);
4232 if (context->is_rtl)
4233 context->offsets[context->cur].advanceOffset = mark_x - base_x;
4234 else
4235 context->offsets[context->cur].advanceOffset = -context->advances[glyph_pos] + base_x - mark_x;
4236 context->offsets[context->cur].ascenderOffset = base_y - mark_y;
4237 opentype_set_glyph_attach_type(context, context->cur, GLYPH_ATTACH_MARK);
4238 context->glyph_infos[context->cur].attach_chain = (int)glyph_pos - (int)context->cur;
4239 context->has_gpos_attachment = 1;
4241 context->cur++;
4243 return TRUE;
4246 static BOOL opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context *context,
4247 const struct lookup *lookup, unsigned int subtable_offset)
4249 const struct dwrite_fonttable *table = &context->table->table;
4250 WORD format;
4252 format = table_read_be_word(table, subtable_offset);
4254 if (format == 1)
4256 const struct ot_gpos_mark_to_base_format1 *format1;
4257 unsigned int base_index, mark_index;
4258 struct glyph_iterator base_iter;
4260 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4262 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4263 context->u.pos.glyphs[context->cur]);
4264 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4266 /* Look back for first base glyph. */
4267 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &base_iter);
4268 if (!glyph_iterator_prev(&base_iter))
4269 return FALSE;
4271 base_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->base_coverage),
4272 context->u.pos.glyphs[base_iter.pos]);
4273 if (base_index == GLYPH_NOT_COVERED) return FALSE;
4275 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4276 base_index, GET_BE_WORD(format1->base_array), GET_BE_WORD(format1->mark_class_count), base_iter.pos);
4278 else
4280 WARN("Unknown mark-to-base format %u.\n", format);
4281 return FALSE;
4284 return TRUE;
4287 static const UINT16 * table_read_array_be_word(const struct dwrite_fonttable *table, unsigned int offset,
4288 unsigned int index, UINT16 *data)
4290 unsigned int count = table_read_be_word(table, offset);
4291 const UINT16 *array;
4293 if (index != ~0u && index >= count) return NULL;
4294 if (!(array = table_read_ensure(table, offset + 2, count * sizeof(*array)))) return FALSE;
4295 *data = index == ~0u ? count : GET_BE_WORD(array[index]);
4296 return array;
4299 static BOOL opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context *context,
4300 const struct lookup *lookup, unsigned int subtable_offset)
4302 const struct dwrite_fonttable *table = &context->table->table;
4303 WORD format;
4305 format = table_read_be_word(table, subtable_offset);
4307 if (format == 1)
4309 unsigned int mark_index, lig_index, comp_index, class_count, comp_count;
4310 const struct ot_gpos_mark_to_lig_format1 *format1;
4311 struct glyph_iterator lig_iter;
4312 unsigned int lig_array;
4313 UINT16 lig_attach;
4315 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4317 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4318 context->u.pos.glyphs[context->cur]);
4319 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4321 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &lig_iter);
4322 if (!glyph_iterator_prev(&lig_iter))
4323 return FALSE;
4325 lig_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->lig_coverage),
4326 context->u.pos.glyphs[lig_iter.pos]);
4327 if (lig_index == GLYPH_NOT_COVERED) return FALSE;
4329 class_count = GET_BE_WORD(format1->mark_class_count);
4331 lig_array = GET_BE_WORD(format1->lig_array);
4333 if (!table_read_array_be_word(table, subtable_offset + lig_array, lig_index, &lig_attach)) return FALSE;
4335 comp_count = table_read_be_word(table, subtable_offset + lig_array + lig_attach);
4336 if (!comp_count) return FALSE;
4338 comp_index = context->u.buffer.glyph_props[lig_iter.pos].components -
4339 context->u.buffer.glyph_props[context->cur].lig_component - 1;
4340 if (comp_index >= comp_count) return FALSE;
4342 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4343 comp_index, lig_array + lig_attach, class_count, lig_iter.pos);
4345 else
4346 WARN("Unknown mark-to-ligature format %u.\n", format);
4348 return FALSE;
4351 static BOOL opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context *context,
4352 const struct lookup *lookup, unsigned int subtable_offset)
4354 const struct dwrite_fonttable *table = &context->table->table;
4355 WORD format;
4357 format = table_read_be_word(table, subtable_offset);
4359 if (format == 1)
4361 const struct ot_gpos_mark_to_mark_format1 *format1;
4362 unsigned int mark1_index, mark2_index;
4363 struct glyph_iterator mark_iter;
4365 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4367 mark1_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark1_coverage),
4368 context->u.pos.glyphs[context->cur]);
4369 if (mark1_index == GLYPH_NOT_COVERED) return FALSE;
4371 glyph_iterator_init(context, lookup->flags & ~LOOKUP_FLAG_IGNORE_MASK, context->cur, 1, &mark_iter);
4372 if (!glyph_iterator_prev(&mark_iter))
4373 return FALSE;
4375 if (!context->u.pos.glyph_props[mark_iter.pos].isDiacritic)
4376 return FALSE;
4378 mark2_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark2_coverage),
4379 context->u.pos.glyphs[mark_iter.pos]);
4380 if (mark2_index == GLYPH_NOT_COVERED) return FALSE;
4382 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark1_array), mark1_index,
4383 mark2_index, GET_BE_WORD(format1->mark2_array), GET_BE_WORD(format1->mark_class_count), mark_iter.pos);
4385 else
4387 WARN("Unknown mark-to-mark format %u.\n", format);
4388 return FALSE;
4391 return TRUE;
4394 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
4395 unsigned int subtable_offset);
4396 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
4397 unsigned int subtable_offset);
4399 static BOOL opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
4401 unsigned int i, lookup_type;
4402 BOOL ret = FALSE;
4404 for (i = 0; i < lookup->subtable_count; ++i)
4406 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
4408 switch (lookup_type)
4410 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
4411 ret = opentype_layout_apply_gpos_single_adjustment(context, lookup, subtable_offset);
4412 break;
4413 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
4414 ret = opentype_layout_apply_gpos_pair_adjustment(context, lookup, subtable_offset);
4415 break;
4416 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
4417 ret = opentype_layout_apply_gpos_cursive_attachment(context, lookup, subtable_offset);
4418 break;
4419 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
4420 ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, lookup, subtable_offset);
4421 break;
4422 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
4423 ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, lookup, subtable_offset);
4424 break;
4425 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
4426 ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, lookup, subtable_offset);
4427 break;
4428 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
4429 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
4430 break;
4431 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
4432 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
4433 break;
4434 case GPOS_LOOKUP_EXTENSION_POSITION:
4435 WARN("Recursive extension lookup.\n");
4436 break;
4437 default:
4438 WARN("Unknown lookup type %u.\n", lookup_type);
4441 if (ret)
4442 break;
4445 return ret;
4448 struct lookups
4450 struct lookup *lookups;
4451 size_t capacity;
4452 size_t count;
4455 static int __cdecl lookups_sorting_compare(const void *a, const void *b)
4457 const struct lookup *left = (const struct lookup *)a;
4458 const struct lookup *right = (const struct lookup *)b;
4459 return left->index < right->index ? -1 : left->index > right->index ? 1 : 0;
4462 static BOOL opentype_layout_init_lookup(const struct ot_gsubgpos_table *table, unsigned short lookup_index,
4463 const struct shaping_feature *feature, struct lookup *lookup)
4465 unsigned short subtable_count, lookup_type, flags, mark_filtering_set;
4466 const struct ot_lookup_table *lookup_table;
4467 unsigned int offset;
4469 if (!(offset = table_read_be_word(&table->table, table->lookup_list +
4470 FIELD_OFFSET(struct ot_lookup_list, lookup[lookup_index]))))
4472 return FALSE;
4475 offset += table->lookup_list;
4477 if (!(lookup_table = table_read_ensure(&table->table, offset, sizeof(*lookup_table))))
4478 return FALSE;
4480 if (!(subtable_count = GET_BE_WORD(lookup_table->subtable_count)))
4481 return FALSE;
4483 lookup_type = GET_BE_WORD(lookup_table->lookup_type);
4484 flags = GET_BE_WORD(lookup_table->flags);
4486 if (flags & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
4488 mark_filtering_set = table_read_be_word(&table->table, offset +
4489 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable_count]));
4490 flags |= mark_filtering_set << 16;
4493 lookup->index = lookup_index;
4494 lookup->type = lookup_type;
4495 lookup->flags = flags;
4496 lookup->subtable_count = subtable_count;
4497 lookup->offset = offset;
4498 if (feature)
4500 lookup->mask = feature->mask;
4501 lookup->auto_zwnj = !(feature->flags & FEATURE_MANUAL_ZWNJ);
4502 lookup->auto_zwj = !(feature->flags & FEATURE_MANUAL_ZWJ);
4505 return TRUE;
4508 static void opentype_layout_add_lookups(const struct ot_feature_list *feature_list, UINT16 total_lookup_count,
4509 const struct ot_gsubgpos_table *table, struct shaping_feature *feature, struct lookups *lookups)
4511 UINT16 feature_offset, lookup_count;
4512 unsigned int i;
4514 /* Feature wasn't found */
4515 if (feature->index == 0xffff)
4516 return;
4518 feature_offset = GET_BE_WORD(feature_list->features[feature->index].offset);
4520 lookup_count = table_read_be_word(&table->table, table->feature_list + feature_offset +
4521 FIELD_OFFSET(struct ot_feature, lookup_count));
4522 if (!lookup_count)
4523 return;
4525 if (!dwrite_array_reserve((void **)&lookups->lookups, &lookups->capacity, lookups->count + lookup_count,
4526 sizeof(*lookups->lookups)))
4528 return;
4531 for (i = 0; i < lookup_count; ++i)
4533 UINT16 lookup_index = table_read_be_word(&table->table, table->feature_list + feature_offset +
4534 FIELD_OFFSET(struct ot_feature, lookuplist_index[i]));
4536 if (lookup_index >= total_lookup_count)
4537 continue;
4539 if (opentype_layout_init_lookup(table, lookup_index, feature, &lookups->lookups[lookups->count]))
4540 lookups->count++;
4544 static void opentype_layout_collect_lookups(struct scriptshaping_context *context, unsigned int script_index,
4545 unsigned int language_index, struct shaping_features *features, const struct ot_gsubgpos_table *table,
4546 struct lookups *lookups)
4548 unsigned int last_num_lookups = 0, stage, script_feature_count = 0;
4549 UINT16 total_feature_count, total_lookup_count;
4550 struct shaping_feature required_feature = { 0 };
4551 const struct ot_feature_list *feature_list;
4552 const struct ot_langsys *langsys = NULL;
4553 struct shaping_feature *feature;
4554 unsigned int i, j, next_bit;
4555 unsigned int global_bit_shift = 1;
4556 unsigned int global_bit_mask = 2;
4557 UINT16 feature_index;
4559 if (!table->table.data)
4560 return;
4562 if (script_index != ~0u)
4564 unsigned int table_offset, langsys_offset;
4566 /* ScriptTable offset. */
4567 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
4568 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
4569 if (!table_offset)
4570 return;
4572 if (language_index == ~0u)
4573 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
4574 else
4575 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
4576 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
4577 FIELD_OFFSET(struct ot_langsys_record, langsys));
4578 langsys_offset += table->script_list + table_offset;
4580 script_feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
4581 if (script_feature_count)
4582 langsys = table_read_ensure(&table->table, langsys_offset,
4583 FIELD_OFFSET(struct ot_langsys, feature_index[script_feature_count]));
4584 if (!langsys)
4585 script_feature_count = 0;
4588 total_feature_count = table_read_be_word(&table->table, table->feature_list);
4589 if (!total_feature_count)
4590 return;
4592 total_lookup_count = table_read_be_word(&table->table, table->lookup_list);
4593 if (!total_lookup_count)
4594 return;
4596 feature_list = table_read_ensure(&table->table, table->feature_list,
4597 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
4598 if (!feature_list)
4599 return;
4601 /* Required feature. */
4602 required_feature.index = langsys ? GET_BE_WORD(langsys->required_feature_index) : 0xffff;
4603 if (required_feature.index < total_feature_count)
4604 required_feature.tag = feature_list->features[required_feature.index].tag;
4605 required_feature.mask = global_bit_mask;
4607 context->global_mask = global_bit_mask;
4608 next_bit = global_bit_shift + 1;
4609 for (i = 0; i < features->count; ++i)
4611 unsigned int bits_needed;
4612 BOOL found = FALSE;
4614 feature = &features->features[i];
4616 feature->index = 0xffff;
4618 if ((feature->flags & FEATURE_GLOBAL) && feature->max_value == 1)
4619 bits_needed = 0;
4620 else
4622 BitScanReverse(&bits_needed, min(feature->max_value, 256));
4623 bits_needed++;
4626 if (!feature->max_value || next_bit + bits_needed > 8 * sizeof (feature->mask))
4627 continue;
4629 if (required_feature.tag == feature->tag)
4630 required_feature.stage = feature->stage;
4632 for (j = 0; j < script_feature_count; ++j)
4634 feature_index = GET_BE_WORD(langsys->feature_index[j]);
4635 if (feature_index >= total_feature_count)
4636 continue;
4637 if ((found = feature_list->features[feature_index].tag == feature->tag))
4639 feature->index = feature_index;
4640 break;
4644 if (!found && (features->features[i].flags & FEATURE_GLOBAL_SEARCH))
4646 for (j = 0; j < total_feature_count; ++j)
4648 if ((found = (feature_list->features[j].tag == feature->tag)))
4650 feature->index = j;
4651 break;
4656 if (!found && !(features->features[i].flags & FEATURE_HAS_FALLBACK))
4657 continue;
4659 if (feature->flags & FEATURE_GLOBAL && feature->max_value == 1)
4661 feature->shift = global_bit_shift;
4662 feature->mask = global_bit_mask;
4664 else
4666 feature->shift = next_bit;
4667 feature->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
4668 next_bit += bits_needed;
4669 context->global_mask |= (feature->default_value << feature->shift) & feature->mask;
4671 if (!found)
4672 feature->flags |= FEATURE_NEEDS_FALLBACK;
4675 for (stage = 0; stage <= features->stage; ++stage)
4677 if (required_feature.index != 0xffff && required_feature.stage == stage)
4678 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &required_feature, lookups);
4680 for (i = 0; i < features->count; ++i)
4682 if (features->features[i].stage == stage)
4683 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &features->features[i], lookups);
4686 /* Sort and merge lookups for current stage. */
4687 if (last_num_lookups < lookups->count)
4689 qsort(lookups->lookups + last_num_lookups, lookups->count - last_num_lookups, sizeof(*lookups->lookups),
4690 lookups_sorting_compare);
4692 j = last_num_lookups;
4693 for (i = j + 1; i < lookups->count; ++i)
4695 if (lookups->lookups[i].index != lookups->lookups[j].index)
4697 lookups->lookups[++j] = lookups->lookups[i];
4699 else
4701 lookups->lookups[j].mask |= lookups->lookups[i].mask;
4702 lookups->lookups[j].auto_zwnj &= lookups->lookups[i].auto_zwnj;
4703 lookups->lookups[j].auto_zwj &= lookups->lookups[i].auto_zwj;
4706 lookups->count = j + 1;
4709 last_num_lookups = lookups->count;
4710 features->stages[stage].last_lookup = last_num_lookups;
4714 static int __cdecl feature_search_compare(const void *a, const void* b)
4716 unsigned int tag = *(unsigned int *)a;
4717 const struct shaping_feature *feature = b;
4719 return tag < feature->tag ? -1 : tag > feature->tag ? 1 : 0;
4722 static unsigned int shaping_features_get_mask(const struct shaping_features *features, unsigned int tag, unsigned int *shift)
4724 struct shaping_feature *feature;
4726 feature = bsearch(&tag, features->features, features->count, sizeof(*features->features), feature_search_compare);
4728 if (!feature || feature->index == 0xffff)
4729 return 0;
4731 if (shift) *shift = feature->shift;
4732 return feature->mask;
4735 unsigned int shape_get_feature_1_mask(const struct shaping_features *features, unsigned int tag)
4737 unsigned int shift, mask = shaping_features_get_mask(features, tag, &shift);
4738 return (1 << shift) & mask;
4741 static void opentype_layout_get_glyph_range_for_text(struct scriptshaping_context *context, unsigned int start_char,
4742 unsigned int end_char, unsigned int *start_glyph, unsigned int *end_glyph)
4744 *start_glyph = context->u.buffer.clustermap[start_char];
4745 if (end_char >= context->length - 1)
4746 *end_glyph = context->glyph_count - 1;
4747 else
4748 *end_glyph = context->u.buffer.clustermap[end_char] - 1;
4751 static void opentype_layout_set_glyph_masks(struct scriptshaping_context *context, const struct shaping_features *features)
4753 const DWRITE_TYPOGRAPHIC_FEATURES **user_features = context->user_features.features;
4754 unsigned int f, r, g, start_char, mask, shift, value;
4756 for (g = 0; g < context->glyph_count; ++g)
4757 context->glyph_infos[g].mask = context->global_mask;
4759 if (opentype_layout_is_subst_context(context) && context->shaper->setup_masks)
4760 context->shaper->setup_masks(context, features);
4762 for (r = 0, start_char = 0; r < context->user_features.range_count; ++r)
4764 unsigned int start_glyph, end_glyph;
4766 if (start_char >= context->length)
4767 break;
4769 if (!context->user_features.range_lengths[r])
4770 continue;
4772 opentype_layout_get_glyph_range_for_text(context, start_char, start_char + context->user_features.range_lengths[r],
4773 &start_glyph, &end_glyph);
4774 start_char += context->user_features.range_lengths[r];
4776 if (start_glyph > end_glyph || end_glyph >= context->glyph_count)
4777 continue;
4779 for (f = 0; f < user_features[r]->featureCount; ++f)
4781 mask = shaping_features_get_mask(features, user_features[r]->features[f].nameTag, &shift);
4782 if (!mask)
4783 continue;
4785 value = (user_features[r]->features[f].parameter << shift) & mask;
4787 for (g = start_glyph; g <= end_glyph; ++g)
4788 context->glyph_infos[g].mask = (context->glyph_infos[g].mask & ~mask) | value;
4793 static void opentype_layout_apply_gpos_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
4795 struct lookup lookup = { 0 };
4796 if (opentype_layout_init_lookup(context->table, lookup_index, NULL, &lookup))
4797 opentype_layout_apply_gpos_lookup(context, &lookup);
4800 static void opentype_propagate_attachment_offsets(struct scriptshaping_context *context, unsigned int i)
4802 enum attach_type type = opentype_get_glyph_attach_type(context, i);
4803 int chain = context->glyph_infos[i].attach_chain;
4804 unsigned int j, k;
4806 if (!chain)
4807 return;
4809 context->glyph_infos[i].attach_chain = 0;
4811 j = (int)i + chain;
4812 if (j >= context->glyph_count)
4813 return;
4815 opentype_propagate_attachment_offsets(context, j);
4817 if (type == GLYPH_ATTACH_CURSIVE)
4819 /* FIXME: handle vertical direction. */
4820 context->offsets[i].ascenderOffset += context->offsets[j].ascenderOffset;
4822 else if (type == GLYPH_ATTACH_MARK)
4824 context->offsets[i].advanceOffset += context->offsets[j].advanceOffset;
4825 context->offsets[i].ascenderOffset += context->offsets[j].ascenderOffset;
4827 /* FIXME: handle vertical adjustment. */
4828 if (context->is_rtl)
4830 for (k = j + 1; k < i + 1; ++k)
4832 context->offsets[i].advanceOffset += context->advances[k];
4835 else
4837 for (k = j; k < i; k++)
4839 context->offsets[i].advanceOffset -= context->advances[k];
4845 void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index,
4846 unsigned int language_index, struct shaping_features *features)
4848 struct lookups lookups = { 0 };
4849 unsigned int i;
4850 BOOL ret;
4852 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
4853 context->u.buffer.apply_context_lookup = opentype_layout_apply_gpos_context_lookup;
4854 opentype_layout_collect_lookups(context, script_index, language_index, features, &context->cache->gpos, &lookups);
4856 for (i = 0; i < context->glyph_count; ++i)
4857 opentype_set_glyph_props(context, i);
4858 opentype_layout_set_glyph_masks(context, features);
4860 for (i = 0; i < lookups.count; ++i)
4862 const struct lookup *lookup = &lookups.lookups[i];
4864 context->cur = 0;
4865 context->lookup_mask = lookup->mask;
4866 context->auto_zwnj = lookup->auto_zwnj;
4867 context->auto_zwj = lookup->auto_zwj;
4869 while (context->cur < context->glyph_count)
4871 ret = FALSE;
4873 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
4874 lookup_is_glyph_match(context, context->cur, lookup->flags))
4876 ret = opentype_layout_apply_gpos_lookup(context, lookup);
4879 if (!ret)
4880 context->cur++;
4884 heap_free(lookups.lookups);
4886 if (context->has_gpos_attachment)
4888 for (i = 0; i < context->glyph_count; ++i)
4889 opentype_propagate_attachment_offsets(context, i);
4893 static void opentype_layout_replace_glyph(struct scriptshaping_context *context, UINT16 glyph)
4895 UINT16 orig_glyph = context->u.subst.glyphs[context->cur];
4896 if (glyph != orig_glyph)
4898 context->u.subst.glyphs[context->cur] = glyph;
4899 opentype_set_subst_glyph_props(context, context->cur);
4903 static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4904 unsigned int subtable_offset)
4906 const struct dwrite_fonttable *table = &context->table->table;
4907 UINT16 format, coverage, orig_glyph, glyph;
4908 unsigned int coverage_index;
4910 orig_glyph = glyph = context->u.subst.glyphs[context->cur];
4912 format = table_read_be_word(table, subtable_offset);
4914 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, coverage));
4916 if (format == 1)
4918 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4919 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4921 glyph = orig_glyph + table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, delta));
4923 else if (format == 2)
4925 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4926 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4928 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count),
4929 coverage_index, &glyph))
4931 return FALSE;
4934 else
4936 WARN("Unknown single substitution format %u.\n", format);
4937 return FALSE;
4940 opentype_layout_replace_glyph(context, glyph);
4941 context->cur++;
4943 return TRUE;
4946 static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *context, unsigned int count)
4948 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
4949 struct shaping_glyph_info *glyph_infos;
4950 unsigned int new_capacity;
4951 UINT16 *glyphs;
4952 BOOL ret;
4954 if (context->u.subst.capacity >= count)
4955 return TRUE;
4957 new_capacity = context->u.subst.capacity * 2;
4959 if ((glyphs = heap_realloc(context->u.subst.glyphs, new_capacity * sizeof(*glyphs))))
4960 context->u.subst.glyphs = glyphs;
4961 if ((glyph_props = heap_realloc(context->u.subst.glyph_props, new_capacity * sizeof(*glyph_props))))
4962 context->u.subst.glyph_props = glyph_props;
4963 if ((glyph_infos = heap_realloc(context->glyph_infos, new_capacity * sizeof(*glyph_infos))))
4964 context->glyph_infos = glyph_infos;
4966 if ((ret = (glyphs && glyph_props && glyph_infos)))
4967 context->u.subst.capacity = new_capacity;
4969 return ret;
4972 static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4973 unsigned int subtable_offset)
4975 const struct dwrite_fonttable *table = &context->table->table;
4976 UINT16 format, coverage, glyph, glyph_count;
4977 unsigned int i, idx, coverage_index;
4978 const UINT16 *glyphs;
4980 idx = context->cur;
4981 glyph = context->u.subst.glyphs[idx];
4983 format = table_read_be_word(table, subtable_offset);
4985 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, coverage));
4987 if (format == 1)
4989 UINT16 seq_offset;
4991 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4992 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4994 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, seq_count),
4995 coverage_index, &seq_offset))
4997 return FALSE;
5000 if (!(glyphs = table_read_array_be_word(table, subtable_offset + seq_offset, ~0u, &glyph_count))) return FALSE;
5002 if (glyph_count == 1)
5004 /* Equivalent of single substitution. */
5005 opentype_layout_replace_glyph(context, GET_BE_WORD(glyphs[0]));
5006 context->cur++;
5008 else if (glyph_count == 0)
5010 context->cur++;
5012 else
5014 unsigned int shift_len, src_idx, dest_idx, mask;
5016 /* Current glyph is also replaced. */
5017 glyph_count--;
5019 if (!(opentype_layout_gsub_ensure_buffer(context, context->glyph_count + glyph_count)))
5020 return FALSE;
5022 shift_len = context->cur + 1 < context->glyph_count ? context->glyph_count - context->cur - 1 : 0;
5024 if (shift_len)
5026 src_idx = context->cur + 1;
5027 dest_idx = src_idx + glyph_count;
5029 memmove(&context->u.subst.glyphs[dest_idx], &context->u.subst.glyphs[src_idx],
5030 shift_len * sizeof(*context->u.subst.glyphs));
5031 memmove(&context->u.subst.glyph_props[dest_idx], &context->u.subst.glyph_props[src_idx],
5032 shift_len * sizeof(*context->u.subst.glyph_props));
5033 memmove(&context->glyph_infos[dest_idx], &context->glyph_infos[src_idx],
5034 shift_len * sizeof(*context->glyph_infos));
5037 mask = context->glyph_infos[context->cur].mask;
5038 for (i = 0, idx = context->cur; i <= glyph_count; ++i)
5040 glyph = GET_BE_WORD(glyphs[i]);
5041 context->u.subst.glyphs[idx + i] = glyph;
5042 if (i)
5044 context->u.subst.glyph_props[idx + i].isClusterStart = 0;
5045 context->u.buffer.glyph_props[idx + i].components = 0;
5046 context->glyph_infos[idx + i].start_text_idx = 0;
5048 opentype_set_subst_glyph_props(context, idx + i);
5049 /* Inherit feature mask from original matched glyph. */
5050 context->glyph_infos[idx + i].mask = mask;
5053 context->cur += glyph_count + 1;
5054 context->glyph_count += glyph_count;
5057 else
5059 WARN("Unknown multiple substitution format %u.\n", format);
5060 return FALSE;
5063 return TRUE;
5066 static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5067 unsigned int subtable_offset)
5069 const struct dwrite_fonttable *table = &context->table->table;
5070 unsigned int idx, coverage_index;
5071 UINT16 format, coverage, glyph;
5073 idx = context->cur;
5074 glyph = context->u.subst.glyphs[idx];
5076 format = table_read_be_word(table, subtable_offset);
5078 if (format == 1)
5080 const struct ot_gsub_altsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
5081 unsigned int shift, alt_index;
5082 UINT16 set_offset;
5084 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, coverage));
5086 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5087 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5089 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, count),
5090 coverage_index, &set_offset))
5091 return FALSE;
5093 /* Argument is 1-based. */
5094 BitScanForward(&shift, context->lookup_mask);
5095 alt_index = (context->lookup_mask & context->glyph_infos[idx].mask) >> shift;
5096 if (!alt_index) return FALSE;
5098 if (!table_read_array_be_word(table, subtable_offset + set_offset, alt_index - 1, &glyph)) return FALSE;
5100 else
5102 WARN("Unexpected alternate substitution format %d.\n", format);
5103 return FALSE;
5106 opentype_layout_replace_glyph(context, glyph);
5107 context->cur++;
5109 return TRUE;
5112 static BOOL opentype_layout_context_match_input(const struct match_context *mc, unsigned int count, const UINT16 *input,
5113 unsigned int *end_offset, unsigned int *match_positions)
5115 struct match_data match_data = { .mc = mc, .subtable_offset = mc->input_offset };
5116 struct scriptshaping_context *context = mc->context;
5117 struct glyph_iterator iter;
5118 unsigned int i;
5120 if (count > GLYPH_CONTEXT_MAX_LENGTH)
5121 return FALSE;
5123 match_positions[0] = context->cur;
5125 glyph_iterator_init(context, mc->lookup->flags, context->cur, count - 1, &iter);
5126 iter.mask = context->lookup_mask;
5127 iter.match_func = mc->match_func;
5128 iter.match_data = &match_data;
5129 iter.glyph_data = input;
5131 for (i = 1; i < count; ++i)
5133 if (!glyph_iterator_next(&iter))
5134 return FALSE;
5136 match_positions[i] = iter.pos;
5139 *end_offset = iter.pos - context->cur + 1;
5141 return TRUE;
5144 /* Marks text segment as unsafe to break between [start, end) glyphs. */
5145 void opentype_layout_unsafe_to_break(struct scriptshaping_context *context, unsigned int start,
5146 unsigned int end)
5148 unsigned int i;
5150 while (start && !context->u.buffer.glyph_props[start].isClusterStart)
5151 --start;
5153 while (--end && !context->u.buffer.glyph_props[end].isClusterStart)
5156 if (start == end)
5158 context->u.buffer.text_props[context->glyph_infos[start].start_text_idx].canBreakShapingAfter = 0;
5159 return;
5162 for (i = context->glyph_infos[start].start_text_idx; i < context->glyph_infos[end].start_text_idx; ++i)
5164 context->u.buffer.text_props[i].canBreakShapingAfter = 0;
5168 static void opentype_layout_delete_glyph(struct scriptshaping_context *context, unsigned int idx)
5170 unsigned int shift_len;
5172 shift_len = context->glyph_count - context->cur - 1;
5174 if (shift_len)
5176 memmove(&context->u.buffer.glyphs[idx], &context->u.buffer.glyphs[idx + 1],
5177 shift_len * sizeof(*context->u.buffer.glyphs));
5178 memmove(&context->u.buffer.glyph_props[idx], &context->u.buffer.glyph_props[idx + 1],
5179 shift_len * sizeof(*context->u.buffer.glyph_props));
5180 memmove(&context->glyph_infos[idx], &context->glyph_infos[idx + 1], shift_len * sizeof(*context->glyph_infos));
5183 context->glyph_count--;
5186 static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset,
5187 const struct lookup *lookup)
5189 struct match_context mc = { .context = context, .lookup = lookup, .match_func = opentype_match_glyph_func };
5190 const struct dwrite_fonttable *gsub = &context->table->table;
5191 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5192 unsigned int i, j, comp_count, match_length = 0;
5193 const struct ot_gsub_lig *lig;
5194 UINT16 lig_glyph;
5196 comp_count = table_read_be_word(gsub, offset + FIELD_OFFSET(struct ot_gsub_lig, comp_count));
5198 if (!comp_count)
5199 return FALSE;
5201 lig = table_read_ensure(gsub, offset, FIELD_OFFSET(struct ot_gsub_lig, components[comp_count-1]));
5202 if (!lig)
5203 return FALSE;
5205 lig_glyph = GET_BE_WORD(lig->lig_glyph);
5207 if (comp_count == 1)
5209 opentype_layout_replace_glyph(context, lig_glyph);
5210 context->cur++;
5211 return TRUE;
5214 if (!opentype_layout_context_match_input(&mc, comp_count, lig->components, &match_length, match_positions))
5215 return FALSE;
5217 opentype_layout_replace_glyph(context, lig_glyph);
5218 context->u.buffer.glyph_props[context->cur].components = comp_count;
5220 /* Positioning against a ligature implies keeping track of ligature component
5221 glyph should be attached to. Update per-glyph property for interleaving glyphs,
5222 0 means attaching to last component, n - attaching to n-th glyph before last. */
5223 for (i = 1; i < comp_count; ++i)
5225 j = match_positions[i - 1] + 1;
5226 while (j < match_positions[i])
5228 context->u.buffer.glyph_props[j++].lig_component = comp_count - i;
5231 opentype_layout_unsafe_to_break(context, match_positions[0], match_positions[comp_count - 1] + 1);
5233 /* Delete ligated glyphs, backwards to preserve index. */
5234 for (i = 1; i < comp_count; ++i)
5236 opentype_layout_delete_glyph(context, match_positions[comp_count - i]);
5239 /* Skip whole matched sequence, accounting for deleted glyphs. */
5240 context->cur += match_length - (comp_count - 1);
5242 return TRUE;
5245 static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5246 unsigned int subtable_offset)
5248 const struct dwrite_fonttable *table = &context->table->table;
5249 UINT16 format, coverage, glyph, lig_set_offset;
5250 unsigned int coverage_index;
5252 glyph = context->u.subst.glyphs[context->cur];
5254 format = table_read_be_word(table, subtable_offset);
5256 if (format == 1)
5258 const struct ot_gsub_ligsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
5259 unsigned int i;
5260 const UINT16 *offsets;
5261 UINT16 lig_count;
5263 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, coverage));
5265 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5266 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5268 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_set_count),
5269 coverage_index, &lig_set_offset))
5270 return FALSE;
5272 if (!(offsets = table_read_array_be_word(table, subtable_offset + lig_set_offset, ~0u, &lig_count)))
5273 return FALSE;
5275 /* First applicable ligature is used. */
5276 for (i = 0; i < lig_count; ++i)
5278 if (opentype_layout_apply_ligature(context, subtable_offset + lig_set_offset + GET_BE_WORD(offsets[i]), lookup))
5279 return TRUE;
5282 else
5283 WARN("Unexpected ligature substitution format %d.\n", format);
5285 return FALSE;
5288 static BOOL opentype_layout_context_match_backtrack(const struct match_context *mc, unsigned int count,
5289 const UINT16 *backtrack, unsigned int *match_start)
5291 struct match_data match_data = { .mc = mc, .subtable_offset = mc->backtrack_offset };
5292 struct scriptshaping_context *context = mc->context;
5293 struct glyph_iterator iter;
5294 unsigned int i;
5296 glyph_iterator_init(context, mc->lookup->flags, context->cur, count, &iter);
5297 iter.match_func = mc->match_func;
5298 iter.match_data = &match_data;
5299 iter.glyph_data = backtrack;
5300 iter.ignore_zwnj |= context->auto_zwnj;
5301 iter.ignore_zwj = 1;
5303 for (i = 0; i < count; ++i)
5305 if (!glyph_iterator_prev(&iter))
5306 return FALSE;
5309 *match_start = iter.pos;
5311 return TRUE;
5314 static BOOL opentype_layout_context_match_lookahead(const struct match_context *mc, unsigned int count,
5315 const UINT16 *lookahead, unsigned int offset, unsigned int *end_index)
5317 struct match_data match_data = { .mc = mc, .subtable_offset = mc->lookahead_offset };
5318 struct scriptshaping_context *context = mc->context;
5319 struct glyph_iterator iter;
5320 unsigned int i;
5322 glyph_iterator_init(context, mc->lookup->flags, context->cur + offset - 1, count, &iter);
5323 iter.match_func = mc->match_func;
5324 iter.match_data = &match_data;
5325 iter.glyph_data = lookahead;
5326 iter.ignore_zwnj |= context->auto_zwnj;
5327 iter.ignore_zwj = 1;
5329 for (i = 0; i < count; ++i)
5331 if (!glyph_iterator_next(&iter))
5332 return FALSE;
5335 *end_index = iter.pos;
5337 return TRUE;
5340 static BOOL opentype_layout_context_apply_lookup(struct scriptshaping_context *context, unsigned int count,
5341 unsigned int *match_positions, unsigned int lookup_count, const UINT16 *lookup_records, unsigned int match_length)
5343 unsigned int i, j;
5344 int end, delta;
5346 if (!context->nesting_level_left)
5347 return TRUE;
5349 end = context->cur + match_length;
5351 for (i = 0; i < lookup_count; ++i)
5353 unsigned int idx = GET_BE_WORD(lookup_records[i]);
5354 unsigned int orig_len, lookup_index, next;
5356 if (idx >= count)
5357 continue;
5359 context->cur = match_positions[idx];
5361 orig_len = context->glyph_count;
5363 lookup_index = GET_BE_WORD(lookup_records[i+1]);
5365 --context->nesting_level_left;
5366 context->u.buffer.apply_context_lookup(context, lookup_index);
5367 ++context->nesting_level_left;
5369 delta = context->glyph_count - orig_len;
5370 if (!delta)
5371 continue;
5373 end += delta;
5374 if (end <= (int)match_positions[idx])
5376 end = match_positions[idx];
5377 break;
5380 next = idx + 1;
5382 if (delta > 0)
5384 if (delta + count > GLYPH_CONTEXT_MAX_LENGTH)
5385 break;
5387 else
5389 delta = max(delta, (int)next - (int)count);
5390 next -= delta;
5393 memmove(match_positions + next + delta, match_positions + next,
5394 (count - next) * sizeof (*match_positions));
5395 next += delta;
5396 count += delta;
5398 for (j = idx + 1; j < next; j++)
5399 match_positions[j] = match_positions[j - 1] + 1;
5401 for (; next < count; next++)
5402 match_positions[next] += delta;
5405 context->cur = end;
5407 return TRUE;
5410 static BOOL opentype_layout_apply_chain_context_match(unsigned int backtrack_count, const UINT16 *backtrack,
5411 unsigned int input_count, const UINT16 *input, unsigned int lookahead_count, const UINT16 *lookahead,
5412 unsigned int lookup_count, const UINT16 *lookup_records, const struct match_context *mc)
5414 unsigned int start_index = 0, match_length = 0, end_index = 0;
5415 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5417 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5418 opentype_layout_context_match_backtrack(mc, backtrack_count, backtrack, &start_index) &&
5419 opentype_layout_context_match_lookahead(mc, lookahead_count, lookahead, input_count, &end_index) &&
5420 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count, lookup_records, match_length);
5423 static BOOL opentype_layout_apply_chain_rule_set(const struct match_context *mc, unsigned int offset)
5425 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5426 const struct dwrite_fonttable *table = &mc->context->table->table;
5427 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5428 const struct ot_gsubgpos_ruleset *ruleset;
5429 unsigned int i, count;
5431 count = table_read_be_word(table, offset);
5432 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5434 for (i = 0; i < count; ++i)
5436 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5438 backtrack_count = table_read_be_word(table, rule_offset);
5439 rule_offset += 2;
5440 backtrack = table_read_ensure(table, rule_offset, backtrack_count * sizeof(*backtrack));
5441 rule_offset += backtrack_count * sizeof(*backtrack);
5443 if (!(input_count = table_read_be_word(table, rule_offset)))
5444 continue;
5446 rule_offset += 2;
5447 input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input));
5448 rule_offset += (input_count - 1) * sizeof(*input);
5450 lookahead_count = table_read_be_word(table, rule_offset);
5451 rule_offset += 2;
5452 lookahead = table_read_ensure(table, rule_offset, lookahead_count * sizeof(*lookahead));
5453 rule_offset += lookahead_count * sizeof(*lookahead);
5455 lookup_count = table_read_be_word(table, rule_offset);
5456 rule_offset += 2;
5457 lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records));
5459 /* First applicable rule is used. */
5460 if (opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input, lookahead_count,
5461 lookahead, lookup_count, lookup_records, mc))
5463 return TRUE;
5467 return FALSE;
5470 static BOOL opentype_layout_apply_context_match(unsigned int input_count, const UINT16 *input, unsigned int lookup_count,
5471 const UINT16 *lookup_records, const struct match_context *mc)
5473 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5474 unsigned int match_length = 0;
5476 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5477 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count,
5478 lookup_records, match_length);
5481 static BOOL opentype_layout_apply_rule_set(const struct match_context *mc, unsigned int offset)
5483 unsigned int input_count, lookup_count;
5484 const struct dwrite_fonttable *table = &mc->context->table->table;
5485 const UINT16 *input, *lookup_records;
5486 const struct ot_gsubgpos_ruleset *ruleset;
5487 unsigned int i, count;
5489 count = table_read_be_word(table, offset);
5490 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5492 for (i = 0; i < count; ++i)
5494 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5496 if (!(input_count = table_read_be_word(table, rule_offset)))
5497 continue;
5498 rule_offset += 2;
5500 if (!(lookup_count = table_read_be_word(table, rule_offset)))
5501 continue;
5502 rule_offset += 2;
5504 if (!(input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input))))
5505 continue;
5506 rule_offset += (input_count - 1) * sizeof(*input);
5508 if (!(lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records))))
5509 continue;
5511 /* First applicable rule is used. */
5512 if (opentype_layout_apply_context_match(input_count, input, lookup_count, lookup_records, mc))
5513 return TRUE;
5516 return FALSE;
5519 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
5520 unsigned int subtable_offset)
5522 struct match_context mc = { .context = context, .lookup = lookup };
5523 const struct dwrite_fonttable *table = &context->table->table;
5524 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5525 UINT16 glyph, format, coverage;
5526 BOOL ret = FALSE;
5528 glyph = context->u.subst.glyphs[context->cur];
5530 format = table_read_be_word(table, subtable_offset);
5532 if (format == 1)
5534 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5536 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5537 if (coverage_index == GLYPH_NOT_COVERED)
5538 return FALSE;
5540 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5541 if (coverage_index >= count)
5542 return FALSE;
5544 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5545 rulesets[coverage_index]));
5546 offset += subtable_offset;
5548 mc.match_func = opentype_match_glyph_func;
5550 ret = opentype_layout_apply_rule_set(&mc, offset);
5552 else if (format == 2)
5554 unsigned int input_classdef, rule_set_idx;
5556 offset = subtable_offset + 2 /* format */;
5558 coverage = table_read_be_word(table, offset);
5559 offset += 2;
5561 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5562 if (coverage_index == GLYPH_NOT_COVERED)
5563 return FALSE;
5565 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5566 offset += 2;
5568 count = table_read_be_word(table, offset);
5569 offset+= 2;
5571 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5572 if (rule_set_idx >= count)
5573 return FALSE;
5575 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5576 offset += subtable_offset;
5578 mc.input_offset = input_classdef;
5579 mc.match_func = opentype_match_class_func;
5581 ret = opentype_layout_apply_rule_set(&mc, offset);
5583 else if (format == 3)
5585 unsigned int input_count, lookup_count;
5586 const UINT16 *input, *lookup_records;
5588 offset = subtable_offset + 2 /* format */;
5590 input_count = table_read_be_word(table, offset);
5591 offset += 2;
5593 if (!input_count)
5594 return FALSE;
5596 lookup_count = table_read_be_word(table, offset);
5597 offset += 2;
5599 if (!(input = table_read_ensure(table, offset, sizeof(*input) * input_count)))
5600 return FALSE;
5601 offset += sizeof(*input) * input_count;
5603 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5604 if (coverage_index == GLYPH_NOT_COVERED)
5605 return FALSE;
5607 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5609 mc.input_offset = subtable_offset;
5610 mc.match_func = opentype_match_coverage_func;
5612 ret = opentype_layout_apply_context_match(input_count, input + 1, lookup_count, lookup_records, &mc);
5614 else
5615 WARN("Unknown contextual substitution format %u.\n", format);
5617 return ret;
5620 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
5621 unsigned int subtable_offset)
5623 struct match_context mc = { .context = context, .lookup = lookup };
5624 const struct dwrite_fonttable *table = &context->table->table;
5625 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5626 UINT16 glyph, format, coverage;
5627 BOOL ret = FALSE;
5629 glyph = context->u.subst.glyphs[context->cur];
5631 format = table_read_be_word(table, subtable_offset);
5633 if (format == 1)
5635 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5637 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5638 if (coverage_index == GLYPH_NOT_COVERED)
5639 return FALSE;
5641 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5642 if (coverage_index >= count)
5643 return FALSE;
5645 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5646 rulesets[coverage_index]));
5647 offset += subtable_offset;
5649 mc.match_func = opentype_match_glyph_func;
5651 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5653 else if (format == 2)
5655 unsigned int backtrack_classdef, input_classdef, lookahead_classdef, rule_set_idx;
5657 offset = subtable_offset + 2 /* format */;
5659 coverage = table_read_be_word(table, offset);
5660 offset += 2;
5662 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5663 if (coverage_index == GLYPH_NOT_COVERED)
5664 return FALSE;
5666 backtrack_classdef = table_read_be_word(table, offset) + subtable_offset;
5667 offset += 2;
5669 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5670 offset += 2;
5672 lookahead_classdef = table_read_be_word(table, offset) + subtable_offset;
5673 offset += 2;
5675 count = table_read_be_word(table, offset);
5676 offset+= 2;
5678 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5679 if (rule_set_idx >= count)
5680 return FALSE;
5682 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5683 offset += subtable_offset;
5685 mc.backtrack_offset = backtrack_classdef;
5686 mc.input_offset = input_classdef;
5687 mc.lookahead_offset = lookahead_classdef;
5688 mc.match_func = opentype_match_class_func;
5690 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5692 else if (format == 3)
5694 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5695 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5697 offset = subtable_offset + 2 /* format */;
5699 backtrack_count = table_read_be_word(table, offset);
5700 offset += 2;
5701 backtrack = table_read_ensure(table, offset, backtrack_count * sizeof(*backtrack));
5702 offset += backtrack_count * sizeof(*backtrack);
5704 input_count = table_read_be_word(table, offset);
5705 offset += 2;
5706 input = table_read_ensure(table, offset, input_count * sizeof(*input));
5707 offset += input_count * sizeof(*input);
5709 lookahead_count = table_read_be_word(table, offset);
5710 offset += 2;
5711 lookahead = table_read_ensure(table, offset, lookahead_count * sizeof(*lookahead));
5712 offset += lookahead_count * sizeof(*lookahead);
5714 lookup_count = table_read_be_word(table, offset);
5715 offset += 2;
5716 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5718 if (input)
5719 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5721 if (coverage_index == GLYPH_NOT_COVERED)
5722 return FALSE;
5724 mc.backtrack_offset = subtable_offset;
5725 mc.input_offset = subtable_offset;
5726 mc.lookahead_offset = subtable_offset;
5727 mc.match_func = opentype_match_coverage_func;
5729 ret = opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input + 1, lookahead_count,
5730 lookahead, lookup_count, lookup_records, &mc);
5732 else
5733 WARN("Unknown chaining contextual substitution format %u.\n", format);
5735 return ret;
5738 static BOOL opentype_layout_apply_gsub_reverse_chain_context_substitution(struct scriptshaping_context *context,
5739 const struct lookup *lookup, unsigned int subtable_offset)
5741 const struct dwrite_fonttable *table = &context->table->table;
5742 unsigned int offset = subtable_offset;
5743 UINT16 glyph, format;
5745 if (context->nesting_level_left != SHAPE_MAX_NESTING_LEVEL)
5746 return FALSE;
5748 glyph = context->u.subst.glyphs[context->cur];
5750 format = table_read_be_word(table, offset);
5751 offset += 2;
5753 if (format == 1)
5755 struct match_context mc = { .context = context, .lookup = lookup };
5756 unsigned int start_index = 0, end_index = 0, backtrack_count, lookahead_count;
5757 unsigned int coverage, coverage_index;
5758 const UINT16 *backtrack, *lookahead;
5760 coverage = table_read_be_word(table, offset);
5761 offset += 2;
5763 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5764 if (coverage_index == GLYPH_NOT_COVERED)
5765 return FALSE;
5767 backtrack_count = table_read_be_word(table, offset);
5768 offset += 2;
5770 backtrack = table_read_ensure(table, offset, sizeof(*backtrack) * backtrack_count);
5771 offset += sizeof(*backtrack) * backtrack_count;
5773 lookahead_count = table_read_be_word(table, offset);
5774 offset += 2;
5776 lookahead = table_read_ensure(table, offset, sizeof(*lookahead) * lookahead_count);
5777 offset += sizeof(*lookahead) * lookahead_count;
5779 mc.match_func = opentype_match_coverage_func;
5780 mc.backtrack_offset = subtable_offset;
5781 mc.lookahead_offset = subtable_offset;
5783 if (opentype_layout_context_match_backtrack(&mc, backtrack_count, backtrack, &start_index) &&
5784 opentype_layout_context_match_lookahead(&mc, lookahead_count, lookahead, 1, &end_index))
5786 unsigned int glyph_count = table_read_be_word(table, offset);
5787 if (coverage_index >= glyph_count)
5788 return FALSE;
5789 offset += 2;
5791 glyph = table_read_be_word(table, offset + coverage_index * sizeof(glyph));
5792 opentype_layout_replace_glyph(context, glyph);
5794 return TRUE;
5797 else
5798 WARN("Unknown reverse chaining contextual substitution format %u.\n", format);
5800 return FALSE;
5803 static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
5805 unsigned int i, lookup_type;
5806 BOOL ret = FALSE;
5808 for (i = 0; i < lookup->subtable_count; ++i)
5810 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
5812 switch (lookup_type)
5814 case GSUB_LOOKUP_SINGLE_SUBST:
5815 ret = opentype_layout_apply_gsub_single_substitution(context, lookup, subtable_offset);
5816 break;
5817 case GSUB_LOOKUP_MULTIPLE_SUBST:
5818 ret = opentype_layout_apply_gsub_mult_substitution(context, lookup, subtable_offset);
5819 break;
5820 case GSUB_LOOKUP_ALTERNATE_SUBST:
5821 ret = opentype_layout_apply_gsub_alt_substitution(context, lookup, subtable_offset);
5822 break;
5823 case GSUB_LOOKUP_LIGATURE_SUBST:
5824 ret = opentype_layout_apply_gsub_lig_substitution(context, lookup, subtable_offset);
5825 break;
5826 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
5827 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
5828 break;
5829 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
5830 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
5831 break;
5832 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
5833 ret = opentype_layout_apply_gsub_reverse_chain_context_substitution(context, lookup, subtable_offset);
5834 break;
5835 case GSUB_LOOKUP_EXTENSION_SUBST:
5836 WARN("Invalid lookup type for extension substitution %#x.\n", lookup_type);
5837 break;
5838 default:
5839 WARN("Unknown lookup type %u.\n", lookup_type);
5842 if (ret)
5843 break;
5846 return ret;
5849 static unsigned int unicode_get_mirrored_char(unsigned int codepoint)
5851 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
5852 WCHAR mirror;
5853 /* TODO: check if mirroring for higher planes makes sense at all */
5854 if (codepoint > 0xffff) return codepoint;
5855 mirror = get_table_entry(wine_mirror_map, codepoint);
5856 return mirror ? mirror : codepoint;
5860 * 034F # Mn COMBINING GRAPHEME JOINER
5861 * 061C # Cf ARABIC LETTER MARK
5862 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5863 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5864 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5865 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5867 static unsigned int opentype_is_zero_width(unsigned int codepoint)
5869 return codepoint == 0x34f || codepoint == 0x61c || codepoint == 0xfeff ||
5870 (codepoint >= 0x180b && codepoint <= 0x180e) || (codepoint >= 0x200b && codepoint <= 0x200f);
5874 * 00AD # Cf SOFT HYPHEN
5875 * 034F # Mn COMBINING GRAPHEME JOINER
5876 * 061C # Cf ARABIC LETTER MARK
5877 * 115F..1160 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER
5878 * 17B4..17B5 # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
5879 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5880 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5881 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5882 * 202A..202E # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
5883 * 2060..2064 # Cf [5] WORD JOINER..INVISIBLE PLUS
5884 * 2065 # Cn <reserved-2065>
5885 * 2066..206F # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
5886 * 3164 # Lo HANGUL FILLER
5887 * FE00..FE0F # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
5888 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5889 * FFA0 # Lo HALFWIDTH HANGUL FILLER
5890 * FFF0..FFF8 # Cn [9] <reserved-FFF0>..<reserved-FFF8>
5891 * 1BCA0..1BCA3 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
5892 * 1D173..1D17A # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
5893 * E0000 # Cn <reserved-E0000>
5894 * E0001 # Cf LANGUAGE TAG
5895 * E0002..E001F # Cn [30] <reserved-E0002>..<reserved-E001F>
5896 * E0020..E007F # Cf [96] TAG SPACE..CANCEL TAG
5897 * E0080..E00FF # Cn [128] <reserved-E0080>..<reserved-E00FF>
5898 * E0100..E01EF # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
5899 * E01F0..E0FFF # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
5901 static unsigned int opentype_is_default_ignorable(unsigned int codepoint)
5903 if (codepoint < 0x80) return 0;
5904 return codepoint == 0xad ||
5905 codepoint == 0x34f ||
5906 codepoint == 0x61c ||
5907 (codepoint >= 0x17b4 && codepoint <= 0x17b5) ||
5908 (codepoint >= 0x180b && codepoint <= 0x180e) ||
5909 (codepoint >= 0x200b && codepoint <= 0x200f) ||
5910 (codepoint >= 0x202a && codepoint <= 0x202e) ||
5911 (codepoint >= 0x2060 && codepoint <= 0x206f) ||
5912 (codepoint >= 0xfe00 && codepoint <= 0xfe0f) ||
5913 codepoint == 0xfeff ||
5914 (codepoint >= 0xfff0 && codepoint <= 0xfff8) ||
5915 (codepoint >= 0x1d173 && codepoint <= 0x1d17a) ||
5916 (codepoint >= 0xe0000 && codepoint <= 0xe0fff);
5919 static unsigned int opentype_is_diacritic(unsigned int codepoint)
5921 WCHAR ch = codepoint;
5922 WORD type = 0;
5923 /* Ignore higher planes for now. */
5924 if (codepoint > 0xffff) return 0;
5925 GetStringTypeW(CT_CTYPE3, &ch, 1, &type);
5926 return !!(type & C3_DIACRITIC);
5929 static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, const struct shaping_features *features)
5931 unsigned int rtlm_mask = shaping_features_get_mask(features, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), NULL);
5932 const struct shaping_font_ops *font = context->cache->font;
5933 unsigned int i, g, c, codepoint, cluster_start_idx = 0;
5934 UINT16 *clustermap = context->u.subst.clustermap;
5935 const WCHAR *text = context->text;
5936 BOOL bmp;
5938 memset(context->u.subst.glyph_props, 0, context->u.subst.max_glyph_count * sizeof(*context->u.subst.glyph_props));
5939 memset(context->u.buffer.text_props, 0, context->length * sizeof(*context->u.buffer.text_props));
5941 for (i = 0; i < context->length; ++i)
5943 g = context->glyph_count;
5945 if ((bmp = !(IS_HIGH_SURROGATE(text[i]) && (i < context->length - 1) && IS_LOW_SURROGATE(text[i + 1]))))
5947 codepoint = text[i];
5949 else
5951 codepoint = 0x10000 + ((text[i] - 0xd800) << 10) + (text[i + 1] - 0xdc00);
5954 if (context->is_rtl)
5956 c = unicode_get_mirrored_char(codepoint);
5957 if (c != codepoint && font->has_glyph(context->cache->context, c))
5958 codepoint = c;
5959 else
5960 context->glyph_infos[i].mask |= rtlm_mask;
5963 /* Glyph availability is not tested for a replacement digit. */
5964 if (*context->u.subst.digits && codepoint >= '0' && codepoint <= '9')
5965 codepoint = context->u.subst.digits[codepoint - '0'];
5967 context->glyph_infos[g].codepoint = codepoint;
5968 context->u.buffer.glyphs[g] = font->get_glyph(context->cache->context, codepoint);
5969 context->u.buffer.glyph_props[g].justification = SCRIPT_JUSTIFY_CHARACTER;
5970 opentype_set_subst_glyph_props(context, g);
5971 if (opentype_is_default_ignorable(codepoint))
5973 context->glyph_infos[g].props |= GLYPH_PROP_IGNORABLE;
5974 if (codepoint == 0x200d)
5975 context->glyph_infos[g].props |= GLYPH_PROP_ZWJ;
5976 else if (codepoint == 0x200c)
5977 context->glyph_infos[g].props |= GLYPH_PROP_ZWNJ;
5978 /* Mongolian FVSs, TAGs, COMBINING GRAPHEME JOINER */
5979 else if ((codepoint >= 0x180b && codepoint <= 0x180d) ||
5980 (codepoint >= 0xe0020 && codepoint <= 0xe007f) ||
5981 codepoint == 0x34f)
5983 context->glyph_infos[g].props |= GLYPH_PROP_HIDDEN;
5987 /* Group diacritics with preceding base. Glyph class is ignored here. */
5988 if (!g || !opentype_is_diacritic(codepoint))
5990 context->u.buffer.glyph_props[g].isClusterStart = 1;
5991 context->glyph_infos[g].start_text_idx = i;
5992 cluster_start_idx = g;
5994 if (opentype_is_zero_width(codepoint))
5995 context->u.buffer.glyph_props[g].isZeroWidthSpace = 1;
5997 context->u.buffer.glyph_props[g].components = 1;
5998 context->glyph_count++;
6000 /* Set initial cluster map here, it's used for setting user features masks. */
6001 clustermap[i] = cluster_start_idx;
6002 if (bmp)
6003 context->u.buffer.text_props[i].canBreakShapingAfter = 1;
6004 else
6006 clustermap[i + 1] = cluster_start_idx;
6007 context->u.buffer.text_props[i + 1].canBreakShapingAfter = 1;
6008 ++i;
6013 static BOOL opentype_is_gsub_lookup_reversed(const struct scriptshaping_context *context, const struct lookup *lookup)
6015 unsigned int lookup_type;
6017 opentype_layout_get_gsubgpos_subtable(context, lookup, 0, &lookup_type);
6018 return lookup_type == GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST;
6021 static void opentype_layout_apply_gsub_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
6023 struct lookup lookup = { 0 };
6024 if (opentype_layout_init_lookup(context->table, lookup_index, NULL, &lookup))
6025 opentype_layout_apply_gsub_lookup(context, &lookup);
6028 void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int script_index,
6029 unsigned int language_index, struct shaping_features *features)
6031 struct lookups lookups = { 0 };
6032 unsigned int i = 0, j, start_idx;
6033 BOOL ret;
6035 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
6036 context->u.buffer.apply_context_lookup = opentype_layout_apply_gsub_context_lookup;
6037 opentype_layout_collect_lookups(context, script_index, language_index, features, context->table, &lookups);
6039 opentype_get_nominal_glyphs(context, features);
6040 opentype_layout_set_glyph_masks(context, features);
6042 for (j = 0; j <= features->stage; ++j)
6044 for (; i < features->stages[j].last_lookup; ++i)
6046 const struct lookup *lookup = &lookups.lookups[i];
6048 context->lookup_mask = lookup->mask;
6049 context->auto_zwnj = lookup->auto_zwnj;
6050 context->auto_zwj = lookup->auto_zwj;
6052 if (!opentype_is_gsub_lookup_reversed(context, lookup))
6054 context->cur = 0;
6055 while (context->cur < context->glyph_count)
6057 ret = FALSE;
6059 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
6060 lookup_is_glyph_match(context, context->cur, lookup->flags))
6062 ret = opentype_layout_apply_gsub_lookup(context, lookup);
6065 if (!ret)
6066 context->cur++;
6069 else
6071 context->cur = context->glyph_count - 1;
6073 for (;;)
6075 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
6076 lookup_is_glyph_match(context, context->cur, lookup->flags))
6078 opentype_layout_apply_gsub_lookup(context, lookup);
6081 if (context->cur == 0) break;
6082 --context->cur;
6087 if (features->stages[j].func)
6088 features->stages[j].func(context, features);
6091 /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding
6092 text span to start_idx. */
6093 start_idx = 0;
6094 for (i = 1; i < context->glyph_count; ++i)
6096 if (context->u.buffer.glyph_props[i].isClusterStart)
6098 unsigned int start_text, end_text;
6100 start_text = context->glyph_infos[start_idx].start_text_idx;
6101 end_text = context->glyph_infos[i].start_text_idx;
6103 for (j = start_text; j < end_text; ++j)
6104 context->u.buffer.clustermap[j] = start_idx;
6106 start_idx = i;
6110 /* Fill the tail. */
6111 for (j = context->glyph_infos[start_idx].start_text_idx; j < context->length; ++j)
6112 context->u.buffer.clustermap[j] = start_idx;
6114 heap_free(lookups.lookups);
6117 static BOOL opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6118 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
6120 const struct dwrite_fonttable *table = &context->table->table;
6121 const UINT16 *offsets;
6122 unsigned int count;
6124 if (format == 1 || format == 2)
6126 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6127 return TRUE;
6129 else if (format == 3)
6131 count = table_read_be_word(table, subtable_offset + 2);
6132 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6, count * sizeof(*offsets))))
6133 return FALSE;
6135 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
6136 return TRUE;
6139 return FALSE;
6142 static BOOL opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6143 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
6145 const struct dwrite_fonttable *table = &context->table->table;
6146 unsigned int count, backtrack_count;
6147 const UINT16 *offsets;
6149 if (format == 1 || format == 2)
6151 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6152 return TRUE;
6154 else if (format == 3)
6156 backtrack_count = table_read_be_word(table, subtable_offset + 2);
6158 count = table_read_be_word(table, subtable_offset + 4 + backtrack_count * sizeof(*offsets));
6160 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6 + backtrack_count * sizeof(*offsets),
6161 count * sizeof(*offsets))))
6162 return FALSE;
6164 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
6165 return TRUE;
6168 return FALSE;
6171 static BOOL opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6172 const struct lookup *lookup)
6174 const struct dwrite_fonttable *gsub = &context->table->table;
6175 static const unsigned short gsub_formats[] =
6177 0, /* Unused */
6178 1, /* SingleSubst */
6179 1, /* MultipleSubst */
6180 1, /* AlternateSubst */
6181 1, /* LigatureSubst */
6182 3, /* ContextSubst */
6183 3, /* ChainContextSubst */
6184 0, /* Extension, unused */
6185 1, /* ReverseChainSubst */
6187 unsigned int i, coverage, lookup_type, format;
6189 for (i = 0; i < lookup->subtable_count; ++i)
6191 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
6193 format = table_read_be_word(gsub, subtable_offset);
6195 if (!format || format > ARRAY_SIZE(gsub_formats) || format > gsub_formats[lookup_type])
6196 break;
6198 coverage = table_read_be_word(gsub, subtable_offset + 2);
6200 switch (lookup_type)
6202 case GSUB_LOOKUP_SINGLE_SUBST:
6203 case GSUB_LOOKUP_MULTIPLE_SUBST:
6204 case GSUB_LOOKUP_ALTERNATE_SUBST:
6205 case GSUB_LOOKUP_LIGATURE_SUBST:
6206 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
6208 if (opentype_layout_is_glyph_covered(gsub, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6209 return TRUE;
6211 break;
6213 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
6215 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6216 return TRUE;
6218 break;
6220 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
6222 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6223 return TRUE;
6225 break;
6227 default:
6228 WARN("Unknown lookup type %u.\n", lookup_type);
6232 return FALSE;
6235 static BOOL opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6236 const struct lookup *lookup)
6238 const struct dwrite_fonttable *gpos = &context->table->table;
6239 static const unsigned short gpos_formats[] =
6241 0, /* Unused */
6242 2, /* SinglePos */
6243 2, /* PairPos */
6244 1, /* CursivePos */
6245 1, /* MarkBasePos */
6246 1, /* MarkLigPos */
6247 1, /* MarkMarkPos */
6248 3, /* ContextPos */
6249 3, /* ChainContextPos */
6250 0, /* Extension, unused */
6252 unsigned int i, coverage, lookup_type, format;
6254 for (i = 0; i < lookup->subtable_count; ++i)
6256 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
6258 format = table_read_be_word(gpos, subtable_offset);
6260 if (!format || format > ARRAY_SIZE(gpos_formats) || format > gpos_formats[lookup_type])
6261 break;
6263 coverage = table_read_be_word(gpos, subtable_offset + 2);
6265 switch (lookup_type)
6267 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
6268 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
6269 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
6270 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
6271 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
6272 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
6274 if (opentype_layout_is_glyph_covered(gpos, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6275 return TRUE;
6277 break;
6279 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
6281 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6282 return TRUE;
6284 break;
6286 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
6288 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6289 return TRUE;
6291 break;
6293 default:
6294 WARN("Unknown lookup type %u.\n", lookup_type);
6298 return FALSE;
6301 typedef BOOL (*p_lookup_is_glyph_covered_func)(struct scriptshaping_context *context, UINT16 glyph, const struct lookup *lookup);
6303 BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index,
6304 unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count,
6305 const UINT16 *glyphs, UINT8 *feature_applies)
6307 p_lookup_is_glyph_covered_func func_is_covered;
6308 struct shaping_features features = { 0 };
6309 struct lookups lookups = { 0 };
6310 BOOL ret = FALSE, is_covered;
6311 unsigned int i, j, applies;
6313 features.features = feature;
6314 features.count = 1;
6316 for (i = 0; i < context->glyph_count; ++i)
6317 opentype_set_glyph_props(context, i);
6319 opentype_layout_collect_lookups(context, script_index, language_index, &features, context->table, &lookups);
6321 func_is_covered = opentype_layout_is_subst_context(context) ? opentype_layout_gsub_lookup_is_glyph_covered :
6322 opentype_layout_gpos_lookup_is_glyph_covered;
6324 for (i = 0; i < lookups.count; ++i)
6326 struct lookup *lookup = &lookups.lookups[i];
6328 applies = 0;
6329 for (j = 0; j < context->glyph_count; ++j)
6331 if (lookup_is_glyph_match(context, j, lookup->flags))
6333 if ((is_covered = func_is_covered(context, glyphs[i], lookup)))
6334 ++applies;
6335 feature_applies[j] |= is_covered;
6339 if ((ret = (applies == context->glyph_count)))
6340 break;
6343 heap_free(lookups.lookups);
6345 return ret;
6348 BOOL opentype_has_vertical_variants(struct dwrite_fontface *fontface)
6350 unsigned int i, j, count = 0, lookup_type, subtable_offset;
6351 struct shaping_features features = { 0 };
6352 struct shaping_feature vert_feature = { 0 };
6353 struct scriptshaping_context context = { 0 };
6354 struct lookups lookups = { 0 };
6355 UINT16 format;
6357 if (fontface->flags & (FONTFACE_VERTICAL_VARIANTS | FONTFACE_NO_VERTICAL_VARIANTS))
6358 return !!(fontface->flags & FONTFACE_VERTICAL_VARIANTS);
6360 context.cache = fontface_get_shaping_cache(fontface);
6361 context.table = &context.cache->gsub;
6363 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6364 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6365 vert_feature.max_value = 1;
6366 vert_feature.default_value = 1;
6368 features.features = &vert_feature;
6369 features.count = features.capacity = 1;
6371 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6373 for (i = 0; i < lookups.count && !count; ++i)
6375 const struct dwrite_fonttable *table = &context.table->table;
6376 const struct lookup *lookup = &lookups.lookups[i];
6378 for (j = 0; j < lookup->subtable_count && !count; ++j)
6380 subtable_offset = opentype_layout_get_gsubgpos_subtable(&context, lookup, j, &lookup_type);
6382 if (lookup_type != GSUB_LOOKUP_SINGLE_SUBST)
6383 continue;
6385 format = table_read_be_word(table, subtable_offset);
6387 if (format == 1)
6389 count = 1;
6391 else if (format == 2)
6393 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count));
6395 else
6396 WARN("Unrecognized single substitution format %u.\n", format);
6400 heap_free(lookups.lookups);
6402 if (count)
6403 fontface->flags |= FONTFACE_VERTICAL_VARIANTS;
6404 else
6405 fontface->flags |= FONTFACE_NO_VERTICAL_VARIANTS;
6407 return !!(fontface->flags & FONTFACE_VERTICAL_VARIANTS);
6410 HRESULT opentype_get_vertical_glyph_variants(struct dwrite_fontface *fontface, unsigned int glyph_count,
6411 const UINT16 *nominal_glyphs, UINT16 *glyphs)
6413 struct shaping_features features = { 0 };
6414 struct shaping_feature vert_feature = { 0 };
6415 struct scriptshaping_context context = { 0 };
6416 struct lookups lookups = { 0 };
6417 unsigned int i;
6419 memcpy(glyphs, nominal_glyphs, glyph_count * sizeof(*glyphs));
6421 if (!opentype_has_vertical_variants(fontface))
6422 return S_OK;
6424 context.cache = fontface_get_shaping_cache(fontface);
6425 context.u.subst.glyphs = glyphs;
6426 context.u.subst.glyph_props = heap_calloc(glyph_count, sizeof(*context.u.subst.glyph_props));
6427 context.u.subst.max_glyph_count = glyph_count;
6428 context.u.subst.capacity = glyph_count;
6429 context.glyph_infos = heap_alloc_zero(sizeof(*context.glyph_infos) * glyph_count);
6430 context.table = &context.cache->gsub;
6432 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6433 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6434 vert_feature.max_value = 1;
6435 vert_feature.default_value = 1;
6437 features.features = &vert_feature;
6438 features.count = features.capacity = 1;
6440 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6441 opentype_layout_set_glyph_masks(&context, &features);
6443 for (i = 0; i < lookups.count; ++i)
6445 const struct lookup *lookup = &lookups.lookups[i];
6447 context.cur = 0;
6448 while (context.cur < context.glyph_count)
6450 BOOL ret = FALSE;
6452 if (lookup_is_glyph_match(&context, context.cur, lookup->flags))
6453 ret = opentype_layout_apply_gsub_lookup(&context, lookup);
6455 if (!ret)
6456 context.cur++;
6460 heap_free(context.u.subst.glyph_props);
6461 heap_free(context.glyph_infos);
6462 heap_free(lookups.lookups);
6464 return S_OK;
6467 BOOL opentype_has_kerning_pairs(struct dwrite_fontface *fontface)
6469 const struct kern_subtable_header *subtable;
6470 struct file_stream_desc stream_desc;
6471 const struct kern_header *header;
6472 unsigned int offset, count, i;
6474 if (fontface->flags & (FONTFACE_KERNING_PAIRS | FONTFACE_NO_KERNING_PAIRS))
6475 return !!(fontface->flags & FONTFACE_KERNING_PAIRS);
6477 fontface->flags |= FONTFACE_NO_KERNING_PAIRS;
6479 stream_desc.stream = fontface->stream;
6480 stream_desc.face_type = fontface->type;
6481 stream_desc.face_index = fontface->index;
6483 opentype_get_font_table(&stream_desc, MS_KERN_TAG, &fontface->kern);
6484 if (fontface->kern.exists)
6486 if ((header = table_read_ensure(&fontface->kern, 0, sizeof(*header))))
6488 count = GET_BE_WORD(header->table_count);
6489 offset = sizeof(*header);
6491 /* FreeType limits table count this way. */
6492 count = min(count, 32);
6494 /* Check for presence of format 0 subtable with horizontal coverage. */
6495 for (i = 0; i < count; ++i)
6497 if (!(subtable = table_read_ensure(&fontface->kern, offset, sizeof(*subtable))))
6498 break;
6500 if (subtable->version == 0 && GET_BE_WORD(subtable->coverage) & 1)
6502 fontface->flags &= ~FONTFACE_NO_KERNING_PAIRS;
6503 fontface->flags |= FONTFACE_KERNING_PAIRS;
6504 break;
6507 offset += GET_BE_WORD(subtable->length);
6512 if (fontface->flags & FONTFACE_NO_KERNING_PAIRS && fontface->kern.data)
6513 IDWriteFontFileStream_ReleaseFileFragment(stream_desc.stream, fontface->kern.context);
6515 return !!(fontface->flags & FONTFACE_KERNING_PAIRS);
6518 struct kern_format0_compare_key
6520 UINT16 left;
6521 UINT16 right;
6524 static int __cdecl kern_format0_compare(const void *a, const void *b)
6526 const struct kern_format0_compare_key *key = a;
6527 const WORD *data = b;
6528 UINT16 left = GET_BE_WORD(data[0]), right = GET_BE_WORD(data[1]);
6529 int ret;
6531 if ((ret = (int)key->left - (int)left)) return ret;
6532 if ((ret = (int)key->right - (int)right)) return ret;
6533 return 0;
6536 HRESULT opentype_get_kerning_pairs(struct dwrite_fontface *fontface, unsigned int count,
6537 const UINT16 *glyphs, INT32 *values)
6539 const struct kern_subtable_header *subtable;
6540 unsigned int i, s, offset, pair_count, subtable_count;
6541 struct kern_format0_compare_key key;
6542 const struct kern_header *header;
6543 const WORD *data;
6545 if (!opentype_has_kerning_pairs(fontface))
6547 memset(values, 0, count * sizeof(*values));
6548 return S_OK;
6551 subtable_count = table_read_be_word(&fontface->kern, 2);
6552 subtable_count = min(subtable_count, 32);
6554 for (i = 0; i < count - 1; ++i)
6556 offset = sizeof(*header);
6558 key.left = glyphs[i];
6559 key.right = glyphs[i + 1];
6560 values[i] = 0;
6562 for (s = 0; s < subtable_count; ++s)
6564 if (!(subtable = table_read_ensure(&fontface->kern, offset, sizeof(*subtable))))
6565 break;
6567 if (subtable->version == 0 && GET_BE_WORD(subtable->coverage) & 1)
6569 if ((data = table_read_ensure(&fontface->kern, offset, GET_BE_WORD(subtable->length))))
6571 /* Skip subtable header */
6572 data += 3;
6573 pair_count = GET_BE_WORD(*data);
6574 data += 4;
6575 /* Move to pair data */
6576 if ((data = table_read_ensure(&fontface->kern, offset + 7 * sizeof(*data),
6577 pair_count * 3 * sizeof(*data))))
6579 if ((data = bsearch(&key, data, pair_count, 3 * sizeof(*data), kern_format0_compare)))
6581 values[i] = (short)GET_BE_WORD(data[2]);
6582 break;
6588 offset += GET_BE_WORD(subtable->length);
6591 values[count - 1] = 0;
6593 return S_OK;