d3d11: Report D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW in CheckFormatSupport().
[wine.git] / dlls / dwrite / opentype.c
blob53925465a12e88c9a08aa8473cf8421b3bb6c82d
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 "config.h"
25 #include "dwrite_private.h"
26 #include "winternl.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
30 #define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d')
31 #define MS_HHEA_TAG DWRITE_MAKE_OPENTYPE_TAG('h','h','e','a')
32 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
33 #define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2')
34 #define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t')
35 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
36 #define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
37 #define MS_NAME_TAG DWRITE_MAKE_OPENTYPE_TAG('n','a','m','e')
38 #define MS_GLYF_TAG DWRITE_MAKE_OPENTYPE_TAG('g','l','y','f')
39 #define MS_CFF__TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F',' ')
40 #define MS_CFF2_TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F','2')
41 #define MS_CPAL_TAG DWRITE_MAKE_OPENTYPE_TAG('C','P','A','L')
42 #define MS_COLR_TAG DWRITE_MAKE_OPENTYPE_TAG('C','O','L','R')
43 #define MS_SVG__TAG DWRITE_MAKE_OPENTYPE_TAG('S','V','G',' ')
44 #define MS_SBIX_TAG DWRITE_MAKE_OPENTYPE_TAG('s','b','i','x')
45 #define MS_MAXP_TAG DWRITE_MAKE_OPENTYPE_TAG('m','a','x','p')
46 #define MS_CBLC_TAG DWRITE_MAKE_OPENTYPE_TAG('C','B','L','C')
47 #define MS_CMAP_TAG DWRITE_MAKE_OPENTYPE_TAG('c','m','a','p')
48 #define MS_META_TAG DWRITE_MAKE_OPENTYPE_TAG('m','e','t','a')
50 /* 'sbix' formats */
51 #define MS_PNG__TAG DWRITE_MAKE_OPENTYPE_TAG('p','n','g',' ')
52 #define MS_JPG__TAG DWRITE_MAKE_OPENTYPE_TAG('j','p','g',' ')
53 #define MS_TIFF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','i','f','f')
55 #define MS_WOFF_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','F')
56 #define MS_WOF2_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','2')
58 /* 'meta' tags */
59 #define MS_DLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('d','l','n','g')
60 #define MS_SLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('s','l','n','g')
62 #ifdef WORDS_BIGENDIAN
63 #define GET_BE_WORD(x) (x)
64 #define GET_BE_DWORD(x) (x)
65 #else
66 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
67 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
68 #endif
70 #define GLYPH_CONTEXT_MAX_LENGTH 64
71 #define SHAPE_MAX_NESTING_LEVEL 6
73 typedef struct {
74 CHAR TTCTag[4];
75 DWORD Version;
76 DWORD numFonts;
77 DWORD OffsetTable[1];
78 } TTC_Header_V1;
80 typedef struct {
81 DWORD version;
82 WORD numTables;
83 WORD searchRange;
84 WORD entrySelector;
85 WORD rangeShift;
86 } TTC_SFNT_V1;
88 typedef struct {
89 DWORD tag;
90 DWORD checkSum;
91 DWORD offset;
92 DWORD length;
93 } TT_TableRecord;
95 struct cmap_encoding_record
97 WORD platformID;
98 WORD encodingID;
99 DWORD offset;
102 struct cmap_header
104 WORD version;
105 WORD num_tables;
106 struct cmap_encoding_record tables[1];
109 enum OPENTYPE_CMAP_TABLE_FORMAT
111 OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING = 4,
112 OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE = 12
115 enum opentype_cmap_table_platform
117 OPENTYPE_CMAP_TABLE_PLATFORM_WIN = 3,
120 enum opentype_cmap_table_encoding
122 OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL = 0,
123 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_BMP = 1,
124 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_FULL = 10,
127 /* PANOSE is 10 bytes in size, need to pack the structure properly */
128 #include "pshpack2.h"
129 typedef struct
131 USHORT majorVersion;
132 USHORT minorVersion;
133 ULONG revision;
134 ULONG checksumadj;
135 ULONG magic;
136 USHORT flags;
137 USHORT unitsPerEm;
138 ULONGLONG created;
139 ULONGLONG modified;
140 SHORT xMin;
141 SHORT yMin;
142 SHORT xMax;
143 SHORT yMax;
144 USHORT macStyle;
145 USHORT lowestRecPPEM;
146 SHORT direction_hint;
147 SHORT index_format;
148 SHORT glyphdata_format;
149 } TT_HEAD;
151 enum TT_HEAD_MACSTYLE
153 TT_HEAD_MACSTYLE_BOLD = 1 << 0,
154 TT_HEAD_MACSTYLE_ITALIC = 1 << 1,
155 TT_HEAD_MACSTYLE_UNDERLINE = 1 << 2,
156 TT_HEAD_MACSTYLE_OUTLINE = 1 << 3,
157 TT_HEAD_MACSTYLE_SHADOW = 1 << 4,
158 TT_HEAD_MACSTYLE_CONDENSED = 1 << 5,
159 TT_HEAD_MACSTYLE_EXTENDED = 1 << 6,
162 typedef struct
164 ULONG Version;
165 ULONG italicAngle;
166 SHORT underlinePosition;
167 SHORT underlineThickness;
168 ULONG fixed_pitch;
169 ULONG minmemType42;
170 ULONG maxmemType42;
171 ULONG minmemType1;
172 ULONG maxmemType1;
173 } TT_POST;
175 typedef struct
177 USHORT version;
178 SHORT xAvgCharWidth;
179 USHORT usWeightClass;
180 USHORT usWidthClass;
181 SHORT fsType;
182 SHORT ySubscriptXSize;
183 SHORT ySubscriptYSize;
184 SHORT ySubscriptXOffset;
185 SHORT ySubscriptYOffset;
186 SHORT ySuperscriptXSize;
187 SHORT ySuperscriptYSize;
188 SHORT ySuperscriptXOffset;
189 SHORT ySuperscriptYOffset;
190 SHORT yStrikeoutSize;
191 SHORT yStrikeoutPosition;
192 SHORT sFamilyClass;
193 PANOSE panose;
194 ULONG ulUnicodeRange1;
195 ULONG ulUnicodeRange2;
196 ULONG ulUnicodeRange3;
197 ULONG ulUnicodeRange4;
198 CHAR achVendID[4];
199 USHORT fsSelection;
200 USHORT usFirstCharIndex;
201 USHORT usLastCharIndex;
202 /* According to the Apple spec, original version didn't have the below fields,
203 * version numbers were taken from the OpenType spec.
205 /* version 0 (TrueType 1.5) */
206 USHORT sTypoAscender;
207 USHORT sTypoDescender;
208 USHORT sTypoLineGap;
209 USHORT usWinAscent;
210 USHORT usWinDescent;
211 /* version 1 (TrueType 1.66) */
212 ULONG ulCodePageRange1;
213 ULONG ulCodePageRange2;
214 /* version 2 (OpenType 1.2) */
215 SHORT sxHeight;
216 SHORT sCapHeight;
217 USHORT usDefaultChar;
218 USHORT usBreakChar;
219 USHORT usMaxContext;
220 } TT_OS2_V2;
222 typedef struct {
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;
238 } TT_HHEA;
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 typedef struct {
323 WORD platformID;
324 WORD encodingID;
325 WORD languageID;
326 WORD nameID;
327 WORD length;
328 WORD offset;
329 } TT_NameRecord;
331 typedef struct {
332 WORD format;
333 WORD count;
334 WORD stringOffset;
335 TT_NameRecord nameRecord[1];
336 } TT_NAME_V0;
338 struct vdmx_header
340 WORD version;
341 WORD num_recs;
342 WORD num_ratios;
345 struct vdmx_ratio
347 BYTE bCharSet;
348 BYTE xRatio;
349 BYTE yStartRatio;
350 BYTE yEndRatio;
353 struct vdmx_vtable
355 WORD yPelHeight;
356 SHORT yMax;
357 SHORT yMin;
360 struct vdmx_group
362 WORD recs;
363 BYTE startsz;
364 BYTE endsz;
365 struct vdmx_vtable entries[1];
368 struct ot_feature_record
370 DWORD tag;
371 WORD offset;
374 struct ot_feature_list
376 WORD feature_count;
377 struct ot_feature_record features[1];
380 struct ot_langsys
382 WORD lookup_order; /* Reserved */
383 WORD required_feature_index;
384 WORD feature_count;
385 WORD feature_index[1];
388 struct ot_langsys_record
390 CHAR tag[4];
391 WORD langsys;
394 struct ot_script
396 WORD default_langsys;
397 WORD langsys_count;
398 struct ot_langsys_record langsys[1];
399 } OT_Script;
401 struct ot_script_record
403 CHAR tag[4];
404 WORD script;
407 struct ot_script_list
409 WORD script_count;
410 struct ot_script_record scripts[1];
413 enum ot_gdef_class
415 GDEF_CLASS_UNCLASSIFIED = 0,
416 GDEF_CLASS_BASE = 1,
417 GDEF_CLASS_LIGATURE = 2,
418 GDEF_CLASS_MARK = 3,
419 GDEF_CLASS_COMPONENT = 4,
420 GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
423 struct gdef_header
425 DWORD version;
426 UINT16 classdef;
427 UINT16 attach_list;
428 UINT16 ligcaret_list;
429 UINT16 markattach_classdef;
430 UINT16 markglyphsetdef;
433 struct ot_gdef_classdef_format1
435 WORD format;
436 WORD start_glyph;
437 WORD glyph_count;
438 WORD classes[1];
441 struct ot_gdef_class_range
443 WORD start_glyph;
444 WORD end_glyph;
445 WORD glyph_class;
448 struct ot_gdef_classdef_format2
450 WORD format;
451 WORD range_count;
452 struct ot_gdef_class_range ranges[1];
455 struct gpos_gsub_header
457 DWORD version;
458 WORD script_list;
459 WORD feature_list;
460 WORD lookup_list;
463 enum gsub_gpos_lookup_flags
465 LOOKUP_FLAG_RTL = 0x1, /* Only used for GPOS cursive attachments. */
467 LOOKUP_FLAG_IGNORE_BASE = 0x2,
468 LOOKUP_FLAG_IGNORE_LIGATURES = 0x4,
469 LOOKUP_FLAG_IGNORE_MARKS = 0x8,
470 LOOKUP_FLAG_IGNORE_MASK = 0xe,
472 LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x10,
473 LOOKUP_FLAG_MARK_ATTACHMENT_TYPE = 0xff00,
476 enum glyph_prop_flags
478 GLYPH_PROP_BASE = LOOKUP_FLAG_IGNORE_BASE,
479 GLYPH_PROP_LIGATURE = LOOKUP_FLAG_IGNORE_LIGATURES,
480 GLYPH_PROP_MARK = LOOKUP_FLAG_IGNORE_MARKS,
483 enum gpos_lookup_type
485 GPOS_LOOKUP_SINGLE_ADJUSTMENT = 1,
486 GPOS_LOOKUP_PAIR_ADJUSTMENT = 2,
487 GPOS_LOOKUP_CURSIVE_ATTACHMENT = 3,
488 GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT = 4,
489 GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT = 5,
490 GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT = 6,
491 GPOS_LOOKUP_CONTEXTUAL_POSITION = 7,
492 GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION = 8,
493 GPOS_LOOKUP_EXTENSION_POSITION = 9,
496 enum gsub_lookup_type
498 GSUB_LOOKUP_SINGLE_SUBST = 1,
499 GSUB_LOOKUP_MULTIPLE_SUBST = 2,
500 GSUB_LOOKUP_ALTERNATE_SUBST = 3,
501 GSUB_LOOKUP_LIGATURE_SUBST = 4,
502 GSUB_LOOKUP_CONTEXTUAL_SUBST = 5,
503 GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST = 6,
504 GSUB_LOOKUP_EXTENSION_SUBST = 7,
505 GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST = 8,
508 enum gpos_value_format
510 GPOS_VALUE_X_PLACEMENT = 0x1,
511 GPOS_VALUE_Y_PLACEMENT = 0x2,
512 GPOS_VALUE_X_ADVANCE = 0x4,
513 GPOS_VALUE_Y_ADVANCE = 0x8,
514 GPOS_VALUE_X_PLACEMENT_DEVICE = 0x10,
515 GPOS_VALUE_Y_PLACEMENT_DEVICE = 0x20,
516 GPOS_VALUE_X_ADVANCE_DEVICE = 0x40,
517 GPOS_VALUE_Y_ADVANCE_DEVICE = 0x80,
520 enum OPENTYPE_PLATFORM_ID
522 OPENTYPE_PLATFORM_UNICODE = 0,
523 OPENTYPE_PLATFORM_MAC,
524 OPENTYPE_PLATFORM_ISO,
525 OPENTYPE_PLATFORM_WIN,
526 OPENTYPE_PLATFORM_CUSTOM
529 struct ot_gsubgpos_extension_format1
531 UINT16 format;
532 UINT16 lookup_type;
533 DWORD extension_offset;
536 struct ot_gsub_singlesubst_format1
538 UINT16 format;
539 UINT16 coverage;
540 short delta;
543 struct ot_gsub_singlesubst_format2
545 UINT16 format;
546 UINT16 coverage;
547 UINT16 count;
548 UINT16 substitutes[1];
551 struct ot_gsub_multsubst_format1
553 UINT16 format;
554 UINT16 coverage;
555 UINT16 seq_count;
556 UINT16 seq[1];
559 struct ot_gsub_altsubst_format1
561 UINT16 format;
562 UINT16 coverage;
563 UINT16 count;
564 UINT16 sets[1];
567 struct ot_gsub_ligsubst_format1
569 UINT16 format;
570 UINT16 coverage;
571 UINT16 lig_set_count;
572 UINT16 lig_sets[1];
575 struct ot_gsub_ligset
577 UINT16 count;
578 UINT16 offsets[1];
581 struct ot_gsub_lig
583 UINT16 lig_glyph;
584 UINT16 comp_count;
585 UINT16 components[1];
588 struct ot_gsubgpos_context_format1
590 UINT16 format;
591 UINT16 coverage;
592 UINT16 ruleset_count;
593 UINT16 rulesets[1];
596 struct ot_gsubgpos_ruleset
598 UINT16 count;
599 UINT16 offsets[1];
602 struct ot_feature
604 WORD feature_params;
605 WORD lookup_count;
606 WORD lookuplist_index[1];
609 struct ot_lookup_list
611 WORD lookup_count;
612 WORD lookup[1];
615 struct ot_lookup_table
617 WORD lookup_type;
618 WORD flags;
619 WORD subtable_count;
620 WORD subtable[1];
623 #define GLYPH_NOT_COVERED (~0u)
625 struct ot_coverage_format1
627 WORD format;
628 WORD glyph_count;
629 WORD glyphs[1];
632 struct ot_coverage_range
634 WORD start_glyph;
635 WORD end_glyph;
636 WORD startcoverage_index;
639 struct ot_coverage_format2
641 WORD format;
642 WORD range_count;
643 struct ot_coverage_range ranges[1];
646 struct ot_gpos_device_table
648 WORD start_size;
649 WORD end_size;
650 WORD format;
651 WORD values[1];
654 struct ot_gpos_singlepos_format1
656 WORD format;
657 WORD coverage;
658 WORD value_format;
659 WORD value[1];
662 struct ot_gpos_singlepos_format2
664 WORD format;
665 WORD coverage;
666 WORD value_format;
667 WORD value_count;
668 WORD values[1];
671 struct ot_gpos_pairvalue
673 WORD second_glyph;
674 BYTE data[1];
677 struct ot_gpos_pairset
679 WORD pairvalue_count;
680 struct ot_gpos_pairvalue pairvalues[1];
683 struct ot_gpos_pairpos_format1
685 WORD format;
686 WORD coverage;
687 WORD value_format1;
688 WORD value_format2;
689 WORD pairset_count;
690 WORD pairsets[1];
693 struct ot_gpos_pairpos_format2
695 WORD format;
696 WORD coverage;
697 WORD value_format1;
698 WORD value_format2;
699 WORD class_def1;
700 WORD class_def2;
701 WORD class1_count;
702 WORD class2_count;
703 WORD values[1];
706 struct ot_gpos_anchor_format1
708 WORD format;
709 short x_coord;
710 short y_coord;
713 struct ot_gpos_anchor_format2
715 WORD format;
716 short x_coord;
717 short y_coord;
718 WORD anchor_point;
721 struct ot_gpos_anchor_format3
723 WORD format;
724 short x_coord;
725 short y_coord;
726 WORD x_dev_offset;
727 WORD y_dev_offset;
730 struct ot_gpos_cursive_format1
732 WORD format;
733 WORD coverage;
734 WORD count;
735 WORD anchors[1];
738 struct ot_gpos_mark_record
740 WORD mark_class;
741 WORD mark_anchor;
744 struct ot_gpos_mark_array
746 WORD count;
747 struct ot_gpos_mark_record records[1];
750 struct ot_gpos_base_array
752 WORD count;
753 WORD offsets[1];
756 struct ot_gpos_mark_to_base_format1
758 WORD format;
759 WORD mark_coverage;
760 WORD base_coverage;
761 WORD mark_class_count;
762 WORD mark_array;
763 WORD base_array;
766 struct ot_gpos_mark_to_lig_format1
768 WORD format;
769 WORD mark_coverage;
770 WORD lig_coverage;
771 WORD mark_class_count;
772 WORD mark_array;
773 WORD lig_array;
776 struct ot_gpos_mark_to_mark_format1
778 WORD format;
779 WORD mark1_coverage;
780 WORD mark2_coverage;
781 WORD mark_class_count;
782 WORD mark1_array;
783 WORD mark2_array;
786 typedef struct {
787 WORD SubstFormat;
788 WORD Coverage;
789 WORD DeltaGlyphID;
790 } GSUB_SingleSubstFormat1;
792 typedef struct {
793 WORD SubstFormat;
794 WORD Coverage;
795 WORD GlyphCount;
796 WORD Substitute[1];
797 } GSUB_SingleSubstFormat2;
799 typedef struct {
800 WORD SubstFormat;
801 WORD ExtensionLookupType;
802 DWORD ExtensionOffset;
803 } GSUB_ExtensionPosFormat1;
805 #include "poppack.h"
807 enum TT_NAME_WINDOWS_ENCODING_ID
809 TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
810 TT_NAME_WINDOWS_ENCODING_UNICODE_BMP,
811 TT_NAME_WINDOWS_ENCODING_SJIS,
812 TT_NAME_WINDOWS_ENCODING_PRC,
813 TT_NAME_WINDOWS_ENCODING_BIG5,
814 TT_NAME_WINDOWS_ENCODING_WANSUNG,
815 TT_NAME_WINDOWS_ENCODING_JOHAB,
816 TT_NAME_WINDOWS_ENCODING_RESERVED1,
817 TT_NAME_WINDOWS_ENCODING_RESERVED2,
818 TT_NAME_WINDOWS_ENCODING_RESERVED3,
819 TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
822 enum TT_NAME_MAC_ENCODING_ID
824 TT_NAME_MAC_ENCODING_ROMAN = 0,
825 TT_NAME_MAC_ENCODING_JAPANESE,
826 TT_NAME_MAC_ENCODING_TRAD_CHINESE,
827 TT_NAME_MAC_ENCODING_KOREAN,
828 TT_NAME_MAC_ENCODING_ARABIC,
829 TT_NAME_MAC_ENCODING_HEBREW,
830 TT_NAME_MAC_ENCODING_GREEK,
831 TT_NAME_MAC_ENCODING_RUSSIAN,
832 TT_NAME_MAC_ENCODING_RSYMBOL,
833 TT_NAME_MAC_ENCODING_DEVANAGARI,
834 TT_NAME_MAC_ENCODING_GURMUKHI,
835 TT_NAME_MAC_ENCODING_GUJARATI,
836 TT_NAME_MAC_ENCODING_ORIYA,
837 TT_NAME_MAC_ENCODING_BENGALI,
838 TT_NAME_MAC_ENCODING_TAMIL,
839 TT_NAME_MAC_ENCODING_TELUGU,
840 TT_NAME_MAC_ENCODING_KANNADA,
841 TT_NAME_MAC_ENCODING_MALAYALAM,
842 TT_NAME_MAC_ENCODING_SINHALESE,
843 TT_NAME_MAC_ENCODING_BURMESE,
844 TT_NAME_MAC_ENCODING_KHMER,
845 TT_NAME_MAC_ENCODING_THAI,
846 TT_NAME_MAC_ENCODING_LAOTIAN,
847 TT_NAME_MAC_ENCODING_GEORGIAN,
848 TT_NAME_MAC_ENCODING_ARMENIAN,
849 TT_NAME_MAC_ENCODING_SIMPL_CHINESE,
850 TT_NAME_MAC_ENCODING_TIBETAN,
851 TT_NAME_MAC_ENCODING_MONGOLIAN,
852 TT_NAME_MAC_ENCODING_GEEZ,
853 TT_NAME_MAC_ENCODING_SLAVIC,
854 TT_NAME_MAC_ENCODING_VIETNAMESE,
855 TT_NAME_MAC_ENCODING_SINDHI,
856 TT_NAME_MAC_ENCODING_UNINTERPRETED
859 enum TT_NAME_MAC_LANGUAGE_ID
861 TT_NAME_MAC_LANGID_ENGLISH = 0,
862 TT_NAME_MAC_LANGID_FRENCH,
863 TT_NAME_MAC_LANGID_GERMAN,
864 TT_NAME_MAC_LANGID_ITALIAN,
865 TT_NAME_MAC_LANGID_DUTCH,
866 TT_NAME_MAC_LANGID_SWEDISH,
867 TT_NAME_MAC_LANGID_SPANISH,
868 TT_NAME_MAC_LANGID_DANISH,
869 TT_NAME_MAC_LANGID_PORTUGUESE,
870 TT_NAME_MAC_LANGID_NORWEGIAN,
871 TT_NAME_MAC_LANGID_HEBREW,
872 TT_NAME_MAC_LANGID_JAPANESE,
873 TT_NAME_MAC_LANGID_ARABIC,
874 TT_NAME_MAC_LANGID_FINNISH,
875 TT_NAME_MAC_LANGID_GREEK,
876 TT_NAME_MAC_LANGID_ICELANDIC,
877 TT_NAME_MAC_LANGID_MALTESE,
878 TT_NAME_MAC_LANGID_TURKISH,
879 TT_NAME_MAC_LANGID_CROATIAN,
880 TT_NAME_MAC_LANGID_TRAD_CHINESE,
881 TT_NAME_MAC_LANGID_URDU,
882 TT_NAME_MAC_LANGID_HINDI,
883 TT_NAME_MAC_LANGID_THAI,
884 TT_NAME_MAC_LANGID_KOREAN,
885 TT_NAME_MAC_LANGID_LITHUANIAN,
886 TT_NAME_MAC_LANGID_POLISH,
887 TT_NAME_MAC_LANGID_HUNGARIAN,
888 TT_NAME_MAC_LANGID_ESTONIAN,
889 TT_NAME_MAC_LANGID_LATVIAN,
890 TT_NAME_MAC_LANGID_SAMI,
891 TT_NAME_MAC_LANGID_FAROESE,
892 TT_NAME_MAC_LANGID_FARSI,
893 TT_NAME_MAC_LANGID_RUSSIAN,
894 TT_NAME_MAC_LANGID_SIMPL_CHINESE,
895 TT_NAME_MAC_LANGID_FLEMISH,
896 TT_NAME_MAC_LANGID_GAELIC,
897 TT_NAME_MAC_LANGID_ALBANIAN,
898 TT_NAME_MAC_LANGID_ROMANIAN,
899 TT_NAME_MAC_LANGID_CZECH,
900 TT_NAME_MAC_LANGID_SLOVAK,
901 TT_NAME_MAC_LANGID_SLOVENIAN,
902 TT_NAME_MAC_LANGID_YIDDISH,
903 TT_NAME_MAC_LANGID_SERBIAN,
904 TT_NAME_MAC_LANGID_MACEDONIAN,
905 TT_NAME_MAC_LANGID_BULGARIAN,
906 TT_NAME_MAC_LANGID_UKRAINIAN,
907 TT_NAME_MAC_LANGID_BYELORUSSIAN,
908 TT_NAME_MAC_LANGID_UZBEK,
909 TT_NAME_MAC_LANGID_KAZAKH,
910 TT_NAME_MAC_LANGID_AZERB_CYR,
911 TT_NAME_MAC_LANGID_AZERB_ARABIC,
912 TT_NAME_MAC_LANGID_ARMENIAN,
913 TT_NAME_MAC_LANGID_GEORGIAN,
914 TT_NAME_MAC_LANGID_MOLDAVIAN,
915 TT_NAME_MAC_LANGID_KIRGHIZ,
916 TT_NAME_MAC_LANGID_TAJIKI,
917 TT_NAME_MAC_LANGID_TURKMEN,
918 TT_NAME_MAC_LANGID_MONGOLIAN,
919 TT_NAME_MAC_LANGID_MONGOLIAN_CYR,
920 TT_NAME_MAC_LANGID_PASHTO,
921 TT_NAME_MAC_LANGID_KURDISH,
922 TT_NAME_MAC_LANGID_KASHMIRI,
923 TT_NAME_MAC_LANGID_SINDHI,
924 TT_NAME_MAC_LANGID_TIBETAN,
925 TT_NAME_MAC_LANGID_NEPALI,
926 TT_NAME_MAC_LANGID_SANSKRIT,
927 TT_NAME_MAC_LANGID_MARATHI,
928 TT_NAME_MAC_LANGID_BENGALI,
929 TT_NAME_MAC_LANGID_ASSAMESE,
930 TT_NAME_MAC_LANGID_GUJARATI,
931 TT_NAME_MAC_LANGID_PUNJABI,
932 TT_NAME_MAC_LANGID_ORIYA,
933 TT_NAME_MAC_LANGID_MALAYALAM,
934 TT_NAME_MAC_LANGID_KANNADA,
935 TT_NAME_MAC_LANGID_TAMIL,
936 TT_NAME_MAC_LANGID_TELUGU,
937 TT_NAME_MAC_LANGID_SINHALESE,
938 TT_NAME_MAC_LANGID_BURMESE,
939 TT_NAME_MAC_LANGID_KHMER,
940 TT_NAME_MAC_LANGID_LAO,
941 TT_NAME_MAC_LANGID_VIETNAMESE,
942 TT_NAME_MAC_LANGID_INDONESIAN,
943 TT_NAME_MAC_LANGID_TAGALOG,
944 TT_NAME_MAC_LANGID_MALAY_ROMAN,
945 TT_NAME_MAC_LANGID_MALAY_ARABIC,
946 TT_NAME_MAC_LANGID_AMHARIC,
947 TT_NAME_MAC_LANGID_TIGRINYA,
948 TT_NAME_MAC_LANGID_GALLA,
949 TT_NAME_MAC_LANGID_SOMALI,
950 TT_NAME_MAC_LANGID_SWAHILI,
951 TT_NAME_MAC_LANGID_KINYARWANDA,
952 TT_NAME_MAC_LANGID_RUNDI,
953 TT_NAME_MAC_LANGID_NYANJA,
954 TT_NAME_MAC_LANGID_MALAGASY,
955 TT_NAME_MAC_LANGID_ESPERANTO,
956 TT_NAME_MAC_LANGID_WELSH = 128,
957 TT_NAME_MAC_LANGID_BASQUE,
958 TT_NAME_MAC_LANGID_CATALAN,
959 TT_NAME_MAC_LANGID_LATIN,
960 TT_NAME_MAC_LANGID_QUECHUA,
961 TT_NAME_MAC_LANGID_GUARANI,
962 TT_NAME_MAC_LANGID_AYMARA,
963 TT_NAME_MAC_LANGID_TATAR,
964 TT_NAME_MAC_LANGID_UIGHUR,
965 TT_NAME_MAC_LANGID_DZONGKHA,
966 TT_NAME_MAC_LANGID_JAVANESE,
967 TT_NAME_MAC_LANGID_SUNDANESE,
968 TT_NAME_MAC_LANGID_GALICIAN,
969 TT_NAME_MAC_LANGID_AFRIKAANS,
970 TT_NAME_MAC_LANGID_BRETON,
971 TT_NAME_MAC_LANGID_INUKTITUT,
972 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC,
973 TT_NAME_MAC_LANGID_MANX_GAELIC,
974 TT_NAME_MAC_LANGID_IRISH_GAELIC,
975 TT_NAME_MAC_LANGID_TONGAN,
976 TT_NAME_MAC_LANGID_GREEK_POLYTONIC,
977 TT_NAME_MAC_LANGID_GREENLANDIC,
978 TT_NAME_MAC_LANGID_AZER_ROMAN
981 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
982 static const char name_mac_langid_to_locale[][10] = {
983 "en-US",
984 "fr-FR",
985 "de-DE",
986 "it-IT",
987 "nl-NL",
988 "sv-SE",
989 "es-ES",
990 "da-DA",
991 "pt-PT",
992 "no-NO",
993 "he-IL",
994 "ja-JP",
995 "ar-AR",
996 "fi-FI",
997 "el-GR",
998 "is-IS",
999 "mt-MT",
1000 "tr-TR",
1001 "hr-HR",
1002 "zh-HK",
1003 "ur-PK",
1004 "hi-IN",
1005 "th-TH",
1006 "ko-KR",
1007 "lt-LT",
1008 "pl-PL",
1009 "hu-HU",
1010 "et-EE",
1011 "lv-LV",
1012 "se-NO",
1013 "fo-FO",
1014 "fa-IR",
1015 "ru-RU",
1016 "zh-CN",
1017 "nl-BE",
1018 "gd-GB",
1019 "sq-AL",
1020 "ro-RO",
1021 "cs-CZ",
1022 "sk-SK",
1023 "sl-SI",
1025 "sr-Latn",
1026 "mk-MK",
1027 "bg-BG",
1028 "uk-UA",
1029 "be-BY",
1030 "uz-Latn",
1031 "kk-KZ",
1032 "az-Cyrl-AZ",
1033 "az-AZ",
1034 "hy-AM",
1035 "ka-GE",
1038 "tg-TJ",
1039 "tk-TM",
1040 "mn-Mong",
1041 "mn-MN",
1042 "ps-AF",
1043 "ku-Arab",
1045 "sd-Arab",
1046 "bo-CN",
1047 "ne-NP",
1048 "sa-IN",
1049 "mr-IN",
1050 "bn-IN",
1051 "as-IN",
1052 "gu-IN",
1053 "pa-Arab",
1054 "or-IN",
1055 "ml-IN",
1056 "kn-IN",
1057 "ta-LK",
1058 "te-IN",
1059 "si-LK",
1061 "km-KH",
1062 "lo-LA",
1063 "vi-VN",
1064 "id-ID",
1066 "ms-MY",
1067 "ms-Arab",
1068 "am-ET",
1069 "ti-ET",
1072 "sw-KE",
1073 "rw-RW",
1111 "cy-GB",
1112 "eu-ES",
1113 "ca-ES",
1118 "tt-RU",
1119 "ug-CN",
1123 "gl-ES",
1124 "af-ZA",
1125 "br-FR",
1126 "iu-Latn-CA",
1127 "gd-GB",
1129 "ga-IE",
1132 "kl-GL",
1133 "az-Latn"
1136 enum OPENTYPE_STRING_ID
1138 OPENTYPE_STRING_COPYRIGHT_NOTICE = 0,
1139 OPENTYPE_STRING_FAMILY_NAME,
1140 OPENTYPE_STRING_SUBFAMILY_NAME,
1141 OPENTYPE_STRING_UNIQUE_IDENTIFIER,
1142 OPENTYPE_STRING_FULL_FONTNAME,
1143 OPENTYPE_STRING_VERSION_STRING,
1144 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1145 OPENTYPE_STRING_TRADEMARK,
1146 OPENTYPE_STRING_MANUFACTURER,
1147 OPENTYPE_STRING_DESIGNER,
1148 OPENTYPE_STRING_DESCRIPTION,
1149 OPENTYPE_STRING_VENDOR_URL,
1150 OPENTYPE_STRING_DESIGNER_URL,
1151 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1152 OPENTYPE_STRING_LICENSE_INFO_URL,
1153 OPENTYPE_STRING_RESERVED_ID15,
1154 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1155 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1156 OPENTYPE_STRING_COMPATIBLE_FULLNAME,
1157 OPENTYPE_STRING_SAMPLE_TEXT,
1158 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1159 OPENTYPE_STRING_WWS_FAMILY_NAME,
1160 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
1163 static const UINT16 dwriteid_to_opentypeid[DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME + 1] =
1165 (UINT16)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
1166 OPENTYPE_STRING_COPYRIGHT_NOTICE,
1167 OPENTYPE_STRING_VERSION_STRING,
1168 OPENTYPE_STRING_TRADEMARK,
1169 OPENTYPE_STRING_MANUFACTURER,
1170 OPENTYPE_STRING_DESIGNER,
1171 OPENTYPE_STRING_DESIGNER_URL,
1172 OPENTYPE_STRING_DESCRIPTION,
1173 OPENTYPE_STRING_VENDOR_URL,
1174 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1175 OPENTYPE_STRING_LICENSE_INFO_URL,
1176 OPENTYPE_STRING_FAMILY_NAME,
1177 OPENTYPE_STRING_SUBFAMILY_NAME,
1178 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1179 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1180 OPENTYPE_STRING_SAMPLE_TEXT,
1181 OPENTYPE_STRING_FULL_FONTNAME,
1182 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1183 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1184 OPENTYPE_STRING_WWS_FAMILY_NAME,
1187 /* CPAL table */
1188 struct cpal_header_0
1190 USHORT version;
1191 USHORT num_palette_entries;
1192 USHORT num_palettes;
1193 USHORT num_color_records;
1194 ULONG offset_first_color_record;
1195 USHORT color_record_indices[1];
1198 struct cpal_color_record
1200 BYTE blue;
1201 BYTE green;
1202 BYTE red;
1203 BYTE alpha;
1206 /* COLR table */
1207 struct colr_header
1209 USHORT version;
1210 USHORT num_baseglyph_records;
1211 ULONG offset_baseglyph_records;
1212 ULONG offset_layer_records;
1213 USHORT num_layer_records;
1216 struct colr_baseglyph_record
1218 USHORT glyph;
1219 USHORT first_layer_index;
1220 USHORT num_layers;
1223 struct colr_layer_record
1225 USHORT glyph;
1226 USHORT palette_index;
1229 struct meta_data_map
1231 DWORD tag;
1232 DWORD offset;
1233 DWORD length;
1236 struct meta_header
1238 DWORD version;
1239 DWORD flags;
1240 DWORD reserved;
1241 DWORD data_maps_count;
1242 struct meta_data_map maps[1];
1245 static const void *table_read_ensure(const struct dwrite_fonttable *table, unsigned int offset, unsigned int size)
1247 if (size > table->size || offset > table->size - size)
1248 return NULL;
1250 return table->data + offset;
1253 static WORD table_read_be_word(const struct dwrite_fonttable *table, unsigned int offset)
1255 const WORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1256 return ptr ? GET_BE_WORD(*ptr) : 0;
1259 static DWORD table_read_be_dword(const struct dwrite_fonttable *table, unsigned int offset)
1261 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1262 return ptr ? GET_BE_DWORD(*ptr) : 0;
1265 static DWORD table_read_dword(const struct dwrite_fonttable *table, unsigned int offset)
1267 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1268 return ptr ? *ptr : 0;
1271 BOOL is_face_type_supported(DWRITE_FONT_FACE_TYPE type)
1273 return (type == DWRITE_FONT_FACE_TYPE_CFF) ||
1274 (type == DWRITE_FONT_FACE_TYPE_TRUETYPE) ||
1275 (type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) ||
1276 (type == DWRITE_FONT_FACE_TYPE_RAW_CFF);
1279 typedef HRESULT (*dwrite_fontfile_analyzer)(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1280 DWRITE_FONT_FACE_TYPE *face_type);
1282 static HRESULT opentype_ttc_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1283 DWRITE_FONT_FACE_TYPE *face_type)
1285 static const DWORD ttctag = MS_TTCF_TAG;
1286 const TTC_Header_V1 *header;
1287 void *context;
1288 HRESULT hr;
1290 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(header), &context);
1291 if (FAILED(hr))
1292 return hr;
1294 if (!memcmp(header->TTCTag, &ttctag, sizeof(ttctag))) {
1295 *font_count = GET_BE_DWORD(header->numFonts);
1296 *file_type = DWRITE_FONT_FILE_TYPE_OPENTYPE_COLLECTION;
1297 *face_type = DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION;
1300 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1302 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1305 static HRESULT opentype_ttf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1306 DWRITE_FONT_FACE_TYPE *face_type)
1308 const DWORD *header;
1309 void *context;
1310 HRESULT hr;
1312 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1313 if (FAILED(hr))
1314 return hr;
1316 if (GET_BE_DWORD(*header) == 0x10000) {
1317 *font_count = 1;
1318 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
1319 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
1322 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1324 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1327 static HRESULT opentype_otf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1328 DWRITE_FONT_FACE_TYPE *face_type)
1330 const DWORD *header;
1331 void *context;
1332 HRESULT hr;
1334 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1335 if (FAILED(hr))
1336 return hr;
1338 if (GET_BE_DWORD(*header) == MS_OTTO_TAG) {
1339 *font_count = 1;
1340 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
1341 *face_type = DWRITE_FONT_FACE_TYPE_CFF;
1344 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1346 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1349 static HRESULT opentype_type1_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1350 DWRITE_FONT_FACE_TYPE *face_type)
1352 #include "pshpack1.h"
1353 /* Specified in Adobe TechNote #5178 */
1354 struct pfm_header {
1355 WORD dfVersion;
1356 DWORD dfSize;
1357 char data0[95];
1358 DWORD dfDevice;
1359 char data1[12];
1361 #include "poppack.h"
1362 struct type1_header {
1363 WORD tag;
1364 char data[14];
1366 const struct type1_header *header;
1367 void *context;
1368 HRESULT hr;
1370 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1371 if (FAILED(hr))
1372 return hr;
1374 /* tag is followed by plain text section */
1375 if (header->tag == 0x8001 &&
1376 (!memcmp(header->data, "%!PS-AdobeFont", 14) ||
1377 !memcmp(header->data, "%!FontType", 10))) {
1378 *font_count = 1;
1379 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFB;
1380 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1383 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1385 /* let's see if it's a .pfm metrics file */
1386 if (*file_type == DWRITE_FONT_FILE_TYPE_UNKNOWN) {
1387 const struct pfm_header *pfm_header;
1388 UINT64 filesize;
1389 DWORD offset;
1390 BOOL header_checked;
1392 hr = IDWriteFontFileStream_GetFileSize(stream, &filesize);
1393 if (FAILED(hr))
1394 return hr;
1396 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&pfm_header, 0, sizeof(*pfm_header), &context);
1397 if (FAILED(hr))
1398 return hr;
1400 offset = pfm_header->dfDevice;
1401 header_checked = pfm_header->dfVersion == 0x100 && pfm_header->dfSize == filesize;
1402 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1404 /* as a last test check static string in PostScript information section */
1405 if (header_checked) {
1406 static const char postscript[] = "PostScript";
1407 char *devtype_name;
1409 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&devtype_name, offset, sizeof(postscript), &context);
1410 if (FAILED(hr))
1411 return hr;
1413 if (!memcmp(devtype_name, postscript, sizeof(postscript))) {
1414 *font_count = 1;
1415 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFM;
1416 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1419 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1423 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1426 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, BOOL *supported, DWRITE_FONT_FILE_TYPE *file_type,
1427 DWRITE_FONT_FACE_TYPE *face_type, UINT32 *face_count)
1429 static dwrite_fontfile_analyzer fontfile_analyzers[] = {
1430 opentype_ttf_analyzer,
1431 opentype_otf_analyzer,
1432 opentype_ttc_analyzer,
1433 opentype_type1_analyzer,
1434 NULL
1436 dwrite_fontfile_analyzer *analyzer = fontfile_analyzers;
1437 DWRITE_FONT_FACE_TYPE face;
1438 HRESULT hr;
1440 if (!face_type)
1441 face_type = &face;
1443 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
1444 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
1445 *face_count = 0;
1447 while (*analyzer) {
1448 hr = (*analyzer)(stream, face_count, file_type, face_type);
1449 if (FAILED(hr))
1450 return hr;
1452 if (hr == S_OK)
1453 break;
1455 analyzer++;
1458 *supported = is_face_type_supported(*face_type);
1459 return S_OK;
1462 HRESULT opentype_try_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag, const void **table_data,
1463 void **table_context, UINT32 *table_size, BOOL *found)
1465 void *table_directory_context, *sfnt_context;
1466 TT_TableRecord *table_record = NULL;
1467 TTC_SFNT_V1 *font_header = NULL;
1468 UINT32 table_offset = 0;
1469 UINT16 table_count;
1470 HRESULT hr;
1472 if (found) *found = FALSE;
1473 if (table_size) *table_size = 0;
1475 *table_data = NULL;
1476 *table_context = NULL;
1478 if (stream_desc->face_type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) {
1479 const TTC_Header_V1 *ttc_header;
1480 void * ttc_context;
1481 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
1482 if (SUCCEEDED(hr)) {
1483 if (stream_desc->face_index >= GET_BE_DWORD(ttc_header->numFonts))
1484 hr = E_INVALIDARG;
1485 else {
1486 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[stream_desc->face_index]);
1487 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
1489 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, ttc_context);
1492 else
1493 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
1495 if (FAILED(hr))
1496 return hr;
1498 table_count = GET_BE_WORD(font_header->numTables);
1499 table_offset += sizeof(*font_header);
1501 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, sfnt_context);
1503 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void **)&table_record, table_offset,
1504 table_count * sizeof(*table_record), &table_directory_context);
1505 if (hr == S_OK) {
1506 UINT16 i;
1508 for (i = 0; i < table_count; i++) {
1509 if (table_record->tag == tag) {
1510 UINT32 offset = GET_BE_DWORD(table_record->offset);
1511 UINT32 length = GET_BE_DWORD(table_record->length);
1513 if (found)
1514 *found = TRUE;
1515 if (table_size)
1516 *table_size = length;
1517 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, table_data, offset,
1518 length, table_context);
1519 break;
1521 table_record++;
1524 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, table_directory_context);
1527 return hr;
1530 static HRESULT opentype_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag,
1531 struct dwrite_fonttable *table)
1533 return opentype_try_get_font_table(stream_desc, tag, (const void **)&table->data, &table->context, &table->size, &table->exists);
1536 /**********
1537 * CMAP
1538 **********/
1540 static UINT16 opentype_cmap_format0_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1542 const UINT8 *glyphs = cmap->data;
1543 return (ch < 0xff) ? glyphs[ch] : 0;
1546 static unsigned int opentype_cmap_format0_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1547 DWRITE_UNICODE_RANGE *ranges)
1549 if (count > 0)
1551 ranges->first = 0;
1552 ranges->last = 255;
1555 return 1;
1558 struct cmap_format4_compare_context
1560 const struct dwrite_cmap *cmap;
1561 unsigned int ch;
1564 static int cmap_format4_compare_range(const void *a, const void *b)
1566 const struct cmap_format4_compare_context *key = a;
1567 const UINT16 *end = b;
1568 unsigned int idx;
1570 if (key->ch > GET_BE_WORD(*end))
1571 return 1;
1573 idx = end - key->cmap->u.format4.ends;
1574 if (key->ch < GET_BE_WORD(key->cmap->u.format4.starts[idx]))
1575 return -1;
1577 return 0;
1580 static UINT16 opentype_cmap_format4_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1582 struct cmap_format4_compare_context key = { .cmap = cmap, .ch = ch };
1583 unsigned int glyph, idx, range_offset;
1584 const UINT16 *end_found;
1586 /* Look up range. */
1587 end_found = bsearch(&key, cmap->u.format4.ends, cmap->u.format4.seg_count, sizeof(*cmap->u.format4.ends),
1588 cmap_format4_compare_range);
1589 if (!end_found)
1590 return 0;
1592 idx = end_found - cmap->u.format4.ends;
1594 range_offset = GET_BE_WORD(cmap->u.format4.id_range_offset[idx]);
1596 if (!range_offset)
1598 glyph = ch + GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1600 else
1602 unsigned int index = range_offset / 2 + (ch - GET_BE_WORD(cmap->u.format4.starts[idx])) + idx - cmap->u.format4.seg_count;
1603 if (index >= cmap->u.format4.glyph_id_array_len)
1604 return 0;
1605 glyph = GET_BE_WORD(cmap->u.format4.glyph_id_array[index]);
1606 if (!glyph)
1607 return 0;
1608 glyph += GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1611 return glyph & 0xffff;
1614 static unsigned int opentype_cmap_format4_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1615 DWRITE_UNICODE_RANGE *ranges)
1617 unsigned int i;
1619 count = min(count, cmap->u.format4.seg_count);
1621 for (i = 0; i < count; ++i)
1623 ranges[i].first = GET_BE_WORD(cmap->u.format4.starts[i]);
1624 ranges[i].last = GET_BE_WORD(cmap->u.format4.ends[i]);
1627 return cmap->u.format4.seg_count;
1630 static UINT16 opentype_cmap_format6_10_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1632 const UINT16 *glyphs = cmap->data;
1633 if (ch < cmap->u.format6_10.first || ch > cmap->u.format6_10.last) return 0;
1634 return glyphs[ch - cmap->u.format6_10.first];
1637 static unsigned int opentype_cmap_format6_10_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1638 DWRITE_UNICODE_RANGE *ranges)
1640 if (count > 0)
1642 ranges->first = cmap->u.format6_10.first;
1643 ranges->last = cmap->u.format6_10.last;
1646 return 1;
1649 static int cmap_format12_13_compare_group(const void *a, const void *b)
1651 const unsigned int *ch = a;
1652 const UINT32 *group = b;
1654 if (*ch > GET_BE_DWORD(group[1]))
1655 return 1;
1657 if (*ch < GET_BE_DWORD(group[0]))
1658 return -1;
1660 return 0;
1663 static UINT16 opentype_cmap_format12_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1665 const UINT32 *groups = cmap->data;
1666 const UINT32 *group_found;
1668 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1669 cmap_format12_13_compare_group)))
1670 return 0;
1672 return GET_BE_DWORD(group_found[0]) <= GET_BE_DWORD(group_found[1]) ?
1673 GET_BE_DWORD(group_found[2]) + (ch - GET_BE_DWORD(group_found[0])) : 0;
1676 static unsigned int opentype_cmap_format12_13_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1677 DWRITE_UNICODE_RANGE *ranges)
1679 unsigned int i, group_count = cmap->u.format12_13.group_count;
1680 const UINT32 *groups = cmap->data;
1682 count = min(count, group_count);
1684 for (i = 0; i < count; ++i)
1686 ranges[i].first = GET_BE_DWORD(groups[3 * i]);
1687 ranges[i].last = GET_BE_DWORD(groups[3 * i + 1]);
1690 return group_count;
1693 static UINT16 opentype_cmap_format13_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1695 const UINT32 *groups = cmap->data;
1696 const UINT32 *group_found;
1698 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1699 cmap_format12_13_compare_group)))
1700 return 0;
1702 return GET_BE_DWORD(group_found[2]);
1705 static UINT16 opentype_cmap_dummy_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1707 return 0;
1710 static unsigned int opentype_cmap_dummy_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1711 DWRITE_UNICODE_RANGE *ranges)
1713 return 0;
1716 UINT16 opentype_cmap_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1718 UINT16 glyph;
1720 if (!cmap->get_glyph) return 0;
1721 glyph = cmap->get_glyph(cmap, ch);
1722 if (!glyph && cmap->symbol && ch <= 0xff)
1723 glyph = cmap->get_glyph(cmap, ch + 0xf000);
1724 return glyph;
1727 static int cmap_header_compare(const void *a, const void *b)
1729 const UINT16 *key = a;
1730 const UINT16 *record = b;
1732 /* Platform. */
1733 if (key[0] < GET_BE_WORD(record[0])) return -1;
1734 if (key[0] > GET_BE_WORD(record[0])) return 1;
1735 /* Encoding. */
1736 if (key[1] < GET_BE_WORD(record[1])) return -1;
1737 if (key[1] > GET_BE_WORD(record[1])) return 1;
1739 return 0;
1742 void dwrite_cmap_init(struct dwrite_cmap *cmap, IDWriteFontFile *file, unsigned int face_index,
1743 DWRITE_FONT_FACE_TYPE face_type)
1745 static const UINT16 encodings[][2] =
1747 { 3, 0 }, /* MS Symbol encoding is preferred. */
1748 { 3, 10 },
1749 { 0, 6 },
1750 { 0, 4 },
1751 { 3, 1 },
1752 { 0, 3 },
1753 { 0, 2 },
1754 { 0, 1 },
1755 { 0, 0 },
1757 const struct cmap_encoding_record *records, *found_record = NULL;
1758 unsigned int length, offset, format, count, f, i, num_records;
1759 struct file_stream_desc stream_desc;
1760 struct dwrite_fonttable table;
1761 const UINT16 *pair = NULL;
1762 HRESULT hr;
1764 if (cmap->data) return;
1766 /* For fontface stream is already available and preset. */
1767 if (!cmap->stream && FAILED(hr = get_filestream_from_file(file, &cmap->stream)))
1769 WARN("Failed to get file stream, hr %#x.\n", hr);
1770 goto failed;
1773 stream_desc.stream = cmap->stream;
1774 stream_desc.face_type = face_type;
1775 stream_desc.face_index = face_index;
1777 opentype_get_font_table(&stream_desc, MS_CMAP_TAG, &table);
1778 if (!table.exists)
1779 goto failed;
1780 cmap->table_context = table.context;
1782 num_records = table_read_be_word(&table, 2);
1783 records = table_read_ensure(&table, 4, sizeof(*records) * num_records);
1785 for (i = 0; i < ARRAY_SIZE(encodings); ++i)
1787 pair = encodings[i];
1788 if ((found_record = bsearch(pair, records, num_records, sizeof(*records), cmap_header_compare)))
1789 break;
1792 if (!found_record)
1794 WARN("No suitable cmap table were found.\n");
1795 goto failed;
1798 /* Symbol encoding. */
1799 cmap->symbol = pair[0] == 3 && pair[1] == 0;
1800 offset = GET_BE_DWORD(found_record->offset);
1802 format = table_read_be_word(&table, offset);
1804 switch (format)
1806 case 0:
1807 cmap->data = table_read_ensure(&table, offset + 6, 256);
1808 cmap->get_glyph = opentype_cmap_format0_get_glyph;
1809 cmap->get_ranges = opentype_cmap_format0_get_ranges;
1810 break;
1811 case 4:
1812 length = table_read_be_word(&table, offset + 2);
1813 cmap->u.format4.seg_count = count = table_read_be_word(&table, offset + 6) / 2;
1814 cmap->u.format4.ends = table_read_ensure(&table, offset + 14, count * 2);
1815 cmap->u.format4.starts = cmap->u.format4.ends + count + 1;
1816 cmap->u.format4.id_delta = cmap->u.format4.starts + count;
1817 cmap->u.format4.id_range_offset = cmap->u.format4.id_delta + count;
1818 cmap->u.format4.glyph_id_array = cmap->data = cmap->u.format4.id_range_offset + count;
1819 cmap->u.format4.glyph_id_array_len = (length - 16 - 8 * count) / 2;
1820 cmap->get_glyph = opentype_cmap_format4_get_glyph;
1821 cmap->get_ranges = opentype_cmap_format4_get_ranges;
1822 break;
1823 case 6:
1824 case 10:
1825 /* Format 10 uses 4 byte fields. */
1826 f = format == 6 ? 1 : 2;
1827 cmap->u.format6_10.first = table_read_be_word(&table, offset + f * 6);
1828 count = table_read_be_word(&table, offset + f * 8);
1829 cmap->u.format6_10.last = cmap->u.format6_10.first + count;
1830 cmap->data = table_read_ensure(&table, offset + f * 10, count * 2);
1831 cmap->get_glyph = opentype_cmap_format6_10_get_glyph;
1832 cmap->get_ranges = opentype_cmap_format6_10_get_ranges;
1833 break;
1834 case 12:
1835 case 13:
1836 cmap->u.format12_13.group_count = count = table_read_be_dword(&table, offset + 12);
1837 cmap->data = table_read_ensure(&table, offset + 16, count * 3 * 4);
1838 cmap->get_glyph = format == 12 ? opentype_cmap_format12_get_glyph : opentype_cmap_format13_get_glyph;
1839 cmap->get_ranges = opentype_cmap_format12_13_get_ranges;
1840 break;
1841 default:
1842 WARN("Unhandled subtable format %u.\n", format);
1845 failed:
1847 if (!cmap->data)
1849 /* Dummy implementation, returns 0 unconditionally. */
1850 cmap->data = cmap;
1851 cmap->get_glyph = opentype_cmap_dummy_get_glyph;
1852 cmap->get_ranges = opentype_cmap_dummy_get_ranges;
1856 void dwrite_cmap_release(struct dwrite_cmap *cmap)
1858 if (cmap->stream)
1860 IDWriteFontFileStream_ReleaseFileFragment(cmap->stream, cmap->table_context);
1861 IDWriteFontFileStream_Release(cmap->stream);
1863 cmap->data = NULL;
1864 cmap->stream = NULL;
1867 HRESULT opentype_cmap_get_unicode_ranges(const struct dwrite_cmap *cmap, unsigned int max_count, DWRITE_UNICODE_RANGE *ranges,
1868 unsigned int *count)
1870 if (!cmap->data)
1871 return E_FAIL;
1873 *count = cmap->get_ranges(cmap, max_count, ranges);
1875 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
1878 void opentype_get_font_typo_metrics(struct file_stream_desc *stream_desc, unsigned int *ascent, unsigned int *descent)
1880 struct dwrite_fonttable os2;
1881 const TT_OS2_V2 *data;
1883 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1884 data = (const TT_OS2_V2 *)os2.data;
1886 *ascent = *descent = 0;
1888 if (os2.size >= FIELD_OFFSET(TT_OS2_V2, sTypoLineGap))
1890 SHORT value = GET_BE_WORD(data->sTypoDescender);
1891 *ascent = GET_BE_WORD(data->sTypoAscender);
1892 *descent = value < 0 ? -value : 0;
1895 if (data)
1896 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
1899 void opentype_get_font_metrics(struct file_stream_desc *stream_desc, DWRITE_FONT_METRICS1 *metrics, DWRITE_CARET_METRICS *caret)
1901 struct dwrite_fonttable os2, head, post, hhea;
1902 const TT_OS2_V2 *tt_os2;
1903 const TT_HEAD *tt_head;
1904 const TT_POST *tt_post;
1905 const TT_HHEA *tt_hhea;
1907 memset(metrics, 0, sizeof(*metrics));
1909 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1910 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
1911 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
1912 opentype_get_font_table(stream_desc, MS_HHEA_TAG, &hhea);
1914 tt_head = (const TT_HEAD *)head.data;
1915 tt_os2 = (const TT_OS2_V2 *)os2.data;
1916 tt_post = (const TT_POST *)post.data;
1917 tt_hhea = (const TT_HHEA *)hhea.data;
1919 if (tt_head) {
1920 metrics->designUnitsPerEm = GET_BE_WORD(tt_head->unitsPerEm);
1921 metrics->glyphBoxLeft = GET_BE_WORD(tt_head->xMin);
1922 metrics->glyphBoxTop = GET_BE_WORD(tt_head->yMax);
1923 metrics->glyphBoxRight = GET_BE_WORD(tt_head->xMax);
1924 metrics->glyphBoxBottom = GET_BE_WORD(tt_head->yMin);
1927 if (caret)
1929 if (tt_hhea) {
1930 caret->slopeRise = GET_BE_WORD(tt_hhea->caretSlopeRise);
1931 caret->slopeRun = GET_BE_WORD(tt_hhea->caretSlopeRun);
1932 caret->offset = GET_BE_WORD(tt_hhea->caretOffset);
1934 else {
1935 caret->slopeRise = 0;
1936 caret->slopeRun = 0;
1937 caret->offset = 0;
1941 if (tt_os2) {
1942 USHORT version = GET_BE_WORD(tt_os2->version);
1944 metrics->ascent = GET_BE_WORD(tt_os2->usWinAscent);
1945 /* Some fonts have usWinDescent value stored as signed short, which could be wrongly
1946 interpreted as large unsigned value. */
1947 metrics->descent = abs((SHORT)GET_BE_WORD(tt_os2->usWinDescent));
1949 /* line gap is estimated using two sets of ascender/descender values and 'hhea' line gap */
1950 if (tt_hhea) {
1951 SHORT descender = (SHORT)GET_BE_WORD(tt_hhea->descender);
1952 INT32 linegap;
1954 linegap = GET_BE_WORD(tt_hhea->ascender) + abs(descender) + GET_BE_WORD(tt_hhea->linegap) -
1955 metrics->ascent - metrics->descent;
1956 metrics->lineGap = linegap > 0 ? linegap : 0;
1959 metrics->strikethroughPosition = GET_BE_WORD(tt_os2->yStrikeoutPosition);
1960 metrics->strikethroughThickness = GET_BE_WORD(tt_os2->yStrikeoutSize);
1961 metrics->subscriptPositionX = GET_BE_WORD(tt_os2->ySubscriptXOffset);
1962 /* Y offset is stored as positive offset below baseline */
1963 metrics->subscriptPositionY = -GET_BE_WORD(tt_os2->ySubscriptYOffset);
1964 metrics->subscriptSizeX = GET_BE_WORD(tt_os2->ySubscriptXSize);
1965 metrics->subscriptSizeY = GET_BE_WORD(tt_os2->ySubscriptYSize);
1966 metrics->superscriptPositionX = GET_BE_WORD(tt_os2->ySuperscriptXOffset);
1967 metrics->superscriptPositionY = GET_BE_WORD(tt_os2->ySuperscriptYOffset);
1968 metrics->superscriptSizeX = GET_BE_WORD(tt_os2->ySuperscriptXSize);
1969 metrics->superscriptSizeY = GET_BE_WORD(tt_os2->ySuperscriptYSize);
1971 /* version 2 fields */
1972 if (version >= 2) {
1973 metrics->capHeight = GET_BE_WORD(tt_os2->sCapHeight);
1974 metrics->xHeight = GET_BE_WORD(tt_os2->sxHeight);
1977 if (GET_BE_WORD(tt_os2->fsSelection) & OS2_FSSELECTION_USE_TYPO_METRICS) {
1978 SHORT descent = GET_BE_WORD(tt_os2->sTypoDescender);
1979 metrics->ascent = GET_BE_WORD(tt_os2->sTypoAscender);
1980 metrics->descent = descent < 0 ? -descent : 0;
1981 metrics->lineGap = GET_BE_WORD(tt_os2->sTypoLineGap);
1982 metrics->hasTypographicMetrics = TRUE;
1985 else {
1986 metrics->strikethroughPosition = metrics->designUnitsPerEm / 3;
1987 if (tt_hhea) {
1988 metrics->ascent = GET_BE_WORD(tt_hhea->ascender);
1989 metrics->descent = abs((SHORT)GET_BE_WORD(tt_hhea->descender));
1993 if (tt_post) {
1994 metrics->underlinePosition = GET_BE_WORD(tt_post->underlinePosition);
1995 metrics->underlineThickness = GET_BE_WORD(tt_post->underlineThickness);
1998 if (metrics->underlineThickness == 0)
1999 metrics->underlineThickness = metrics->designUnitsPerEm / 14;
2000 if (metrics->strikethroughThickness == 0)
2001 metrics->strikethroughThickness = metrics->underlineThickness;
2003 /* estimate missing metrics */
2004 if (metrics->xHeight == 0)
2005 metrics->xHeight = metrics->designUnitsPerEm / 2;
2006 if (metrics->capHeight == 0)
2007 metrics->capHeight = metrics->designUnitsPerEm * 7 / 10;
2009 if (tt_os2)
2010 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2011 if (tt_head)
2012 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2013 if (tt_post)
2014 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2015 if (tt_hhea)
2016 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, hhea.context);
2019 void opentype_get_font_properties(struct file_stream_desc *stream_desc, struct dwrite_font_props *props)
2021 struct dwrite_fonttable os2, head, colr, cpal;
2022 BOOL is_symbol, is_monospaced;
2023 const TT_OS2_V2 *tt_os2;
2024 const TT_HEAD *tt_head;
2026 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2027 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
2029 tt_os2 = (const TT_OS2_V2 *)os2.data;
2030 tt_head = (const TT_HEAD *)head.data;
2032 /* default stretch, weight and style to normal */
2033 props->stretch = DWRITE_FONT_STRETCH_NORMAL;
2034 props->weight = DWRITE_FONT_WEIGHT_NORMAL;
2035 props->style = DWRITE_FONT_STYLE_NORMAL;
2036 memset(&props->panose, 0, sizeof(props->panose));
2037 memset(&props->fontsig, 0, sizeof(props->fontsig));
2038 memset(&props->lf, 0, sizeof(props->lf));
2039 props->flags = 0;
2041 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
2042 if (tt_os2)
2044 USHORT version = GET_BE_WORD(tt_os2->version);
2045 USHORT fsSelection = GET_BE_WORD(tt_os2->fsSelection);
2046 USHORT usWeightClass = GET_BE_WORD(tt_os2->usWeightClass);
2047 USHORT usWidthClass = GET_BE_WORD(tt_os2->usWidthClass);
2049 if (usWidthClass > DWRITE_FONT_STRETCH_UNDEFINED && usWidthClass <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
2050 props->stretch = usWidthClass;
2052 if (usWeightClass >= 1 && usWeightClass <= 9)
2053 usWeightClass *= 100;
2055 if (usWeightClass > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
2056 props->weight = DWRITE_FONT_WEIGHT_ULTRA_BLACK;
2057 else if (usWeightClass > 0)
2058 props->weight = usWeightClass;
2060 if (version >= 4 && (fsSelection & OS2_FSSELECTION_OBLIQUE))
2061 props->style = DWRITE_FONT_STYLE_OBLIQUE;
2062 else if (fsSelection & OS2_FSSELECTION_ITALIC)
2063 props->style = DWRITE_FONT_STYLE_ITALIC;
2064 props->lf.lfItalic = !!(fsSelection & OS2_FSSELECTION_ITALIC);
2066 memcpy(&props->panose, &tt_os2->panose, sizeof(props->panose));
2068 /* FONTSIGNATURE */
2069 props->fontsig.fsUsb[0] = GET_BE_DWORD(tt_os2->ulUnicodeRange1);
2070 props->fontsig.fsUsb[1] = GET_BE_DWORD(tt_os2->ulUnicodeRange2);
2071 props->fontsig.fsUsb[2] = GET_BE_DWORD(tt_os2->ulUnicodeRange3);
2072 props->fontsig.fsUsb[3] = GET_BE_DWORD(tt_os2->ulUnicodeRange4);
2074 if (version)
2076 props->fontsig.fsCsb[0] = GET_BE_DWORD(tt_os2->ulCodePageRange1);
2077 props->fontsig.fsCsb[1] = GET_BE_DWORD(tt_os2->ulCodePageRange2);
2080 else if (tt_head) {
2081 USHORT macStyle = GET_BE_WORD(tt_head->macStyle);
2083 if (macStyle & TT_HEAD_MACSTYLE_CONDENSED)
2084 props->stretch = DWRITE_FONT_STRETCH_CONDENSED;
2085 else if (macStyle & TT_HEAD_MACSTYLE_EXTENDED)
2086 props->stretch = DWRITE_FONT_STRETCH_EXPANDED;
2088 if (macStyle & TT_HEAD_MACSTYLE_BOLD)
2089 props->weight = DWRITE_FONT_WEIGHT_BOLD;
2091 if (macStyle & TT_HEAD_MACSTYLE_ITALIC) {
2092 props->style = DWRITE_FONT_STYLE_ITALIC;
2093 props->lf.lfItalic = 1;
2097 props->lf.lfWeight = props->weight;
2099 /* FONT_IS_SYMBOL */
2100 if (!(is_symbol = props->panose.familyKind == DWRITE_PANOSE_FAMILY_SYMBOL))
2102 struct dwrite_fonttable cmap;
2103 int i, offset, num_tables;
2105 opentype_get_font_table(stream_desc, MS_CMAP_TAG, &cmap);
2107 if (cmap.data)
2109 num_tables = table_read_be_word(&cmap, FIELD_OFFSET(struct cmap_header, num_tables));
2110 offset = FIELD_OFFSET(struct cmap_header, tables);
2112 for (i = 0; !is_symbol && i < num_tables; ++i)
2114 WORD platform, encoding;
2116 platform = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2117 FIELD_OFFSET(struct cmap_encoding_record, platformID));
2118 encoding = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2119 FIELD_OFFSET(struct cmap_encoding_record, encodingID));
2121 is_symbol = platform == OPENTYPE_CMAP_TABLE_PLATFORM_WIN &&
2122 encoding == OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL;
2125 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cmap.context);
2128 if (is_symbol)
2129 props->flags |= FONT_IS_SYMBOL;
2131 /* FONT_IS_MONOSPACED */
2132 if (!(is_monospaced = props->panose.text.proportion == DWRITE_PANOSE_PROPORTION_MONOSPACED))
2134 struct dwrite_fonttable post;
2136 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
2138 if (post.data)
2140 is_monospaced = !!table_read_dword(&post, FIELD_OFFSET(TT_POST, fixed_pitch));
2142 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2145 if (is_monospaced)
2146 props->flags |= FONT_IS_MONOSPACED;
2148 /* FONT_IS_COLORED */
2149 opentype_get_font_table(stream_desc, MS_COLR_TAG, &colr);
2150 if (colr.data)
2152 opentype_get_font_table(stream_desc, MS_CPAL_TAG, &cpal);
2153 if (cpal.data)
2155 props->flags |= FONT_IS_COLORED;
2156 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cpal.context);
2159 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, colr.context);
2162 TRACE("stretch=%d, weight=%d, style %d\n", props->stretch, props->weight, props->style);
2164 if (os2.data)
2165 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2166 if (head.data)
2167 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2170 static UINT get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform, USHORT encoding)
2172 UINT codepage = 0;
2174 switch (platform) {
2175 case OPENTYPE_PLATFORM_UNICODE:
2176 break;
2177 case OPENTYPE_PLATFORM_MAC:
2178 switch (encoding)
2180 case TT_NAME_MAC_ENCODING_ROMAN:
2181 codepage = 10000;
2182 break;
2183 case TT_NAME_MAC_ENCODING_JAPANESE:
2184 codepage = 10001;
2185 break;
2186 case TT_NAME_MAC_ENCODING_TRAD_CHINESE:
2187 codepage = 10002;
2188 break;
2189 case TT_NAME_MAC_ENCODING_KOREAN:
2190 codepage = 10003;
2191 break;
2192 case TT_NAME_MAC_ENCODING_ARABIC:
2193 codepage = 10004;
2194 break;
2195 case TT_NAME_MAC_ENCODING_HEBREW:
2196 codepage = 10005;
2197 break;
2198 case TT_NAME_MAC_ENCODING_GREEK:
2199 codepage = 10006;
2200 break;
2201 case TT_NAME_MAC_ENCODING_RUSSIAN:
2202 codepage = 10007;
2203 break;
2204 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE:
2205 codepage = 10008;
2206 break;
2207 case TT_NAME_MAC_ENCODING_THAI:
2208 codepage = 10021;
2209 break;
2210 default:
2211 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2212 break;
2214 break;
2215 case OPENTYPE_PLATFORM_WIN:
2216 switch (encoding)
2218 case TT_NAME_WINDOWS_ENCODING_SYMBOL:
2219 case TT_NAME_WINDOWS_ENCODING_UNICODE_BMP:
2220 case TT_NAME_WINDOWS_ENCODING_UNICODE_FULL:
2221 break;
2222 case TT_NAME_WINDOWS_ENCODING_SJIS:
2223 codepage = 932;
2224 break;
2225 case TT_NAME_WINDOWS_ENCODING_PRC:
2226 codepage = 936;
2227 break;
2228 case TT_NAME_WINDOWS_ENCODING_BIG5:
2229 codepage = 950;
2230 break;
2231 case TT_NAME_WINDOWS_ENCODING_WANSUNG:
2232 codepage = 20949;
2233 break;
2234 case TT_NAME_WINDOWS_ENCODING_JOHAB:
2235 codepage = 1361;
2236 break;
2237 default:
2238 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2239 break;
2241 break;
2242 default:
2243 FIXME("unknown platform %d\n", platform);
2246 return codepage;
2249 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform, USHORT lang_id, WCHAR *locale, USHORT locale_len)
2251 static const WCHAR enusW[] = {'e','n','-','U','S',0};
2253 switch (platform) {
2254 case OPENTYPE_PLATFORM_MAC:
2256 const char *locale_name = NULL;
2258 if (lang_id > TT_NAME_MAC_LANGID_AZER_ROMAN)
2259 WARN("invalid mac lang id %d\n", lang_id);
2260 else if (!name_mac_langid_to_locale[lang_id][0])
2261 FIXME("failed to map mac lang id %d to locale name\n", lang_id);
2262 else
2263 locale_name = name_mac_langid_to_locale[lang_id];
2265 if (locale_name)
2266 MultiByteToWideChar(CP_ACP, 0, name_mac_langid_to_locale[lang_id], -1, locale, locale_len);
2267 else
2268 strcpyW(locale, enusW);
2269 break;
2271 case OPENTYPE_PLATFORM_WIN:
2272 if (!LCIDToLocaleName(MAKELCID(lang_id, SORT_DEFAULT), locale, locale_len, 0)) {
2273 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id, SORT_DEFAULT));
2274 strcpyW(locale, enusW);
2276 break;
2277 case OPENTYPE_PLATFORM_UNICODE:
2278 strcpyW(locale, enusW);
2279 break;
2280 default:
2281 FIXME("unknown platform %d\n", platform);
2285 static BOOL opentype_decode_namerecord(const TT_NAME_V0 *header, BYTE *storage_area, USHORT recid, IDWriteLocalizedStrings *strings)
2287 const TT_NameRecord *record = &header->nameRecord[recid];
2288 USHORT lang_id, length, offset, encoding, platform;
2289 BOOL ret = FALSE;
2291 platform = GET_BE_WORD(record->platformID);
2292 lang_id = GET_BE_WORD(record->languageID);
2293 length = GET_BE_WORD(record->length);
2294 offset = GET_BE_WORD(record->offset);
2295 encoding = GET_BE_WORD(record->encodingID);
2297 if (lang_id < 0x8000) {
2298 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
2299 WCHAR *name_string;
2300 UINT codepage;
2302 codepage = get_name_record_codepage(platform, encoding);
2303 get_name_record_locale(platform, lang_id, locale, ARRAY_SIZE(locale));
2305 if (codepage) {
2306 DWORD len = MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, NULL, 0);
2307 name_string = heap_alloc(sizeof(WCHAR) * (len+1));
2308 MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, name_string, len);
2309 name_string[len] = 0;
2311 else {
2312 int i;
2314 length /= sizeof(WCHAR);
2315 name_string = heap_strdupnW((LPWSTR)(storage_area + offset), length);
2316 for (i = 0; i < length; i++)
2317 name_string[i] = GET_BE_WORD(name_string[i]);
2320 TRACE("string %s for locale %s found\n", debugstr_w(name_string), debugstr_w(locale));
2321 add_localizedstring(strings, locale, name_string);
2322 heap_free(name_string);
2323 ret = TRUE;
2325 else
2326 FIXME("handle NAME format 1\n");
2328 return ret;
2331 static HRESULT opentype_get_font_strings_from_id(const void *table_data, enum OPENTYPE_STRING_ID id, IDWriteLocalizedStrings **strings)
2333 int i, count, candidate_mac, candidate_unicode;
2334 const TT_NAME_V0 *header;
2335 BYTE *storage_area = 0;
2336 WORD format;
2337 BOOL exists;
2338 HRESULT hr;
2340 if (!table_data)
2341 return E_FAIL;
2343 hr = create_localizedstrings(strings);
2344 if (FAILED(hr)) return hr;
2346 header = table_data;
2347 format = GET_BE_WORD(header->format);
2349 switch (format) {
2350 case 0:
2351 case 1:
2352 break;
2353 default:
2354 FIXME("unsupported NAME format %d\n", format);
2357 storage_area = (LPBYTE)table_data + GET_BE_WORD(header->stringOffset);
2358 count = GET_BE_WORD(header->count);
2360 exists = FALSE;
2361 candidate_unicode = candidate_mac = -1;
2362 for (i = 0; i < count; i++) {
2363 const TT_NameRecord *record = &header->nameRecord[i];
2364 USHORT platform;
2366 if (GET_BE_WORD(record->nameID) != id)
2367 continue;
2369 platform = GET_BE_WORD(record->platformID);
2370 switch (platform)
2372 /* Skip Unicode or Mac entries for now, fonts tend to duplicate those
2373 strings as WIN platform entries. If font does not have WIN entry for
2374 this id, we will use Mac or Unicode platform entry while assuming
2375 en-US locale. */
2376 case OPENTYPE_PLATFORM_UNICODE:
2377 if (candidate_unicode == -1)
2378 candidate_unicode = i;
2379 break;
2380 case OPENTYPE_PLATFORM_MAC:
2381 if (candidate_mac == -1)
2382 candidate_mac = i;
2383 break;
2384 case OPENTYPE_PLATFORM_WIN:
2385 if (opentype_decode_namerecord(header, storage_area, i, *strings))
2386 exists = TRUE;
2387 break;
2388 default:
2389 FIXME("platform %i not supported\n", platform);
2390 break;
2394 if (!exists)
2396 if (candidate_mac != -1)
2397 exists = opentype_decode_namerecord(header, storage_area, candidate_mac, *strings);
2398 if (!exists && candidate_unicode != -1)
2399 exists = opentype_decode_namerecord(header, storage_area, candidate_unicode, *strings);
2401 if (!exists)
2403 IDWriteLocalizedStrings_Release(*strings);
2404 *strings = NULL;
2408 if (*strings)
2409 sort_localizedstrings(*strings);
2411 return exists ? S_OK : E_FAIL;
2414 static WCHAR *meta_get_lng_name(WCHAR *str, WCHAR **ctx)
2416 static const WCHAR delimW[] = {',',' ',0};
2417 WCHAR *ret;
2419 if (!str) str = *ctx;
2420 while (*str && strchrW(delimW, *str)) str++;
2421 if (!*str) return NULL;
2422 ret = str++;
2423 while (*str && !strchrW(delimW, *str)) str++;
2424 if (*str) *str++ = 0;
2425 *ctx = str;
2427 return ret;
2430 static HRESULT opentype_get_font_strings_from_meta(const struct file_stream_desc *stream_desc,
2431 DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **ret)
2433 static const WCHAR emptyW[] = { 0 };
2434 const struct meta_data_map *maps;
2435 IDWriteLocalizedStrings *strings;
2436 struct dwrite_fonttable meta;
2437 DWORD version, i, count, tag;
2438 HRESULT hr;
2440 *ret = NULL;
2442 switch (id)
2444 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2445 tag = MS_DLNG_TAG;
2446 break;
2447 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2448 tag = MS_SLNG_TAG;
2449 break;
2450 default:
2451 WARN("Unexpected id %d.\n", id);
2452 return S_OK;
2455 if (FAILED(hr = create_localizedstrings(&strings)))
2456 return hr;
2458 opentype_get_font_table(stream_desc, MS_META_TAG, &meta);
2460 if (meta.data)
2462 version = table_read_be_dword(&meta, 0);
2463 if (version != 1)
2465 WARN("Unexpected meta table version %d.\n", version);
2466 goto end;
2469 count = table_read_be_dword(&meta, FIELD_OFFSET(struct meta_header, data_maps_count));
2470 if (!(maps = table_read_ensure(&meta, FIELD_OFFSET(struct meta_header, maps),
2471 count * sizeof(struct meta_data_map))))
2472 goto end;
2474 for (i = 0; i < count; ++i)
2476 const char *data;
2478 if (maps[i].tag == tag && maps[i].length)
2480 DWORD length = GET_BE_DWORD(maps[i].length), j;
2482 if ((data = table_read_ensure(&meta, GET_BE_DWORD(maps[i].offset), length)))
2484 WCHAR *ptrW = heap_alloc((length + 1) * sizeof(WCHAR)), *ctx, *token;
2486 if (!ptrW)
2488 hr = E_OUTOFMEMORY;
2489 goto end;
2492 /* Data is stored in comma separated list, ASCII range only. */
2493 for (j = 0; j < length; ++j)
2494 ptrW[j] = data[j];
2495 ptrW[length] = 0;
2497 token = meta_get_lng_name(ptrW, &ctx);
2499 while (token)
2501 add_localizedstring(strings, emptyW, token);
2502 token = meta_get_lng_name(NULL, &ctx);
2505 heap_free(ptrW);
2509 end:
2510 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, meta.context);
2513 if (IDWriteLocalizedStrings_GetCount(strings))
2514 *ret = strings;
2515 else
2516 IDWriteLocalizedStrings_Release(strings);
2518 return hr;
2521 HRESULT opentype_get_font_info_strings(const struct file_stream_desc *stream_desc, DWRITE_INFORMATIONAL_STRING_ID id,
2522 IDWriteLocalizedStrings **strings)
2524 struct dwrite_fonttable name;
2526 switch (id)
2528 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2529 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2530 opentype_get_font_strings_from_meta(stream_desc, id, strings);
2531 break;
2532 default:
2533 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2534 opentype_get_font_strings_from_id(name.data, dwriteid_to_opentypeid[id], strings);
2535 if (name.context)
2536 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2539 return S_OK;
2542 /* FamilyName locating order is WWS Family Name -> Preferred Family Name -> Family Name. If font claims to
2543 have 'Preferred Family Name' in WWS format, then WWS name is not used. */
2544 HRESULT opentype_get_font_familyname(struct file_stream_desc *stream_desc, IDWriteLocalizedStrings **names)
2546 struct dwrite_fonttable os2, name;
2547 const TT_OS2_V2 *tt_os2;
2548 const void *name_table;
2549 HRESULT hr;
2551 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2552 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2554 tt_os2 = (const TT_OS2_V2 *)os2.data;
2555 name_table = (const void *)name.data;
2557 *names = NULL;
2559 /* if Preferred Family doesn't conform to WWS model try WWS name */
2560 if (tt_os2 && !(GET_BE_WORD(tt_os2->fsSelection) & OS2_FSSELECTION_WWS))
2561 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_WWS_FAMILY_NAME, names);
2562 else
2563 hr = E_FAIL;
2565 if (FAILED(hr))
2566 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME, names);
2567 if (FAILED(hr))
2568 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_FAMILY_NAME, names);
2570 if (os2.context)
2571 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2572 if (name.context)
2573 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2575 return hr;
2578 /* FaceName locating order is WWS Face Name -> Preferred Face Name -> Face Name. If font claims to
2579 have 'Preferred Face Name' in WWS format, then WWS name is not used. */
2580 HRESULT opentype_get_font_facename(struct file_stream_desc *stream_desc, WCHAR *lfname, IDWriteLocalizedStrings **names)
2582 struct dwrite_fonttable os2, name;
2583 IDWriteLocalizedStrings *lfnames;
2584 const TT_OS2_V2 *tt_os2;
2585 const void *name_table;
2586 HRESULT hr;
2588 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2589 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2591 tt_os2 = (const TT_OS2_V2 *)os2.data;
2592 name_table = name.data;
2594 *names = NULL;
2596 /* if Preferred Family doesn't conform to WWS model try WWS name */
2597 if (tt_os2 && !(GET_BE_WORD(tt_os2->fsSelection) & OS2_FSSELECTION_WWS))
2598 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_WWS_SUBFAMILY_NAME, names);
2599 else
2600 hr = E_FAIL;
2602 if (FAILED(hr))
2603 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME, names);
2604 if (FAILED(hr))
2605 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_SUBFAMILY_NAME, names);
2607 /* User locale is preferred, with fallback to en-us. */
2608 *lfname = 0;
2609 if (SUCCEEDED(opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_FAMILY_NAME, &lfnames))) {
2610 static const WCHAR enusW[] = {'e','n','-','u','s',0};
2611 WCHAR localeW[LOCALE_NAME_MAX_LENGTH];
2612 UINT32 index;
2613 BOOL exists;
2615 exists = FALSE;
2616 if (GetSystemDefaultLocaleName(localeW, ARRAY_SIZE(localeW)))
2617 IDWriteLocalizedStrings_FindLocaleName(lfnames, localeW, &index, &exists);
2619 if (!exists)
2620 IDWriteLocalizedStrings_FindLocaleName(lfnames, enusW, &index, &exists);
2622 if (exists) {
2623 UINT32 length = 0;
2624 WCHAR *nameW;
2626 IDWriteLocalizedStrings_GetStringLength(lfnames, index, &length);
2627 nameW = heap_alloc((length + 1) * sizeof(WCHAR));
2628 if (nameW) {
2629 *nameW = 0;
2630 IDWriteLocalizedStrings_GetString(lfnames, index, nameW, length + 1);
2631 lstrcpynW(lfname, nameW, LF_FACESIZE);
2632 heap_free(nameW);
2636 IDWriteLocalizedStrings_Release(lfnames);
2639 if (os2.context)
2640 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2641 if (name.context)
2642 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2644 return hr;
2647 static const struct ot_langsys *opentype_get_langsys(const struct ot_gsubgpos_table *table, unsigned int script_index,
2648 unsigned int language_index, unsigned int *feature_count)
2650 unsigned int table_offset, langsys_offset;
2651 const struct ot_langsys *langsys = NULL;
2653 *feature_count = 0;
2655 if (!table->table.data || script_index == ~0u)
2656 return NULL;
2658 /* ScriptTable offset. */
2659 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
2660 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
2661 if (!table_offset)
2662 return NULL;
2664 if (language_index == ~0u)
2665 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
2666 else
2667 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
2668 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
2669 FIELD_OFFSET(struct ot_langsys_record, langsys));
2670 langsys_offset += table->script_list + table_offset;
2672 *feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
2673 if (*feature_count)
2674 langsys = table_read_ensure(&table->table, langsys_offset, FIELD_OFFSET(struct ot_langsys, feature_index[*feature_count]));
2675 if (!langsys)
2676 *feature_count = 0;
2678 return langsys;
2681 void opentype_get_typographic_features(struct ot_gsubgpos_table *table, unsigned int script_index,
2682 unsigned int language_index, struct tag_array *t)
2684 unsigned int i, total_feature_count, script_feature_count;
2685 const struct ot_feature_list *feature_list;
2686 const struct ot_langsys *langsys = NULL;
2688 langsys = opentype_get_langsys(table, script_index, language_index, &script_feature_count);
2690 total_feature_count = table_read_be_word(&table->table, table->feature_list);
2691 if (!total_feature_count)
2692 return;
2694 feature_list = table_read_ensure(&table->table, table->feature_list,
2695 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
2696 if (!feature_list)
2697 return;
2699 for (i = 0; i < script_feature_count; ++i)
2701 unsigned int feature_index = GET_BE_WORD(langsys->feature_index[i]);
2702 if (feature_index >= total_feature_count)
2703 continue;
2705 if (!dwrite_array_reserve((void **)&t->tags, &t->capacity, t->count + 1, sizeof(*t->tags)))
2706 return;
2708 t->tags[t->count++] = feature_list->features[feature_index].tag;
2712 static unsigned int find_vdmx_group(const struct vdmx_header *hdr)
2714 WORD num_ratios, i;
2715 const struct vdmx_ratio *ratios = (struct vdmx_ratio *)(hdr + 1);
2716 BYTE dev_x_ratio = 1, dev_y_ratio = 1;
2717 unsigned int group_offset = 0;
2719 num_ratios = GET_BE_WORD(hdr->num_ratios);
2721 for (i = 0; i < num_ratios; i++) {
2723 if (!ratios[i].bCharSet) continue;
2725 if ((ratios[i].xRatio == 0 && ratios[i].yStartRatio == 0 &&
2726 ratios[i].yEndRatio == 0) ||
2727 (ratios[i].xRatio == dev_x_ratio && ratios[i].yStartRatio <= dev_y_ratio &&
2728 ratios[i].yEndRatio >= dev_y_ratio))
2730 group_offset = GET_BE_WORD(*((WORD *)(ratios + num_ratios) + i));
2731 break;
2735 return group_offset;
2738 BOOL opentype_get_vdmx_size(const struct dwrite_fonttable *vdmx, INT emsize, UINT16 *ascent, UINT16 *descent)
2740 unsigned int num_ratios, num_recs, group_offset, i;
2741 const struct vdmx_header *header;
2742 const struct vdmx_group *group;
2744 if (!vdmx->exists)
2745 return FALSE;
2747 num_ratios = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_ratios));
2748 num_recs = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_recs));
2750 header = table_read_ensure(vdmx, 0, sizeof(*header) + num_ratios * sizeof(struct vdmx_ratio) +
2751 num_recs * sizeof(*group));
2753 if (!header)
2754 return FALSE;
2756 group_offset = find_vdmx_group(header);
2757 if (!group_offset)
2758 return FALSE;
2760 num_recs = table_read_be_word(vdmx, group_offset);
2761 group = table_read_ensure(vdmx, group_offset, FIELD_OFFSET(struct vdmx_group, entries[num_recs]));
2763 if (!group)
2764 return FALSE;
2766 if (emsize < group->startsz || emsize >= group->endsz)
2767 return FALSE;
2769 for (i = 0; i < num_recs; ++i)
2771 WORD ppem = GET_BE_WORD(group->entries[i].yPelHeight);
2772 if (ppem > emsize) {
2773 FIXME("interpolate %d\n", emsize);
2774 return FALSE;
2777 if (ppem == emsize) {
2778 *ascent = (SHORT)GET_BE_WORD(group->entries[i].yMax);
2779 *descent = -(SHORT)GET_BE_WORD(group->entries[i].yMin);
2780 return TRUE;
2784 return FALSE;
2787 unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable *gasp, float emsize)
2789 unsigned int version, num_ranges, i;
2790 const struct gasp_header *table;
2791 WORD flags = 0;
2793 if (!gasp->exists)
2794 return 0;
2796 num_ranges = table_read_be_word(gasp, FIELD_OFFSET(struct gasp_header, num_ranges));
2798 table = table_read_ensure(gasp, 0, FIELD_OFFSET(struct gasp_header, ranges[num_ranges]));
2799 if (!table)
2800 return 0;
2802 version = GET_BE_WORD(table->version);
2803 if (version > 1)
2805 ERR("Unsupported gasp table format version %u.\n", version);
2806 goto done;
2809 for (i = 0; i < num_ranges; ++i)
2811 flags = GET_BE_WORD(table->ranges[i].flags);
2812 if (emsize <= GET_BE_WORD(table->ranges[i].max_ppem)) break;
2815 done:
2816 return flags;
2819 unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable *cpal)
2821 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palettes));
2824 unsigned int opentype_get_cpal_paletteentrycount(const struct dwrite_fonttable *cpal)
2826 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palette_entries));
2829 HRESULT opentype_get_cpal_entries(const struct dwrite_fonttable *cpal, unsigned int palette,
2830 unsigned int first_entry_index, unsigned int entry_count, DWRITE_COLOR_F *entries)
2832 unsigned int num_palettes, num_palette_entries, i;
2833 const struct cpal_color_record *records;
2834 const struct cpal_header_0 *header;
2836 header = table_read_ensure(cpal, 0, sizeof(*header));
2838 if (!cpal->exists || !header)
2839 return DWRITE_E_NOCOLOR;
2841 num_palettes = GET_BE_WORD(header->num_palettes);
2842 if (palette >= num_palettes)
2843 return DWRITE_E_NOCOLOR;
2845 header = table_read_ensure(cpal, 0, FIELD_OFFSET(struct cpal_header_0, color_record_indices[palette]));
2846 if (!header)
2847 return DWRITE_E_NOCOLOR;
2849 num_palette_entries = GET_BE_WORD(header->num_palette_entries);
2850 if (first_entry_index + entry_count > num_palette_entries)
2851 return E_INVALIDARG;
2853 records = table_read_ensure(cpal, GET_BE_DWORD(header->offset_first_color_record),
2854 sizeof(*records) * GET_BE_WORD(header->num_color_records));
2855 if (!records)
2856 return DWRITE_E_NOCOLOR;
2858 first_entry_index += GET_BE_WORD(header->color_record_indices[palette]);
2860 for (i = 0; i < entry_count; i++) {
2861 entries[i].u1.r = records[first_entry_index + i].red / 255.0f;
2862 entries[i].u2.g = records[first_entry_index + i].green / 255.0f;
2863 entries[i].u3.b = records[first_entry_index + i].blue / 255.0f;
2864 entries[i].u4.a = records[first_entry_index + i].alpha / 255.0f;
2867 return S_OK;
2870 static int colr_compare_gid(const void *g, const void *r)
2872 const struct colr_baseglyph_record *record = r;
2873 UINT16 glyph = *(UINT16*)g, GID = GET_BE_WORD(record->glyph);
2874 int ret = 0;
2876 if (glyph > GID)
2877 ret = 1;
2878 else if (glyph < GID)
2879 ret = -1;
2881 return ret;
2884 HRESULT opentype_get_colr_glyph(const struct dwrite_fonttable *colr, UINT16 glyph, struct dwrite_colorglyph *ret)
2886 unsigned int num_baseglyph_records, offset_baseglyph_records;
2887 const struct colr_baseglyph_record *record;
2888 const struct colr_layer_record *layer;
2889 const struct colr_header *header;
2891 memset(ret, 0, sizeof(*ret));
2892 ret->glyph = glyph;
2893 ret->palette_index = 0xffff;
2895 header = table_read_ensure(colr, 0, sizeof(*header));
2896 if (!header)
2897 return S_FALSE;
2899 num_baseglyph_records = GET_BE_WORD(header->num_baseglyph_records);
2900 offset_baseglyph_records = GET_BE_DWORD(header->offset_baseglyph_records);
2901 if (!table_read_ensure(colr, offset_baseglyph_records, num_baseglyph_records * sizeof(*record)))
2903 return S_FALSE;
2906 record = bsearch(&glyph, colr->data + offset_baseglyph_records, num_baseglyph_records,
2907 sizeof(*record), colr_compare_gid);
2908 if (!record)
2909 return S_FALSE;
2911 ret->first_layer = GET_BE_WORD(record->first_layer_index);
2912 ret->num_layers = GET_BE_WORD(record->num_layers);
2914 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
2915 (ret->first_layer + ret->layer) * sizeof(*layer))))
2917 layer += ret->first_layer + ret->layer;
2918 ret->glyph = GET_BE_WORD(layer->glyph);
2919 ret->palette_index = GET_BE_WORD(layer->palette_index);
2922 return S_OK;
2925 void opentype_colr_next_glyph(const struct dwrite_fonttable *colr, struct dwrite_colorglyph *glyph)
2927 const struct colr_layer_record *layer;
2928 const struct colr_header *header;
2930 /* iterated all the way through */
2931 if (glyph->layer == glyph->num_layers)
2932 return;
2934 if (!(header = table_read_ensure(colr, 0, sizeof(*header))))
2935 return;
2937 glyph->layer++;
2939 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
2940 (glyph->first_layer + glyph->layer) * sizeof(*layer))))
2942 layer += glyph->first_layer + glyph->layer;
2943 glyph->glyph = GET_BE_WORD(layer->glyph);
2944 glyph->palette_index = GET_BE_WORD(layer->palette_index);
2948 BOOL opentype_has_vertical_variants(IDWriteFontFace5 *fontface)
2950 const struct gpos_gsub_header *header;
2951 const struct ot_feature_list *featurelist;
2952 const struct ot_lookup_list *lookup_list;
2953 BOOL exists = FALSE, ret = FALSE;
2954 unsigned int i, j;
2955 const void *data;
2956 void *context;
2957 UINT32 size;
2958 HRESULT hr;
2960 hr = IDWriteFontFace5_TryGetFontTable(fontface, MS_GSUB_TAG, &data, &size, &context, &exists);
2961 if (FAILED(hr) || !exists)
2962 return FALSE;
2964 header = data;
2965 featurelist = (struct ot_feature_list *)((BYTE*)header + GET_BE_WORD(header->feature_list));
2966 lookup_list = (const struct ot_lookup_list *)((BYTE*)header + GET_BE_WORD(header->lookup_list));
2968 for (i = 0; i < GET_BE_WORD(featurelist->feature_count); i++) {
2969 if (featurelist->features[i].tag == DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING) {
2970 const struct ot_feature *feature = (const struct ot_feature*)((BYTE*)featurelist + GET_BE_WORD(featurelist->features[i].offset));
2971 UINT16 lookup_count = GET_BE_WORD(feature->lookup_count), index, count, type;
2972 const GSUB_SingleSubstFormat2 *subst2;
2973 const struct ot_lookup_table *lookup_table;
2974 UINT32 offset;
2976 if (lookup_count == 0)
2977 continue;
2979 for (j = 0; j < lookup_count; ++j) {
2980 /* check if lookup is empty */
2981 index = GET_BE_WORD(feature->lookuplist_index[j]);
2982 lookup_table = (const struct ot_lookup_table *)((BYTE*)lookup_list + GET_BE_WORD(lookup_list->lookup[index]));
2984 type = GET_BE_WORD(lookup_table->lookup_type);
2985 if (type != GSUB_LOOKUP_SINGLE_SUBST && type != GSUB_LOOKUP_EXTENSION_SUBST)
2986 continue;
2988 count = GET_BE_WORD(lookup_table->subtable_count);
2989 if (count == 0)
2990 continue;
2992 offset = GET_BE_WORD(lookup_table->subtable[0]);
2993 if (type == GSUB_LOOKUP_EXTENSION_SUBST) {
2994 const GSUB_ExtensionPosFormat1 *ext = (const GSUB_ExtensionPosFormat1 *)((const BYTE *)lookup_table + offset);
2995 if (GET_BE_WORD(ext->SubstFormat) == 1)
2996 offset += GET_BE_DWORD(ext->ExtensionOffset);
2997 else
2998 FIXME("Unhandled Extension Substitution Format %u\n", GET_BE_WORD(ext->SubstFormat));
3001 subst2 = (const GSUB_SingleSubstFormat2*)((BYTE*)lookup_table + offset);
3002 index = GET_BE_WORD(subst2->SubstFormat);
3003 if (index == 1)
3004 FIXME("Validate Single Substitution Format 1\n");
3005 else if (index == 2) {
3006 /* SimSun-ExtB has 0 glyph count for this substitution */
3007 if (GET_BE_WORD(subst2->GlyphCount) > 0) {
3008 ret = TRUE;
3009 break;
3012 else
3013 WARN("Unknown Single Substitution Format, %u\n", index);
3018 IDWriteFontFace5_ReleaseFontTable(fontface, context);
3020 return ret;
3023 static BOOL opentype_has_font_table(IDWriteFontFace5 *fontface, UINT32 tag)
3025 BOOL exists = FALSE;
3026 const void *data;
3027 void *context;
3028 UINT32 size;
3029 HRESULT hr;
3031 hr = IDWriteFontFace5_TryGetFontTable(fontface, tag, &data, &size, &context, &exists);
3032 if (FAILED(hr))
3033 return FALSE;
3035 if (exists)
3036 IDWriteFontFace5_ReleaseFontTable(fontface, context);
3038 return exists;
3041 static unsigned int opentype_get_sbix_formats(IDWriteFontFace5 *fontface)
3043 unsigned int num_strikes, num_glyphs, i, j, ret = 0;
3044 const struct sbix_header *sbix_header;
3045 struct dwrite_fonttable table;
3047 memset(&table, 0, sizeof(table));
3048 table.exists = TRUE;
3050 if (!get_fontface_table(fontface, MS_MAXP_TAG, &table))
3051 return 0;
3053 num_glyphs = table_read_be_word(&table, FIELD_OFFSET(struct maxp, num_glyphs));
3055 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
3057 memset(&table, 0, sizeof(table));
3058 table.exists = TRUE;
3060 if (!get_fontface_table(fontface, MS_SBIX_TAG, &table))
3061 return 0;
3063 num_strikes = table_read_be_dword(&table, FIELD_OFFSET(struct sbix_header, num_strikes));
3064 sbix_header = table_read_ensure(&table, 0, FIELD_OFFSET(struct sbix_header, strike_offset[num_strikes]));
3066 if (sbix_header)
3068 for (i = 0; i < num_strikes; ++i)
3070 unsigned int strike_offset = GET_BE_DWORD(sbix_header->strike_offset[i]);
3071 const struct sbix_strike *strike = table_read_ensure(&table, strike_offset,
3072 FIELD_OFFSET(struct sbix_strike, glyphdata_offsets[num_glyphs + 1]));
3074 if (!strike)
3075 continue;
3077 for (j = 0; j < num_glyphs; j++)
3079 unsigned int offset = GET_BE_DWORD(strike->glyphdata_offsets[j]);
3080 unsigned int next_offset = GET_BE_DWORD(strike->glyphdata_offsets[j + 1]);
3081 const struct sbix_glyph_data *glyph_data;
3083 if (offset == next_offset)
3084 continue;
3086 glyph_data = table_read_ensure(&table, strike_offset + offset, sizeof(*glyph_data));
3087 if (!glyph_data)
3088 continue;
3090 switch (glyph_data->graphic_type)
3092 case MS_PNG__TAG:
3093 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3094 break;
3095 case MS_JPG__TAG:
3096 ret |= DWRITE_GLYPH_IMAGE_FORMATS_JPEG;
3097 break;
3098 case MS_TIFF_TAG:
3099 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TIFF;
3100 break;
3101 default:
3102 FIXME("unexpected bitmap format %s\n", debugstr_tag(GET_BE_DWORD(glyph_data->graphic_type)));
3108 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
3110 return ret;
3113 static unsigned int opentype_get_cblc_formats(IDWriteFontFace5 *fontface)
3115 const unsigned int format_mask = DWRITE_GLYPH_IMAGE_FORMATS_PNG |
3116 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3117 const struct cblc_bitmapsize_table *sizes;
3118 struct dwrite_fonttable cblc = { 0 };
3119 unsigned int num_sizes, i, ret = 0;
3120 const struct cblc_header *header;
3122 cblc.exists = TRUE;
3123 if (!get_fontface_table(fontface, MS_CBLC_TAG, &cblc))
3124 return 0;
3126 num_sizes = table_read_be_dword(&cblc, FIELD_OFFSET(struct cblc_header, num_sizes));
3127 sizes = table_read_ensure(&cblc, sizeof(*header), num_sizes * sizeof(*sizes));
3129 if (sizes)
3131 for (i = 0; i < num_sizes; ++i)
3133 BYTE bpp = sizes[i].bit_depth;
3135 if ((ret & format_mask) == format_mask)
3136 break;
3138 if (bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8)
3139 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3140 else if (bpp == 32)
3141 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3145 IDWriteFontFace5_ReleaseFontTable(fontface, cblc.context);
3147 return ret;
3150 UINT32 opentype_get_glyph_image_formats(IDWriteFontFace5 *fontface)
3152 UINT32 ret = DWRITE_GLYPH_IMAGE_FORMATS_NONE;
3154 if (opentype_has_font_table(fontface, MS_GLYF_TAG))
3155 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
3157 if (opentype_has_font_table(fontface, MS_CFF__TAG) ||
3158 opentype_has_font_table(fontface, MS_CFF2_TAG))
3159 ret |= DWRITE_GLYPH_IMAGE_FORMATS_CFF;
3161 if (opentype_has_font_table(fontface, MS_COLR_TAG))
3162 ret |= DWRITE_GLYPH_IMAGE_FORMATS_COLR;
3164 if (opentype_has_font_table(fontface, MS_SVG__TAG))
3165 ret |= DWRITE_GLYPH_IMAGE_FORMATS_SVG;
3167 if (opentype_has_font_table(fontface, MS_SBIX_TAG))
3168 ret |= opentype_get_sbix_formats(fontface);
3170 if (opentype_has_font_table(fontface, MS_CBLC_TAG))
3171 ret |= opentype_get_cblc_formats(fontface);
3173 return ret;
3176 DWRITE_CONTAINER_TYPE opentype_analyze_container_type(void const *data, UINT32 data_size)
3178 DWORD signature;
3180 if (data_size < sizeof(DWORD))
3181 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3183 /* Both WOFF and WOFF2 start with 4 bytes signature. */
3184 signature = *(DWORD *)data;
3186 switch (signature)
3188 case MS_WOFF_TAG:
3189 return DWRITE_CONTAINER_TYPE_WOFF;
3190 case MS_WOF2_TAG:
3191 return DWRITE_CONTAINER_TYPE_WOFF2;
3192 default:
3193 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3197 void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache)
3199 cache->font->grab_font_table(cache->context, MS_GSUB_TAG, &cache->gsub.table.data, &cache->gsub.table.size,
3200 &cache->gsub.table.context);
3202 if (cache->gsub.table.data)
3204 cache->gsub.script_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, script_list));
3205 cache->gsub.feature_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3206 cache->gsub.lookup_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3209 cache->font->grab_font_table(cache->context, MS_GPOS_TAG, &cache->gpos.table.data, &cache->gpos.table.size,
3210 &cache->gpos.table.context);
3212 if (cache->gpos.table.data)
3214 cache->gpos.script_list = table_read_be_word(&cache->gpos.table,
3215 FIELD_OFFSET(struct gpos_gsub_header, script_list));
3216 cache->gpos.feature_list = table_read_be_word(&cache->gpos.table,
3217 FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3218 cache->gpos.lookup_list = table_read_be_word(&cache->gpos.table,
3219 FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3222 cache->font->grab_font_table(cache->context, MS_GDEF_TAG, &cache->gdef.table.data, &cache->gdef.table.size,
3223 &cache->gdef.table.context);
3225 if (cache->gdef.table.data)
3227 unsigned int version = table_read_be_dword(&cache->gdef.table, 0);
3229 cache->gdef.classdef = table_read_be_word(&cache->gdef.table, FIELD_OFFSET(struct gdef_header, classdef));
3230 cache->gdef.markattachclassdef = table_read_be_word(&cache->gdef.table,
3231 FIELD_OFFSET(struct gdef_header, markattach_classdef));
3232 if (version >= 0x00010002)
3233 cache->gdef.markglyphsetdef = table_read_be_word(&cache->gdef.table,
3234 FIELD_OFFSET(struct gdef_header, markglyphsetdef));
3238 unsigned int opentype_layout_find_script(const struct scriptshaping_cache *cache, unsigned int kind, DWORD script,
3239 unsigned int *script_index)
3241 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3242 UINT16 script_count;
3243 unsigned int i;
3245 *script_index = ~0u;
3247 script_count = table_read_be_word(&table->table, table->script_list);
3248 if (!script_count)
3249 return 0;
3251 for (i = 0; i < script_count; i++)
3253 unsigned int tag = table_read_dword(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3254 i * sizeof(struct ot_script_record));
3255 if (!tag)
3256 continue;
3258 if (tag == script)
3260 *script_index = i;
3261 return script;
3265 return 0;
3268 unsigned int opentype_layout_find_language(const struct scriptshaping_cache *cache, unsigned int kind, DWORD language,
3269 unsigned int script_index, unsigned int *language_index)
3271 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3272 UINT16 table_offset, lang_count;
3273 unsigned int i;
3275 *language_index = ~0u;
3277 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3278 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
3279 if (!table_offset)
3280 return 0;
3282 lang_count = table_read_be_word(&table->table, table->script_list + table_offset +
3283 FIELD_OFFSET(struct ot_script, langsys_count));
3284 for (i = 0; i < lang_count; i++)
3286 unsigned int tag = table_read_dword(&table->table, table->script_list + table_offset +
3287 FIELD_OFFSET(struct ot_script, langsys) + i * sizeof(struct ot_langsys_record));
3289 if (tag == language)
3291 *language_index = i;
3292 return language;
3296 /* Try 'defaultLangSys' if it's set. */
3297 if (table_read_be_word(&table->table, table->script_list + table_offset))
3298 return ~0u;
3300 return 0;
3303 static int gdef_class_compare_format2(const void *g, const void *r)
3305 const struct ot_gdef_class_range *range = r;
3306 UINT16 glyph = *(UINT16 *)g;
3308 if (glyph < GET_BE_WORD(range->start_glyph))
3309 return -1;
3310 else if (glyph > GET_BE_WORD(range->end_glyph))
3311 return 1;
3312 else
3313 return 0;
3316 static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable *table,
3317 unsigned int offset, UINT16 glyph)
3319 WORD format = table_read_be_word(table, offset), count;
3320 unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED;
3322 if (format == 1)
3324 const struct ot_gdef_classdef_format1 *format1;
3326 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
3327 format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
3328 if (format1)
3330 WORD start_glyph = GET_BE_WORD(format1->start_glyph);
3331 if (glyph >= start_glyph && (glyph - start_glyph) < count)
3333 glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
3334 if (glyph_class > GDEF_CLASS_MAX)
3335 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3339 else if (format == 2)
3341 const struct ot_gdef_classdef_format2 *format2;
3343 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
3344 format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
3345 if (format2)
3347 const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
3348 sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
3349 glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
3350 GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
3351 if (glyph_class > GDEF_CLASS_MAX)
3352 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3355 else
3356 WARN("Unknown GDEF format %u.\n", format);
3358 return glyph_class;
3361 static unsigned int opentype_set_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3363 struct scriptshaping_cache *cache = context->cache;
3364 unsigned int glyph_class = 0, props;
3366 if (cache->gdef.classdef)
3368 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.classdef,
3369 context->u.buffer.glyphs[idx]);
3372 switch (glyph_class)
3374 case GDEF_CLASS_BASE:
3375 props = GLYPH_PROP_BASE;
3376 break;
3377 case GDEF_CLASS_LIGATURE:
3378 props = GLYPH_PROP_LIGATURE;
3379 break;
3380 case GDEF_CLASS_MARK:
3381 props = GLYPH_PROP_MARK;
3382 if (cache->gdef.markattachclassdef)
3384 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.markattachclassdef,
3385 context->u.buffer.glyphs[idx]);
3386 props |= glyph_class << 8;
3388 break;
3389 default:
3390 props = 0;
3393 context->glyph_infos[idx].props = props;
3395 return props;
3398 static void opentype_set_subst_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3400 unsigned int glyph_props = opentype_set_glyph_props(context, idx) & LOOKUP_FLAG_IGNORE_MASK;
3401 context->u.subst.glyph_props[idx].isDiacritic = !!(glyph_props == GLYPH_PROP_MARK);
3402 context->u.subst.glyph_props[idx].isZeroWidthSpace = !!(glyph_props == GLYPH_PROP_MARK);
3405 struct coverage_compare_format1_context
3407 UINT16 glyph;
3408 const UINT16 *table_base;
3409 unsigned int *coverage_index;
3412 static int coverage_compare_format1(const void *left, const void *right)
3414 const struct coverage_compare_format1_context *context = left;
3415 UINT16 glyph = GET_BE_WORD(*(UINT16 *)right);
3416 int ret;
3418 ret = context->glyph - glyph;
3419 if (!ret)
3420 *context->coverage_index = (UINT16 *)right - context->table_base;
3422 return ret;
3425 static int coverage_compare_format2(const void *g, const void *r)
3427 const struct ot_coverage_range *range = r;
3428 UINT16 glyph = *(UINT16 *)g;
3430 if (glyph < GET_BE_WORD(range->start_glyph))
3431 return -1;
3432 else if (glyph > GET_BE_WORD(range->end_glyph))
3433 return 1;
3434 else
3435 return 0;
3438 static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable *table, unsigned int coverage,
3439 UINT16 glyph)
3441 WORD format = table_read_be_word(table, coverage), count;
3443 count = table_read_be_word(table, coverage + 2);
3445 if (format == 1)
3447 const struct ot_coverage_format1 *format1 = table_read_ensure(table, coverage,
3448 FIELD_OFFSET(struct ot_coverage_format1, glyphs[count]));
3449 struct coverage_compare_format1_context context;
3450 unsigned int coverage_index = GLYPH_NOT_COVERED;
3452 if (format1)
3454 context.glyph = glyph;
3455 context.table_base = format1->glyphs;
3456 context.coverage_index = &coverage_index;
3458 bsearch(&context, format1->glyphs, count, sizeof(glyph), coverage_compare_format1);
3461 return coverage_index;
3463 else if (format == 2)
3465 const struct ot_coverage_format2 *format2 = table_read_ensure(table, coverage,
3466 FIELD_OFFSET(struct ot_coverage_format2, ranges[count]));
3467 if (format2)
3469 const struct ot_coverage_range *range = bsearch(&glyph, format2->ranges, count,
3470 sizeof(struct ot_coverage_range), coverage_compare_format2);
3471 return range && glyph <= GET_BE_WORD(range->end_glyph) ?
3472 GET_BE_WORD(range->startcoverage_index) + glyph - GET_BE_WORD(range->start_glyph) :
3473 GLYPH_NOT_COVERED;
3476 else
3477 WARN("Unknown coverage format %u.\n", format);
3479 return -1;
3482 static inline unsigned int dwrite_popcount(unsigned int x)
3484 #ifdef HAVE___BUILTIN_POPCOUNT
3485 return __builtin_popcount(x);
3486 #else
3487 x -= x >> 1 & 0x55555555;
3488 x = (x & 0x33333333) + (x >> 2 & 0x33333333);
3489 return ((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
3490 #endif
3493 static float opentype_scale_gpos_be_value(WORD value, float emsize, UINT16 upem)
3495 return (short)GET_BE_WORD(value) * emsize / upem;
3498 static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context *context, unsigned int offset)
3500 const struct dwrite_fonttable *table = &context->table->table;
3501 unsigned int start_size, end_size, format, value_word;
3502 unsigned int index, ppem, mask;
3503 int value;
3505 if (!offset)
3506 return 0;
3508 start_size = table_read_be_word(table, offset);
3509 end_size = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, end_size));
3511 ppem = context->emsize;
3512 if (ppem < start_size || ppem > end_size)
3513 return 0;
3515 format = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, format));
3517 if (format < 1 || format > 3)
3518 return 0;
3520 index = ppem - start_size;
3522 value_word = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, values[index >> (4 - format)]));
3523 mask = 0xffff >> (16 - (1 << format));
3525 value = (value_word >> ((index % (4 - format)) * (1 << format))) & mask;
3527 if ((unsigned int)value >= ((mask + 1) >> 1))
3528 value -= mask + 1;
3530 return value;
3533 static void opentype_layout_apply_gpos_value(struct scriptshaping_context *context, unsigned int table_offset,
3534 WORD value_format, const WORD *values, unsigned int glyph)
3536 const struct scriptshaping_cache *cache = context->cache;
3537 DWRITE_GLYPH_OFFSET *offset = &context->offsets[glyph];
3538 float *advance = &context->advances[glyph];
3540 if (!value_format)
3541 return;
3543 if (value_format & GPOS_VALUE_X_PLACEMENT)
3545 offset->advanceOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3546 values++;
3548 if (value_format & GPOS_VALUE_Y_PLACEMENT)
3550 offset->ascenderOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3551 values++;
3553 if (value_format & GPOS_VALUE_X_ADVANCE)
3555 *advance += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3556 values++;
3558 if (value_format & GPOS_VALUE_Y_ADVANCE)
3560 values++;
3562 if (value_format & GPOS_VALUE_X_PLACEMENT_DEVICE)
3564 offset->advanceOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3565 values++;
3567 if (value_format & GPOS_VALUE_Y_PLACEMENT_DEVICE)
3569 offset->ascenderOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3570 values++;
3572 if (value_format & GPOS_VALUE_X_ADVANCE_DEVICE)
3574 *advance += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3575 values++;
3577 if (value_format & GPOS_VALUE_Y_ADVANCE_DEVICE)
3579 values++;
3583 static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context *context,
3584 unsigned int lookup_offset, unsigned int subtable)
3586 unsigned int subtable_offset = table_read_be_word(&context->table->table, lookup_offset +
3587 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
3589 return lookup_offset + subtable_offset;
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;
3638 static void glyph_iterator_init(struct scriptshaping_context *context, unsigned int flags, unsigned int pos,
3639 unsigned int len, struct glyph_iterator *iter)
3641 iter->context = context;
3642 iter->flags = flags;
3643 iter->pos = pos;
3644 iter->len = len;
3645 iter->mask = ~0u;
3646 iter->match_func = NULL;
3647 iter->match_data = NULL;
3648 iter->glyph_data = NULL;
3651 struct ot_gdef_mark_glyph_sets
3653 UINT16 format;
3654 UINT16 count;
3655 DWORD offsets[1];
3658 static BOOL opentype_match_glyph_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3660 return glyph == glyph_data;
3663 static BOOL opentype_match_class_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3665 const struct match_context *mc = data->mc;
3666 UINT16 glyph_class = opentype_layout_get_glyph_class(&mc->context->table->table, data->subtable_offset, glyph);
3667 return glyph_class == glyph_data;
3670 static BOOL opentype_match_coverage_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3672 const struct match_context *mc = data->mc;
3673 return opentype_layout_is_glyph_covered(&mc->context->table->table, data->subtable_offset + glyph_data, glyph)
3674 != GLYPH_NOT_COVERED;
3677 static BOOL opentype_layout_mark_set_covers(const struct scriptshaping_cache *cache, unsigned int set_index,
3678 UINT16 glyph)
3680 unsigned int format, offset = cache->gdef.markglyphsetdef, coverage_offset, set_count;
3682 if (!offset)
3683 return FALSE;
3685 format = table_read_be_word(&cache->gdef.table, offset);
3687 if (format == 1)
3689 set_count = table_read_be_word(&cache->gdef.table, offset + 2);
3690 if (!set_count || set_index >= set_count)
3691 return FALSE;
3693 coverage_offset = table_read_be_dword(&cache->gdef.table, offset + 2 + set_index * sizeof(coverage_offset));
3694 return opentype_layout_is_glyph_covered(&cache->gdef.table, offset + coverage_offset, glyph) != GLYPH_NOT_COVERED;
3696 else
3697 WARN("Unexpected MarkGlyphSets format %#x.\n", format);
3699 return FALSE;
3702 static BOOL lookup_is_glyph_match(const struct scriptshaping_context *context, unsigned int idx, unsigned int match_props)
3704 unsigned int glyph_props = context->glyph_infos[idx].props;
3705 UINT16 glyph = context->u.buffer.glyphs[idx];
3707 if (glyph_props & match_props & LOOKUP_FLAG_IGNORE_MASK)
3708 return FALSE;
3710 if (!(glyph_props & GLYPH_PROP_MARK))
3711 return TRUE;
3713 if (match_props & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
3714 return opentype_layout_mark_set_covers(context->cache, match_props >> 16, glyph);
3716 if (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE)
3717 return (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE) == (glyph_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE);
3719 return TRUE;
3722 static enum iterator_match glyph_iterator_may_skip(const struct glyph_iterator *iter)
3724 if (!lookup_is_glyph_match(iter->context, iter->pos, iter->flags))
3725 return ITER_YES;
3727 return ITER_NO;
3730 static enum iterator_match glyph_iterator_may_match(const struct glyph_iterator *iter)
3732 if (!(iter->mask & iter->context->glyph_infos[iter->pos].mask))
3733 return ITER_NO;
3735 /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that
3736 in all matching functions. */
3737 if (iter->match_func)
3738 return !!iter->match_func(iter->context->u.buffer.glyphs[iter->pos], GET_BE_WORD(*iter->glyph_data), iter->match_data);
3740 return ITER_MAYBE;
3743 static BOOL glyph_iterator_next(struct glyph_iterator *iter)
3745 enum iterator_match skip, match;
3747 while (iter->pos + iter->len < iter->context->glyph_count)
3749 ++iter->pos;
3751 skip = glyph_iterator_may_skip(iter);
3752 if (skip == ITER_YES)
3753 continue;
3755 match = glyph_iterator_may_match(iter);
3756 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3758 --iter->len;
3759 if (iter->glyph_data)
3760 ++iter->glyph_data;
3761 return TRUE;
3764 if (skip == ITER_NO)
3765 return FALSE;
3768 return FALSE;
3771 static BOOL glyph_iterator_prev(struct glyph_iterator *iter)
3773 enum iterator_match skip, match;
3775 while (iter->pos > iter->len - 1)
3777 --iter->pos;
3779 skip = glyph_iterator_may_skip(iter);
3780 if (skip == ITER_YES)
3781 continue;
3783 match = glyph_iterator_may_match(iter);
3784 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3786 --iter->len;
3787 if (iter->glyph_data)
3788 ++iter->glyph_data;
3789 return TRUE;
3792 if (skip == ITER_NO)
3793 return FALSE;
3796 return FALSE;
3799 struct lookup
3801 unsigned short index;
3802 unsigned short type;
3803 unsigned short flags;
3804 unsigned short subtable_count;
3806 unsigned int mask;
3807 unsigned int offset;
3810 static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context,
3811 const struct lookup *lookup, unsigned int subtable_offset)
3813 const struct dwrite_fonttable *table = &context->table->table;
3814 UINT16 format, value_format, value_len, coverage, glyph;
3816 unsigned int coverage_index;
3818 format = table_read_be_word(table, subtable_offset);
3820 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage));
3821 value_format = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format));
3822 value_len = dwrite_popcount(value_format);
3824 glyph = context->u.pos.glyphs[context->cur];
3826 if (format == 1)
3828 const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(table, subtable_offset,
3829 FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len]));
3831 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3832 if (coverage_index == GLYPH_NOT_COVERED)
3833 return FALSE;
3835 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, context->cur);
3837 else if (format == 2)
3839 WORD value_count = table_read_be_word(table, subtable_offset +
3840 FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count));
3841 const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(table, subtable_offset,
3842 FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD));
3844 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3845 if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count)
3846 return FALSE;
3848 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, &format2->values[coverage_index * value_len],
3849 context->cur);
3851 else
3853 WARN("Unknown single adjustment format %u.\n", format);
3854 return FALSE;
3857 context->cur++;
3859 return TRUE;
3862 static int gpos_pair_adjustment_compare_format1(const void *g, const void *r)
3864 const struct ot_gpos_pairvalue *pairvalue = r;
3865 UINT16 second_glyph = GET_BE_WORD(pairvalue->second_glyph);
3866 return *(UINT16 *)g - second_glyph;
3869 static BOOL opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context *context,
3870 const struct lookup *lookup, unsigned int subtable_offset)
3872 const struct dwrite_fonttable *table = &context->table->table;
3873 unsigned int first_glyph, second_glyph;
3874 struct glyph_iterator iter_pair;
3875 WORD format, coverage;
3877 WORD value_format1, value_format2, value_len1, value_len2;
3878 unsigned int coverage_index;
3880 glyph_iterator_init(context, lookup->flags, context->cur, 1, &iter_pair);
3881 if (!glyph_iterator_next(&iter_pair))
3882 return FALSE;
3884 if (context->is_rtl)
3886 first_glyph = iter_pair.pos;
3887 second_glyph = context->cur;
3889 else
3891 first_glyph = context->cur;
3892 second_glyph = iter_pair.pos;
3895 format = table_read_be_word(table, subtable_offset);
3897 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1, coverage));
3898 if (!coverage)
3899 return FALSE;
3901 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, context->u.pos.glyphs[first_glyph]);
3902 if (coverage_index == GLYPH_NOT_COVERED)
3903 return FALSE;
3905 if (format == 1)
3907 const struct ot_gpos_pairpos_format1 *format1;
3908 WORD pairset_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1,
3909 pairset_count));
3910 unsigned int pairvalue_len, pairset_offset;
3911 const struct ot_gpos_pairset *pairset;
3912 const WORD *pairvalue;
3913 WORD pairvalue_count;
3915 if (!pairset_count || coverage_index >= pairset_count)
3916 return FALSE;
3918 format1 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairsets[pairset_count]));
3919 if (!format1)
3920 return FALSE;
3922 /* Ordered paired values. */
3923 pairvalue_count = table_read_be_word(table, subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]));
3924 if (!pairvalue_count)
3925 return FALSE;
3927 /* Structure length is variable, but does not change across the subtable. */
3928 value_format1 = GET_BE_WORD(format1->value_format1) & 0xff;
3929 value_format2 = GET_BE_WORD(format1->value_format2) & 0xff;
3931 value_len1 = dwrite_popcount(value_format1);
3932 value_len2 = dwrite_popcount(value_format2);
3933 pairvalue_len = FIELD_OFFSET(struct ot_gpos_pairvalue, data) + value_len1 * sizeof(WORD) +
3934 value_len2 * sizeof(WORD);
3936 pairset_offset = subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]);
3937 pairset = table_read_ensure(table, subtable_offset + pairset_offset, pairvalue_len * pairvalue_count);
3938 if (!pairset)
3939 return FALSE;
3941 pairvalue = bsearch(&context->u.pos.glyphs[second_glyph], pairset->pairvalues, pairvalue_count,
3942 pairvalue_len, gpos_pair_adjustment_compare_format1);
3943 if (!pairvalue)
3944 return FALSE;
3946 pairvalue += 1; /* Skip SecondGlyph. */
3947 opentype_layout_apply_gpos_value(context, pairset_offset, value_format1, pairvalue, first_glyph);
3948 opentype_layout_apply_gpos_value(context, pairset_offset, value_format2, pairvalue + value_len1,
3949 second_glyph);
3951 context->cur = iter_pair.pos;
3952 if (value_len2)
3953 context->cur++;
3955 else if (format == 2)
3957 const struct ot_gpos_pairpos_format2 *format2;
3958 WORD class1_count, class2_count;
3959 unsigned int class1, class2;
3960 const WCHAR *values;
3962 value_format1 = table_read_be_word(table, subtable_offset +
3963 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format1)) & 0xff;
3964 value_format2 = table_read_be_word(table, subtable_offset +
3965 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format2)) & 0xff;
3967 class1_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class1_count));
3968 class2_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class2_count));
3970 value_len1 = dwrite_popcount(value_format1);
3971 value_len2 = dwrite_popcount(value_format2);
3973 format2 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format2,
3974 values[class1_count * class2_count * (value_len1 + value_len2)]));
3975 if (!format2)
3976 return FALSE;
3978 class1 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def1),
3979 context->u.pos.glyphs[first_glyph]);
3980 class2 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def2),
3981 context->u.pos.glyphs[second_glyph]);
3983 if (!(class1 < class1_count && class2 < class2_count))
3984 return FALSE;
3986 values = &format2->values[(class1 * class2_count + class2) * (value_len1 + value_len2)];
3987 opentype_layout_apply_gpos_value(context, subtable_offset, value_format1, values, first_glyph);
3988 opentype_layout_apply_gpos_value(context, subtable_offset, value_format2, values + value_len1,
3989 second_glyph);
3991 context->cur = iter_pair.pos;
3992 if (value_len2)
3993 context->cur++;
3995 else
3997 WARN("Unknown pair adjustment format %u.\n", format);
3998 return FALSE;
4001 return TRUE;
4004 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context *context, unsigned int anchor_offset,
4005 unsigned int glyph_index, float *x, float *y)
4007 const struct scriptshaping_cache *cache = context->cache;
4008 const struct dwrite_fonttable *table = &context->table->table;
4010 WORD format = table_read_be_word(table, anchor_offset);
4012 *x = *y = 0.0f;
4014 if (format == 1)
4016 const struct ot_gpos_anchor_format1 *format1 = table_read_ensure(table, anchor_offset, sizeof(*format1));
4018 if (format1)
4020 *x = opentype_scale_gpos_be_value(format1->x_coord, context->emsize, cache->upem);
4021 *y = opentype_scale_gpos_be_value(format1->y_coord, context->emsize, cache->upem);
4024 else if (format == 2)
4026 const struct ot_gpos_anchor_format2 *format2 = table_read_ensure(table, anchor_offset, sizeof(*format2));
4028 if (format2)
4030 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
4031 FIXME("Use outline anchor point for glyph %u.\n", context->u.pos.glyphs[glyph_index]);
4033 *x = opentype_scale_gpos_be_value(format2->x_coord, context->emsize, cache->upem);
4034 *y = opentype_scale_gpos_be_value(format2->y_coord, context->emsize, cache->upem);
4037 else if (format == 3)
4039 const struct ot_gpos_anchor_format3 *format3 = table_read_ensure(table, anchor_offset, sizeof(*format3));
4041 if (format3)
4043 *x = opentype_scale_gpos_be_value(format3->x_coord, context->emsize, cache->upem);
4044 *y = opentype_scale_gpos_be_value(format3->y_coord, context->emsize, cache->upem);
4046 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
4048 if (format3->x_dev_offset)
4049 *x += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->x_dev_offset));
4050 if (format3->y_dev_offset)
4051 *y += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->y_dev_offset));
4055 else
4056 WARN("Unknown anchor format %u.\n", format);
4059 static BOOL opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context *context,
4060 const struct lookup *lookup, unsigned int subtable_offset)
4062 const struct dwrite_fonttable *table = &context->table->table;
4063 UINT16 format, glyph;
4065 format = table_read_be_word(table, subtable_offset);
4066 glyph = context->u.pos.glyphs[context->cur];
4068 if (format == 1)
4070 WORD coverage_offset = table_read_be_word(table, subtable_offset +
4071 FIELD_OFFSET(struct ot_gpos_cursive_format1, coverage));
4072 unsigned int glyph_index, entry_count, entry_anchor, exit_anchor;
4073 float entry_x, entry_y, exit_x, exit_y, delta;
4074 struct glyph_iterator prev_iter;
4076 if (!coverage_offset)
4077 return FALSE;
4079 entry_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_cursive_format1, count));
4081 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset, glyph);
4082 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4083 return FALSE;
4085 entry_anchor = table_read_be_word(table, subtable_offset +
4086 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2]));
4087 if (!entry_anchor)
4088 return FALSE;
4090 glyph_iterator_init(context, lookup->flags, context->cur, 1, &prev_iter);
4091 if (!glyph_iterator_prev(&prev_iter))
4092 return FALSE;
4094 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset,
4095 context->u.pos.glyphs[prev_iter.pos]);
4096 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4097 return FALSE;
4099 exit_anchor = table_read_be_word(table, subtable_offset +
4100 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2 + 1]));
4101 if (!exit_anchor)
4102 return FALSE;
4104 opentype_layout_gpos_get_anchor(context, subtable_offset + exit_anchor, prev_iter.pos, &exit_x, &exit_y);
4105 opentype_layout_gpos_get_anchor(context, subtable_offset + entry_anchor, context->cur, &entry_x, &entry_y);
4107 if (context->is_rtl)
4109 delta = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4110 context->advances[prev_iter.pos] -= delta;
4111 context->advances[context->cur] = entry_x + context->offsets[context->cur].advanceOffset;
4112 context->offsets[prev_iter.pos].advanceOffset -= delta;
4114 else
4116 delta = entry_x + context->offsets[context->cur].advanceOffset;
4117 context->advances[prev_iter.pos] = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4118 context->advances[context->cur] -= delta;
4119 context->offsets[context->cur].advanceOffset -= delta;
4122 if (lookup->flags & LOOKUP_FLAG_RTL)
4123 context->offsets[prev_iter.pos].ascenderOffset = entry_y - exit_y;
4124 else
4125 context->offsets[context->cur].ascenderOffset = exit_y - entry_y;
4127 context->cur++;
4129 else
4131 WARN("Unknown cursive attachment format %u.\n", format);
4132 return FALSE;
4135 return TRUE;
4138 static BOOL opentype_layout_apply_mark_array(struct scriptshaping_context *context, unsigned int subtable_offset,
4139 unsigned int mark_array, unsigned int mark_index, unsigned int glyph_index, unsigned int anchors_matrix,
4140 unsigned int class_count, unsigned int glyph_pos)
4142 const struct dwrite_fonttable *table = &context->table->table;
4143 unsigned int mark_class, mark_count, glyph_count;
4144 const struct ot_gpos_mark_record *record;
4145 float mark_x, mark_y, base_x, base_y;
4146 const UINT16 *anchors;
4148 mark_count = table_read_be_word(table, subtable_offset + mark_array);
4149 if (mark_index >= mark_count) return FALSE;
4151 if (!(record = table_read_ensure(table, subtable_offset + mark_array +
4152 FIELD_OFFSET(struct ot_gpos_mark_array, records[mark_index]), sizeof(*record))))
4154 return FALSE;
4157 mark_class = GET_BE_WORD(record->mark_class);
4158 if (mark_class >= class_count) return FALSE;
4160 glyph_count = table_read_be_word(table, subtable_offset + anchors_matrix);
4161 if (glyph_index >= glyph_count) return FALSE;
4163 /* Anchors data is stored as two dimensional array [glyph_count][class_count], starting with row count field. */
4164 anchors = table_read_ensure(table, subtable_offset + anchors_matrix + 2, glyph_count * class_count * sizeof(*anchors));
4165 if (!anchors) return FALSE;
4167 opentype_layout_gpos_get_anchor(context, subtable_offset + mark_array + GET_BE_WORD(record->mark_anchor),
4168 context->cur, &mark_x, &mark_y);
4169 opentype_layout_gpos_get_anchor(context, subtable_offset + anchors_matrix +
4170 GET_BE_WORD(anchors[glyph_index * class_count + mark_class]), glyph_pos, &base_x, &base_y);
4172 if (context->is_rtl)
4173 context->offsets[context->cur].advanceOffset = mark_x - base_x;
4174 else
4175 context->offsets[context->cur].advanceOffset = -context->advances[glyph_pos] + base_x - mark_x;
4177 context->offsets[context->cur].ascenderOffset = base_y - mark_y;
4178 context->cur++;
4180 return TRUE;
4183 static BOOL opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context *context,
4184 const struct lookup *lookup, unsigned int subtable_offset)
4186 const struct dwrite_fonttable *table = &context->table->table;
4187 WORD format;
4189 format = table_read_be_word(table, subtable_offset);
4191 if (format == 1)
4193 const struct ot_gpos_mark_to_base_format1 *format1;
4194 unsigned int base_index, mark_index;
4195 struct glyph_iterator base_iter;
4197 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4199 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4200 context->u.pos.glyphs[context->cur]);
4201 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4203 /* Look back for first base glyph. */
4204 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &base_iter);
4205 if (!glyph_iterator_prev(&base_iter))
4206 return FALSE;
4208 base_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->base_coverage),
4209 context->u.pos.glyphs[base_iter.pos]);
4210 if (base_index == GLYPH_NOT_COVERED) return FALSE;
4212 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4213 base_index, GET_BE_WORD(format1->base_array), GET_BE_WORD(format1->mark_class_count), base_iter.pos);
4215 else
4217 WARN("Unknown mark-to-base format %u.\n", format);
4218 return FALSE;
4221 return TRUE;
4224 static const UINT16 * table_read_array_be_word(const struct dwrite_fonttable *table, unsigned int offset,
4225 unsigned int index, UINT16 *data)
4227 unsigned int count = table_read_be_word(table, offset);
4228 const UINT16 *array;
4230 if (index != ~0u && index >= count) return NULL;
4231 if (!(array = table_read_ensure(table, offset + 2, count * sizeof(*array)))) return FALSE;
4232 *data = index == ~0u ? count : GET_BE_WORD(array[index]);
4233 return array;
4236 static BOOL opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context *context,
4237 const struct lookup *lookup, unsigned int subtable_offset)
4239 const struct dwrite_fonttable *table = &context->table->table;
4240 WORD format;
4242 format = table_read_be_word(table, subtable_offset);
4244 if (format == 1)
4246 unsigned int mark_index, lig_index, comp_index, class_count, comp_count;
4247 const struct ot_gpos_mark_to_lig_format1 *format1;
4248 struct glyph_iterator lig_iter;
4249 unsigned int lig_array;
4250 UINT16 lig_attach;
4252 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4254 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4255 context->u.pos.glyphs[context->cur]);
4256 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4258 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &lig_iter);
4259 if (!glyph_iterator_prev(&lig_iter))
4260 return FALSE;
4262 lig_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->lig_coverage),
4263 context->u.pos.glyphs[lig_iter.pos]);
4264 if (lig_index == GLYPH_NOT_COVERED) return FALSE;
4266 class_count = GET_BE_WORD(format1->mark_class_count);
4268 lig_array = GET_BE_WORD(format1->lig_array);
4270 if (!table_read_array_be_word(table, subtable_offset + lig_array, lig_index, &lig_attach)) return FALSE;
4272 comp_count = table_read_be_word(table, subtable_offset + lig_array + lig_attach);
4273 if (!comp_count) return FALSE;
4275 comp_index = context->u.buffer.glyph_props[lig_iter.pos].components -
4276 context->u.buffer.glyph_props[context->cur].lig_component - 1;
4277 if (comp_index >= comp_count) return FALSE;
4279 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4280 comp_index, lig_array + lig_attach, class_count, lig_iter.pos);
4282 else
4283 WARN("Unknown mark-to-ligature format %u.\n", format);
4285 return FALSE;
4288 static BOOL opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context *context,
4289 const struct lookup *lookup, unsigned int subtable_offset)
4291 const struct dwrite_fonttable *table = &context->table->table;
4292 WORD format;
4294 format = table_read_be_word(table, subtable_offset);
4296 if (format == 1)
4298 const struct ot_gpos_mark_to_mark_format1 *format1;
4299 unsigned int mark1_index, mark2_index;
4300 struct glyph_iterator mark_iter;
4302 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4304 mark1_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark1_coverage),
4305 context->u.pos.glyphs[context->cur]);
4306 if (mark1_index == GLYPH_NOT_COVERED) return FALSE;
4308 glyph_iterator_init(context, lookup->flags & ~LOOKUP_FLAG_IGNORE_MASK, context->cur, 1, &mark_iter);
4309 if (!glyph_iterator_prev(&mark_iter))
4310 return FALSE;
4312 if (!context->u.pos.glyph_props[mark_iter.pos].isDiacritic)
4313 return FALSE;
4315 mark2_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark2_coverage),
4316 context->u.pos.glyphs[mark_iter.pos]);
4317 if (mark2_index == GLYPH_NOT_COVERED) return FALSE;
4319 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark1_array), mark1_index,
4320 mark2_index, GET_BE_WORD(format1->mark2_array), GET_BE_WORD(format1->mark_class_count), mark_iter.pos);
4322 else
4324 WARN("Unknown mark-to-mark format %u.\n", format);
4325 return FALSE;
4328 return TRUE;
4331 static unsigned int opentype_layout_adjust_extension_subtable(struct scriptshaping_context *context,
4332 unsigned int *subtable_offset)
4334 const struct ot_gsubgpos_extension_format1 *format1;
4336 if (!(format1 = table_read_ensure(&context->table->table, *subtable_offset, sizeof(*format1))))
4337 return 0;
4339 if (GET_BE_WORD(format1->format) != 1)
4341 WARN("Unexpected extension table format %#x.\n", format1->format);
4342 return 0;
4345 *subtable_offset = *subtable_offset + GET_BE_DWORD(format1->extension_offset);
4347 return GET_BE_WORD(format1->lookup_type);
4350 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
4351 unsigned int subtable_offset);
4352 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
4353 unsigned int subtable_offset);
4355 static BOOL opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
4357 unsigned int i, lookup_type;
4358 BOOL ret = FALSE;
4360 for (i = 0; i < lookup->subtable_count; ++i)
4362 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
4364 if (lookup->type == GPOS_LOOKUP_EXTENSION_POSITION)
4366 lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset);
4367 if (!lookup_type)
4368 continue;
4370 else
4371 lookup_type = lookup->type;
4373 switch (lookup_type)
4375 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
4376 ret = opentype_layout_apply_gpos_single_adjustment(context, lookup, subtable_offset);
4377 break;
4378 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
4379 ret = opentype_layout_apply_gpos_pair_adjustment(context, lookup, subtable_offset);
4380 break;
4381 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
4382 ret = opentype_layout_apply_gpos_cursive_attachment(context, lookup, subtable_offset);
4383 break;
4384 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
4385 ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, lookup, subtable_offset);
4386 break;
4387 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
4388 ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, lookup, subtable_offset);
4389 break;
4390 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
4391 ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, lookup, subtable_offset);
4392 break;
4393 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
4394 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
4395 break;
4396 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
4397 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
4398 break;
4399 case GPOS_LOOKUP_EXTENSION_POSITION:
4400 WARN("Recursive extension lookup.\n");
4401 break;
4402 default:
4403 WARN("Unknown lookup type %u.\n", lookup_type);
4406 if (ret)
4407 break;
4410 return ret;
4413 struct lookups
4415 struct lookup *lookups;
4416 size_t capacity;
4417 size_t count;
4420 static int lookups_sorting_compare(const void *a, const void *b)
4422 const struct lookup *left = (const struct lookup *)a;
4423 const struct lookup *right = (const struct lookup *)b;
4424 return left->index < right->index ? -1 : left->index > right->index ? 1 : 0;
4427 static BOOL opentype_layout_init_lookup(const struct ot_gsubgpos_table *table, unsigned short lookup_index, unsigned int mask,
4428 struct lookup *lookup)
4430 unsigned short subtable_count, lookup_type, flags, mark_filtering_set;
4431 const struct ot_lookup_table *lookup_table;
4432 unsigned int offset;
4434 if (!(offset = table_read_be_word(&table->table, table->lookup_list +
4435 FIELD_OFFSET(struct ot_lookup_list, lookup[lookup_index]))))
4437 return FALSE;
4440 offset += table->lookup_list;
4442 if (!(lookup_table = table_read_ensure(&table->table, offset, sizeof(*lookup_table))))
4443 return FALSE;
4445 if (!(subtable_count = GET_BE_WORD(lookup_table->subtable_count)))
4446 return FALSE;
4448 lookup_type = GET_BE_WORD(lookup_table->lookup_type);
4449 flags = GET_BE_WORD(lookup_table->flags);
4451 if (flags & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
4453 mark_filtering_set = table_read_be_word(&table->table, offset +
4454 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable_count]));
4455 flags |= mark_filtering_set << 16;
4458 lookup->index = lookup_index;
4459 lookup->type = lookup_type;
4460 lookup->flags = flags;
4461 lookup->subtable_count = subtable_count;
4462 lookup->mask = mask;
4463 lookup->offset = offset;
4465 return TRUE;
4468 static void opentype_layout_add_lookups(const struct ot_feature_list *feature_list, UINT16 total_lookup_count,
4469 const struct ot_gsubgpos_table *table, struct shaping_feature *feature, struct lookups *lookups)
4471 UINT16 feature_offset, lookup_count;
4472 unsigned int i;
4474 /* Feature wasn't found */
4475 if (feature->index == 0xffff)
4476 return;
4478 feature_offset = GET_BE_WORD(feature_list->features[feature->index].offset);
4480 lookup_count = table_read_be_word(&table->table, table->feature_list + feature_offset +
4481 FIELD_OFFSET(struct ot_feature, lookup_count));
4482 if (!lookup_count)
4483 return;
4485 if (!dwrite_array_reserve((void **)&lookups->lookups, &lookups->capacity, lookups->count + lookup_count,
4486 sizeof(*lookups->lookups)))
4488 return;
4491 for (i = 0; i < lookup_count; ++i)
4493 UINT16 lookup_index = table_read_be_word(&table->table, table->feature_list + feature_offset +
4494 FIELD_OFFSET(struct ot_feature, lookuplist_index[i]));
4496 if (lookup_index >= total_lookup_count)
4497 continue;
4499 if (opentype_layout_init_lookup(table, lookup_index, feature->mask, &lookups->lookups[lookups->count]))
4500 lookups->count++;
4504 static void opentype_layout_collect_lookups(struct scriptshaping_context *context, unsigned int script_index,
4505 unsigned int language_index, const struct shaping_features *features, const struct ot_gsubgpos_table *table,
4506 struct lookups *lookups)
4508 unsigned int last_num_lookups = 0, stage, script_feature_count = 0;
4509 UINT16 total_feature_count, total_lookup_count;
4510 struct shaping_feature required_feature = { 0 };
4511 const struct ot_feature_list *feature_list;
4512 const struct ot_langsys *langsys = NULL;
4513 struct shaping_feature *feature;
4514 unsigned int i, j, next_bit;
4515 unsigned int global_bit_shift = 1;
4516 unsigned int global_bit_mask = 2;
4517 UINT16 feature_index;
4519 if (!table->table.data)
4520 return;
4522 if (script_index != ~0u)
4524 unsigned int table_offset, langsys_offset;
4526 /* ScriptTable offset. */
4527 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
4528 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
4529 if (!table_offset)
4530 return;
4532 if (language_index == ~0u)
4533 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
4534 else
4535 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
4536 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
4537 FIELD_OFFSET(struct ot_langsys_record, langsys));
4538 langsys_offset += table->script_list + table_offset;
4540 script_feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
4541 if (script_feature_count)
4542 langsys = table_read_ensure(&table->table, langsys_offset,
4543 FIELD_OFFSET(struct ot_langsys, feature_index[script_feature_count]));
4544 if (!langsys)
4545 script_feature_count = 0;
4548 total_feature_count = table_read_be_word(&table->table, table->feature_list);
4549 if (!total_feature_count)
4550 return;
4552 total_lookup_count = table_read_be_word(&table->table, table->lookup_list);
4553 if (!total_lookup_count)
4554 return;
4556 feature_list = table_read_ensure(&table->table, table->feature_list,
4557 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
4558 if (!feature_list)
4559 return;
4561 /* Required feature. */
4562 required_feature.index = langsys ? GET_BE_WORD(langsys->required_feature_index) : 0xffff;
4563 if (required_feature.index < total_feature_count)
4564 required_feature.tag = feature_list->features[required_feature.index].tag;
4565 required_feature.mask = global_bit_mask;
4567 context->global_mask = global_bit_mask;
4568 next_bit = global_bit_shift + 1;
4569 for (i = 0; i < features->count; ++i)
4571 unsigned int bits_needed;
4572 BOOL found = FALSE;
4574 feature = &features->features[i];
4576 feature->index = 0xffff;
4578 if ((feature->flags & FEATURE_GLOBAL) && feature->max_value == 1)
4579 bits_needed = 0;
4580 else
4581 BitScanReverse(&bits_needed, min(feature->max_value, 256));
4583 if (!feature->max_value || next_bit + bits_needed > 8 * sizeof (feature->mask))
4584 continue;
4586 if (required_feature.tag == feature->tag)
4587 required_feature.stage = feature->stage;
4589 for (j = 0; j < script_feature_count; ++j)
4591 feature_index = GET_BE_WORD(langsys->feature_index[j]);
4592 if (feature_index >= total_feature_count)
4593 continue;
4594 if ((found = feature_list->features[feature_index].tag == feature->tag))
4596 feature->index = feature_index;
4597 break;
4601 if (!found && (features->features[i].flags & FEATURE_GLOBAL_SEARCH))
4603 for (j = 0; j < total_feature_count; ++j)
4605 if ((found = (feature_list->features[j].tag == feature->tag)))
4607 feature->index = j;
4608 break;
4613 if (!found)
4614 continue;
4616 if (feature->flags & FEATURE_GLOBAL && feature->max_value == 1)
4618 feature->shift = global_bit_shift;
4619 feature->mask = global_bit_mask;
4621 else
4623 feature->shift = next_bit;
4624 feature->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
4625 next_bit += bits_needed;
4626 context->global_mask |= (feature->default_value << feature->shift) & feature->mask;
4630 for (stage = 0; stage <= features->stage; ++stage)
4632 if (required_feature.index != 0xffff && required_feature.stage == stage)
4633 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &required_feature, lookups);
4635 for (i = 0; i < features->count; ++i)
4637 if (features->features[i].stage == stage)
4638 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &features->features[i], lookups);
4641 /* Sort and merge lookups for current stage. */
4642 if (last_num_lookups < lookups->count)
4644 qsort(lookups->lookups + last_num_lookups, lookups->count - last_num_lookups, sizeof(*lookups->lookups),
4645 lookups_sorting_compare);
4647 j = last_num_lookups;
4648 for (i = j + 1; i < lookups->count; ++i)
4650 if (lookups->lookups[i].index != lookups->lookups[j].index)
4652 lookups->lookups[++j] = lookups->lookups[i];
4654 else
4656 lookups->lookups[j].mask |= lookups->lookups[i].mask;
4659 lookups->count = j + 1;
4662 last_num_lookups = lookups->count;
4666 static int feature_search_compare(const void *a, const void* b)
4668 unsigned int tag = *(unsigned int *)a;
4669 const struct shaping_feature *feature = b;
4671 return tag < feature->tag ? -1 : tag > feature->tag ? 1 : 0;
4674 static unsigned int shaping_features_get_mask(const struct shaping_features *features, unsigned int tag, unsigned int *shift)
4676 struct shaping_feature *feature;
4678 feature = bsearch(&tag, features->features, features->count, sizeof(*features->features), feature_search_compare);
4680 if (!feature || feature->index == 0xffff)
4681 return 0;
4683 if (shift) *shift = feature->shift;
4684 return feature->mask;
4687 static void opentype_layout_get_glyph_range_for_text(struct scriptshaping_context *context, unsigned int start_char,
4688 unsigned int end_char, unsigned int *start_glyph, unsigned int *end_glyph)
4690 *start_glyph = context->u.buffer.clustermap[start_char];
4691 if (end_char >= context->length - 1)
4692 *end_glyph = context->glyph_count - 1;
4693 else
4694 *end_glyph = context->u.buffer.clustermap[end_char + 1] - 1;
4697 static void opentype_layout_set_glyph_masks(struct scriptshaping_context *context, const struct shaping_features *features)
4699 const DWRITE_TYPOGRAPHIC_FEATURES **user_features = context->user_features.features;
4700 unsigned int f, r, g, start_char, mask, shift, value;
4702 for (g = 0; g < context->glyph_count; ++g)
4703 context->glyph_infos[g].mask = context->global_mask;
4705 /* FIXME: set shaper masks */
4707 for (r = 0, start_char = 0; r < context->user_features.range_count; ++r)
4709 unsigned int start_glyph, end_glyph;
4711 if (start_char >= context->length)
4712 break;
4714 opentype_layout_get_glyph_range_for_text(context, start_char, start_char + context->user_features.range_lengths[r],
4715 &start_glyph, &end_glyph);
4716 start_char += context->user_features.range_lengths[r];
4718 if (start_glyph > end_glyph || end_glyph >= context->glyph_count)
4719 continue;
4721 for (f = 0; f < user_features[r]->featureCount; ++f)
4723 mask = shaping_features_get_mask(features, user_features[r]->features[f].nameTag, &shift);
4724 if (!mask)
4725 continue;
4727 value = (user_features[r]->features[f].parameter << shift) & mask;
4729 for (g = start_glyph; g <= end_glyph; ++g)
4730 context->glyph_infos[g].mask = (context->glyph_infos[g].mask & ~mask) | value;
4735 static void opentype_layout_apply_gpos_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
4737 struct lookup lookup = { 0 };
4738 /* Feature mask is intentionally zero, it's not used outside of main loop. */
4739 if (opentype_layout_init_lookup(context->table, lookup_index, 0, &lookup))
4740 opentype_layout_apply_gpos_lookup(context, &lookup);
4743 void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index,
4744 unsigned int language_index, const struct shaping_features *features)
4746 struct lookups lookups = { 0 };
4747 unsigned int i;
4748 BOOL ret;
4750 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
4751 context->u.buffer.apply_context_lookup = opentype_layout_apply_gpos_context_lookup;
4752 opentype_layout_collect_lookups(context, script_index, language_index, features, &context->cache->gpos, &lookups);
4754 for (i = 0; i < context->glyph_count; ++i)
4755 opentype_set_glyph_props(context, i);
4756 opentype_layout_set_glyph_masks(context, features);
4758 for (i = 0; i < lookups.count; ++i)
4760 const struct lookup *lookup = &lookups.lookups[i];
4762 context->cur = 0;
4763 context->lookup_mask = lookup->mask;
4765 while (context->cur < context->glyph_count)
4767 ret = FALSE;
4769 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
4770 lookup_is_glyph_match(context, context->cur, lookup->flags))
4772 ret = opentype_layout_apply_gpos_lookup(context, lookup);
4775 if (!ret)
4776 context->cur++;
4780 heap_free(lookups.lookups);
4783 static void opentype_layout_replace_glyph(struct scriptshaping_context *context, UINT16 glyph)
4785 UINT16 orig_glyph = context->u.subst.glyphs[context->cur];
4786 if (glyph != orig_glyph)
4788 context->u.subst.glyphs[context->cur] = glyph;
4789 opentype_set_subst_glyph_props(context, context->cur);
4793 static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4794 unsigned int subtable_offset)
4796 const struct dwrite_fonttable *table = &context->table->table;
4797 UINT16 format, coverage, orig_glyph, glyph;
4798 unsigned int coverage_index;
4800 orig_glyph = glyph = context->u.subst.glyphs[context->cur];
4802 format = table_read_be_word(table, subtable_offset);
4804 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, coverage));
4806 if (format == 1)
4808 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4809 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4811 glyph = orig_glyph + table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, delta));
4813 else if (format == 2)
4815 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4816 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4818 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count),
4819 coverage_index, &glyph))
4821 return FALSE;
4824 else
4826 WARN("Unknown single substitution format %u.\n", format);
4827 return FALSE;
4830 opentype_layout_replace_glyph(context, glyph);
4831 context->cur++;
4833 return TRUE;
4836 static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *context, unsigned int count)
4838 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
4839 struct shaping_glyph_info *glyph_infos;
4840 unsigned int new_capacity;
4841 UINT16 *glyphs;
4842 BOOL ret;
4844 if (context->u.subst.capacity >= count)
4845 return TRUE;
4847 new_capacity = context->u.subst.capacity * 2;
4849 if ((glyphs = heap_realloc(context->u.subst.glyphs, new_capacity * sizeof(*glyphs))))
4850 context->u.subst.glyphs = glyphs;
4851 if ((glyph_props = heap_realloc(context->u.subst.glyph_props, new_capacity * sizeof(*glyph_props))))
4852 context->u.subst.glyph_props = glyph_props;
4853 if ((glyph_infos = heap_realloc(context->glyph_infos, new_capacity * sizeof(*glyph_infos))))
4854 context->glyph_infos = glyph_infos;
4856 if ((ret = (glyphs && glyph_props && glyph_infos)))
4857 context->u.subst.capacity = new_capacity;
4859 return ret;
4862 static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4863 unsigned int subtable_offset)
4865 const struct dwrite_fonttable *table = &context->table->table;
4866 UINT16 format, coverage, glyph, glyph_count;
4867 unsigned int i, idx, coverage_index;
4868 const UINT16 *glyphs;
4870 idx = context->cur;
4871 glyph = context->u.subst.glyphs[idx];
4873 format = table_read_be_word(table, subtable_offset);
4875 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, coverage));
4877 if (format == 1)
4879 UINT16 seq_offset;
4881 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4882 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4884 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, seq_count),
4885 coverage_index, &seq_offset))
4887 return FALSE;
4890 if (!(glyphs = table_read_array_be_word(table, subtable_offset + seq_offset, ~0u, &glyph_count))) return FALSE;
4892 if (glyph_count == 1)
4894 /* Equivalent of single substitution. */
4895 opentype_layout_replace_glyph(context, GET_BE_WORD(glyphs[0]));
4896 context->cur++;
4898 else if (glyph_count == 0)
4900 context->cur++;
4902 else
4904 unsigned int shift_len, src_idx, dest_idx, mask;
4906 /* Current glyph is also replaced. */
4907 glyph_count--;
4909 if (!(opentype_layout_gsub_ensure_buffer(context, context->glyph_count + glyph_count)))
4910 return FALSE;
4912 shift_len = context->cur + 1 < context->glyph_count ? context->glyph_count - context->cur - 1 : 0;
4914 if (shift_len)
4916 src_idx = context->cur + 1;
4917 dest_idx = src_idx + glyph_count;
4919 memmove(&context->u.subst.glyphs[dest_idx], &context->u.subst.glyphs[src_idx],
4920 shift_len * sizeof(*context->u.subst.glyphs));
4921 memmove(&context->u.subst.glyph_props[dest_idx], &context->u.subst.glyph_props[src_idx],
4922 shift_len * sizeof(*context->u.subst.glyph_props));
4923 memmove(&context->glyph_infos[dest_idx], &context->glyph_infos[src_idx],
4924 shift_len * sizeof(*context->glyph_infos));
4927 mask = context->glyph_infos[context->cur].mask;
4928 for (i = 0, idx = context->cur; i <= glyph_count; ++i)
4930 glyph = GET_BE_WORD(glyphs[i]);
4931 context->u.subst.glyphs[idx + i] = glyph;
4932 if (i)
4934 context->u.subst.glyph_props[idx + i].isClusterStart = 0;
4935 context->u.buffer.glyph_props[idx + i].components = 0;
4936 context->glyph_infos[idx + i].start_text_idx = 0;
4938 opentype_set_subst_glyph_props(context, idx + i);
4939 /* Inherit feature mask from original matched glyph. */
4940 context->glyph_infos[idx + i].mask = mask;
4943 context->cur += glyph_count + 1;
4944 context->glyph_count += glyph_count;
4947 else
4949 WARN("Unknown multiple substitution format %u.\n", format);
4950 return FALSE;
4953 return TRUE;
4956 static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4957 unsigned int subtable_offset)
4959 const struct dwrite_fonttable *table = &context->table->table;
4960 unsigned int idx, coverage_index;
4961 UINT16 format, coverage, glyph;
4963 idx = context->cur;
4964 glyph = context->u.subst.glyphs[idx];
4966 format = table_read_be_word(table, subtable_offset);
4968 if (format == 1)
4970 const struct ot_gsub_altsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
4971 unsigned int shift, alt_index;
4972 UINT16 set_offset;
4974 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, coverage));
4976 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4977 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4979 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, count),
4980 coverage_index, &set_offset))
4981 return FALSE;
4983 /* Argument is 1-based. */
4984 BitScanForward(&shift, context->lookup_mask);
4985 alt_index = (context->lookup_mask & context->glyph_infos[idx].mask) >> shift;
4986 if (!alt_index) return FALSE;
4988 if (!table_read_array_be_word(table, subtable_offset + set_offset, alt_index - 1, &glyph)) return FALSE;
4990 else
4992 WARN("Unexpected alternate substitution format %d.\n", format);
4993 return FALSE;
4996 opentype_layout_replace_glyph(context, glyph);
4997 context->cur++;
4999 return TRUE;
5002 static BOOL opentype_layout_context_match_input(const struct match_context *mc, unsigned int count, const UINT16 *input,
5003 unsigned int *end_offset, unsigned int *match_positions)
5005 struct match_data match_data = { .mc = mc, .subtable_offset = mc->input_offset };
5006 struct scriptshaping_context *context = mc->context;
5007 struct glyph_iterator iter;
5008 unsigned int i;
5010 if (count > GLYPH_CONTEXT_MAX_LENGTH)
5011 return FALSE;
5013 match_positions[0] = context->cur;
5015 glyph_iterator_init(context, mc->lookup->flags, context->cur, count - 1, &iter);
5016 iter.mask = context->lookup_mask;
5017 iter.match_func = mc->match_func;
5018 iter.match_data = &match_data;
5019 iter.glyph_data = input;
5021 for (i = 1; i < count; ++i)
5023 if (!glyph_iterator_next(&iter))
5024 return FALSE;
5026 match_positions[i] = iter.pos;
5029 *end_offset = iter.pos - context->cur + 1;
5031 return TRUE;
5034 static void opentype_layout_unsafe_to_break(struct scriptshaping_context *context, unsigned int idx)
5036 if (context->u.buffer.glyph_props[idx].isClusterStart)
5037 context->u.buffer.text_props[context->glyph_infos[idx].start_text_idx].canBreakShapingAfter = 0;
5040 static void opentype_layout_delete_glyph(struct scriptshaping_context *context, unsigned int idx)
5042 unsigned int shift_len;
5044 shift_len = context->glyph_count - context->cur - 1;
5046 if (shift_len)
5048 memmove(&context->u.buffer.glyphs[idx], &context->u.buffer.glyphs[idx + 1],
5049 shift_len * sizeof(*context->u.buffer.glyphs));
5050 memmove(&context->u.buffer.glyph_props[idx], &context->u.buffer.glyph_props[idx + 1],
5051 shift_len * sizeof(*context->u.buffer.glyph_props));
5052 memmove(&context->glyph_infos[idx], &context->glyph_infos[idx + 1], shift_len * sizeof(*context->glyph_infos));
5055 context->glyph_count--;
5058 static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset,
5059 const struct lookup *lookup)
5061 struct match_context mc = { .context = context, .lookup = lookup, .match_func = opentype_match_glyph_func };
5062 const struct dwrite_fonttable *gsub = &context->table->table;
5063 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5064 unsigned int i, j, comp_count, match_length = 0;
5065 const struct ot_gsub_lig *lig;
5066 UINT16 lig_glyph;
5068 comp_count = table_read_be_word(gsub, offset + FIELD_OFFSET(struct ot_gsub_lig, comp_count));
5070 if (!comp_count)
5071 return FALSE;
5073 lig = table_read_ensure(gsub, offset, FIELD_OFFSET(struct ot_gsub_lig, components[comp_count-1]));
5074 if (!lig)
5075 return FALSE;
5077 lig_glyph = GET_BE_WORD(lig->lig_glyph);
5079 if (comp_count == 1)
5081 opentype_layout_replace_glyph(context, lig_glyph);
5082 context->cur++;
5083 return TRUE;
5086 if (!opentype_layout_context_match_input(&mc, comp_count, lig->components, &match_length, match_positions))
5087 return FALSE;
5089 opentype_layout_replace_glyph(context, lig_glyph);
5090 context->u.buffer.glyph_props[context->cur].components = comp_count;
5092 /* Positioning against a ligature implies keeping track of ligature component
5093 glyph should be attached to. Update per-glyph property for interleaving glyphs,
5094 0 means attaching to last component, n - attaching to n-th glyph before last. */
5095 for (i = 1; i < comp_count; ++i)
5097 j = match_positions[i - 1] + 1;
5098 while (j < match_positions[i])
5100 context->u.buffer.glyph_props[j++].lig_component = comp_count - i;
5102 opentype_layout_unsafe_to_break(context, i);
5103 context->u.buffer.glyph_props[i].isClusterStart = 0;
5104 context->glyph_infos[i].start_text_idx = 0;
5107 /* Delete ligated glyphs, backwards to preserve index. */
5108 for (i = 1; i < comp_count; ++i)
5110 opentype_layout_delete_glyph(context, match_positions[comp_count - i]);
5113 /* Skip whole matched sequence, accounting for deleted glyphs. */
5114 context->cur += match_length - (comp_count - 1);
5116 return TRUE;
5119 static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5120 unsigned int subtable_offset)
5122 const struct dwrite_fonttable *table = &context->table->table;
5123 UINT16 format, coverage, glyph, lig_set_offset;
5124 unsigned int coverage_index;
5126 glyph = context->u.subst.glyphs[context->cur];
5128 format = table_read_be_word(table, subtable_offset);
5130 if (format == 1)
5132 const struct ot_gsub_ligsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
5133 unsigned int i;
5134 const UINT16 *offsets;
5135 UINT16 lig_count;
5137 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, coverage));
5139 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5140 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5142 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_set_count),
5143 coverage_index, &lig_set_offset))
5144 return FALSE;
5146 if (!(offsets = table_read_array_be_word(table, subtable_offset + lig_set_offset, ~0u, &lig_count)))
5147 return FALSE;
5149 /* First applicable ligature is used. */
5150 for (i = 0; i < lig_count; ++i)
5152 if (opentype_layout_apply_ligature(context, subtable_offset + lig_set_offset + GET_BE_WORD(offsets[i]), lookup))
5153 return TRUE;
5156 else
5157 WARN("Unexpected ligature substitution format %d.\n", format);
5159 return FALSE;
5162 static BOOL opentype_layout_context_match_backtrack(const struct match_context *mc, unsigned int count,
5163 const UINT16 *backtrack, unsigned int *match_start)
5165 struct match_data match_data = { .mc = mc, .subtable_offset = mc->backtrack_offset };
5166 struct scriptshaping_context *context = mc->context;
5167 struct glyph_iterator iter;
5168 unsigned int i;
5170 glyph_iterator_init(context, mc->lookup->flags, context->cur, count, &iter);
5171 iter.match_func = mc->match_func;
5172 iter.match_data = &match_data;
5173 iter.glyph_data = backtrack;
5175 for (i = 0; i < count; ++i)
5177 if (!glyph_iterator_prev(&iter))
5178 return FALSE;
5181 *match_start = iter.pos;
5183 return TRUE;
5186 static BOOL opentype_layout_context_match_lookahead(const struct match_context *mc, unsigned int count,
5187 const UINT16 *lookahead, unsigned int offset, unsigned int *end_index)
5189 struct match_data match_data = { .mc = mc, .subtable_offset = mc->lookahead_offset };
5190 struct scriptshaping_context *context = mc->context;
5191 struct glyph_iterator iter;
5192 unsigned int i;
5194 glyph_iterator_init(context, mc->lookup->flags, context->cur + offset - 1, count, &iter);
5195 iter.match_func = mc->match_func;
5196 iter.match_data = &match_data;
5197 iter.glyph_data = lookahead;
5199 for (i = 0; i < count; ++i)
5201 if (!glyph_iterator_next(&iter))
5202 return FALSE;
5205 *end_index = iter.pos;
5207 return TRUE;
5210 static BOOL opentype_layout_context_apply_lookup(struct scriptshaping_context *context, unsigned int count,
5211 unsigned int *match_positions, unsigned int lookup_count, const UINT16 *lookup_records, unsigned int match_length)
5213 unsigned int i, j;
5214 int end, delta;
5216 if (!context->nesting_level_left)
5217 return TRUE;
5219 end = context->cur + match_length;
5221 for (i = 0; i < lookup_count; ++i)
5223 unsigned int idx = GET_BE_WORD(lookup_records[i]);
5224 unsigned int orig_len, lookup_index, next;
5226 if (idx >= count)
5227 continue;
5229 context->cur = match_positions[idx];
5231 orig_len = context->glyph_count;
5233 lookup_index = GET_BE_WORD(lookup_records[i+1]);
5235 --context->nesting_level_left;
5236 context->u.buffer.apply_context_lookup(context, lookup_index);
5237 ++context->nesting_level_left;
5239 delta = context->glyph_count - orig_len;
5240 if (!delta)
5241 continue;
5243 end += delta;
5244 if (end <= (int)match_positions[idx])
5246 end = match_positions[idx];
5247 break;
5250 next = idx + 1;
5252 if (delta > 0)
5254 if (delta + count > GLYPH_CONTEXT_MAX_LENGTH)
5255 break;
5257 else
5259 delta = max(delta, (int)next - (int)count);
5260 next -= delta;
5263 memmove(match_positions + next + delta, match_positions + next,
5264 (count - next) * sizeof (*match_positions));
5265 next += delta;
5266 count += delta;
5268 for (j = idx + 1; j < next; j++)
5269 match_positions[j] = match_positions[j - 1] + 1;
5271 for (; next < count; next++)
5272 match_positions[next] += delta;
5275 context->cur = end;
5277 return TRUE;
5280 static BOOL opentype_layout_apply_chain_context_match(unsigned int backtrack_count, const UINT16 *backtrack,
5281 unsigned int input_count, const UINT16 *input, unsigned int lookahead_count, const UINT16 *lookahead,
5282 unsigned int lookup_count, const UINT16 *lookup_records, const struct match_context *mc)
5284 unsigned int start_index = 0, match_length = 0, end_index = 0;
5285 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5287 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5288 opentype_layout_context_match_backtrack(mc, backtrack_count, backtrack, &start_index) &&
5289 opentype_layout_context_match_lookahead(mc, lookahead_count, lookahead, input_count, &end_index) &&
5290 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count, lookup_records, match_length);
5293 static BOOL opentype_layout_apply_chain_rule_set(const struct match_context *mc, unsigned int offset)
5295 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5296 const struct dwrite_fonttable *table = &mc->context->table->table;
5297 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5298 const struct ot_gsubgpos_ruleset *ruleset;
5299 unsigned int i, count;
5301 count = table_read_be_word(table, offset);
5302 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5304 for (i = 0; i < count; ++i)
5306 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5308 backtrack_count = table_read_be_word(table, rule_offset);
5309 rule_offset += 2;
5310 backtrack = table_read_ensure(table, rule_offset, backtrack_count * sizeof(*backtrack));
5311 rule_offset += backtrack_count * sizeof(*backtrack);
5313 if (!(input_count = table_read_be_word(table, rule_offset)))
5314 continue;
5316 rule_offset += 2;
5317 input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input));
5318 rule_offset += (input_count - 1) * sizeof(*input);
5320 lookahead_count = table_read_be_word(table, rule_offset);
5321 rule_offset += 2;
5322 lookahead = table_read_ensure(table, rule_offset, lookahead_count * sizeof(*lookahead));
5323 rule_offset += lookahead_count * sizeof(*lookahead);
5325 lookup_count = table_read_be_word(table, rule_offset);
5326 rule_offset += 2;
5327 lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records));
5329 /* First applicable rule is used. */
5330 if (opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input, lookahead_count,
5331 lookahead, lookup_count, lookup_records, mc))
5333 return TRUE;
5337 return FALSE;
5340 static BOOL opentype_layout_apply_context_match(unsigned int input_count, const UINT16 *input, unsigned int lookup_count,
5341 const UINT16 *lookup_records, const struct match_context *mc)
5343 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5344 unsigned int match_length = 0;
5346 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5347 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count,
5348 lookup_records, match_length);
5351 static BOOL opentype_layout_apply_rule_set(const struct match_context *mc, unsigned int offset)
5353 unsigned int input_count, lookup_count;
5354 const struct dwrite_fonttable *table = &mc->context->table->table;
5355 const UINT16 *input, *lookup_records;
5356 const struct ot_gsubgpos_ruleset *ruleset;
5357 unsigned int i, count;
5359 count = table_read_be_word(table, offset);
5360 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5362 for (i = 0; i < count; ++i)
5364 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5366 if (!(input_count = table_read_be_word(table, rule_offset)))
5367 continue;
5368 rule_offset += 2;
5370 if (!(lookup_count = table_read_be_word(table, rule_offset)))
5371 continue;
5372 rule_offset += 2;
5374 if (!(input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input))))
5375 continue;
5376 rule_offset += (input_count - 1) * sizeof(*input);
5378 if (!(lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records))))
5379 continue;
5381 /* First applicable rule is used. */
5382 if (opentype_layout_apply_context_match(input_count, input, lookup_count, lookup_records, mc))
5383 return TRUE;
5386 return FALSE;
5389 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
5390 unsigned int subtable_offset)
5392 struct match_context mc = { .context = context, .lookup = lookup };
5393 const struct dwrite_fonttable *table = &context->table->table;
5394 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5395 UINT16 glyph, format, coverage;
5396 BOOL ret = FALSE;
5398 glyph = context->u.subst.glyphs[context->cur];
5400 format = table_read_be_word(table, subtable_offset);
5402 if (format == 1)
5404 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5406 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5407 if (coverage_index == GLYPH_NOT_COVERED)
5408 return FALSE;
5410 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5411 if (coverage_index >= count)
5412 return FALSE;
5414 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5415 rulesets[coverage_index]));
5416 offset += subtable_offset;
5418 mc.match_func = opentype_match_glyph_func;
5420 ret = opentype_layout_apply_rule_set(&mc, offset);
5422 else if (format == 2)
5424 unsigned int input_classdef, rule_set_idx;
5426 offset = subtable_offset + 2 /* format */;
5428 coverage = table_read_be_word(table, offset);
5429 offset += 2;
5431 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5432 if (coverage_index == GLYPH_NOT_COVERED)
5433 return FALSE;
5435 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5436 offset += 2;
5438 count = table_read_be_word(table, offset);
5439 offset+= 2;
5441 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5442 if (rule_set_idx >= count)
5443 return FALSE;
5445 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5446 offset += subtable_offset;
5448 mc.input_offset = input_classdef;
5449 mc.match_func = opentype_match_class_func;
5451 ret = opentype_layout_apply_rule_set(&mc, offset);
5453 else if (format == 3)
5455 unsigned int input_count, lookup_count;
5456 const UINT16 *input, *lookup_records;
5458 offset = subtable_offset + 2 /* format */;
5460 input_count = table_read_be_word(table, offset);
5461 offset += 2;
5463 if (!input_count)
5464 return FALSE;
5466 lookup_count = table_read_be_word(table, offset);
5467 offset += 2;
5469 if (!(input = table_read_ensure(table, offset, sizeof(*input) * input_count)))
5470 return FALSE;
5471 offset += sizeof(*input) * input_count;
5473 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5474 if (coverage_index == GLYPH_NOT_COVERED)
5475 return FALSE;
5477 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5479 mc.input_offset = subtable_offset;
5480 mc.match_func = opentype_match_coverage_func;
5482 ret = opentype_layout_apply_context_match(input_count, input + 1, lookup_count, lookup_records, &mc);
5484 else
5485 WARN("Unknown contextual substitution format %u.\n", format);
5487 return ret;
5490 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
5491 unsigned int subtable_offset)
5493 struct match_context mc = { .context = context, .lookup = lookup };
5494 const struct dwrite_fonttable *table = &context->table->table;
5495 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5496 UINT16 glyph, format, coverage;
5497 BOOL ret = FALSE;
5499 glyph = context->u.subst.glyphs[context->cur];
5501 format = table_read_be_word(table, subtable_offset);
5503 if (format == 1)
5505 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5507 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5508 if (coverage_index == GLYPH_NOT_COVERED)
5509 return FALSE;
5511 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5512 if (coverage_index >= count)
5513 return FALSE;
5515 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5516 rulesets[coverage_index]));
5517 offset += subtable_offset;
5519 mc.match_func = opentype_match_glyph_func;
5521 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5523 else if (format == 2)
5525 unsigned int backtrack_classdef, input_classdef, lookahead_classdef, rule_set_idx;
5527 offset = subtable_offset + 2 /* format */;
5529 coverage = table_read_be_word(table, offset);
5530 offset += 2;
5532 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5533 if (coverage_index == GLYPH_NOT_COVERED)
5534 return FALSE;
5536 backtrack_classdef = table_read_be_word(table, offset) + subtable_offset;
5537 offset += 2;
5539 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5540 offset += 2;
5542 lookahead_classdef = table_read_be_word(table, offset) + subtable_offset;
5543 offset += 2;
5545 count = table_read_be_word(table, offset);
5546 offset+= 2;
5548 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5549 if (rule_set_idx >= count)
5550 return FALSE;
5552 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5553 offset += subtable_offset;
5555 mc.backtrack_offset = backtrack_classdef;
5556 mc.input_offset = input_classdef;
5557 mc.lookahead_offset = lookahead_classdef;
5558 mc.match_func = opentype_match_class_func;
5560 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5562 else if (format == 3)
5564 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5565 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5567 offset = subtable_offset + 2 /* format */;
5569 backtrack_count = table_read_be_word(table, offset);
5570 offset += 2;
5571 backtrack = table_read_ensure(table, offset, backtrack_count * sizeof(*backtrack));
5572 offset += backtrack_count * sizeof(*backtrack);
5574 input_count = table_read_be_word(table, offset);
5575 offset += 2;
5576 input = table_read_ensure(table, offset, input_count * sizeof(*input));
5577 offset += input_count * sizeof(*input);
5579 lookahead_count = table_read_be_word(table, offset);
5580 offset += 2;
5581 lookahead = table_read_ensure(table, offset, lookahead_count * sizeof(*lookahead));
5582 offset += lookahead_count * sizeof(*lookahead);
5584 lookup_count = table_read_be_word(table, offset);
5585 offset += 2;
5586 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5588 if (input)
5589 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5591 if (coverage_index == GLYPH_NOT_COVERED)
5592 return FALSE;
5594 mc.backtrack_offset = subtable_offset;
5595 mc.input_offset = subtable_offset;
5596 mc.lookahead_offset = subtable_offset;
5597 mc.match_func = opentype_match_coverage_func;
5599 ret = opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input + 1, lookahead_count,
5600 lookahead, lookup_count, lookup_records, &mc);
5602 else
5603 WARN("Unknown chaining contextual substitution format %u.\n", format);
5605 return ret;
5608 static BOOL opentype_layout_apply_gsub_reverse_chain_context_substitution(struct scriptshaping_context *context,
5609 const struct lookup *lookup, unsigned int subtable_offset)
5611 const struct dwrite_fonttable *table = &context->table->table;
5612 unsigned int offset = subtable_offset;
5613 UINT16 glyph, format;
5615 if (context->nesting_level_left != SHAPE_MAX_NESTING_LEVEL)
5616 return FALSE;
5618 glyph = context->u.subst.glyphs[context->cur];
5620 format = table_read_be_word(table, offset);
5621 offset += 2;
5623 if (format == 1)
5625 struct match_context mc = { .context = context, .lookup = lookup };
5626 unsigned int start_index = 0, end_index = 0, backtrack_count, lookahead_count;
5627 unsigned int coverage, coverage_index;
5628 const UINT16 *backtrack, *lookahead;
5630 coverage = table_read_be_word(table, offset);
5631 offset += 2;
5633 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5634 if (coverage_index == GLYPH_NOT_COVERED)
5635 return FALSE;
5637 backtrack_count = table_read_be_word(table, offset);
5638 offset += 2;
5640 backtrack = table_read_ensure(table, offset, sizeof(*backtrack) * backtrack_count);
5641 offset += sizeof(*backtrack) * backtrack_count;
5643 lookahead_count = table_read_be_word(table, offset);
5644 offset += 2;
5646 lookahead = table_read_ensure(table, offset, sizeof(*lookahead) * lookahead_count);
5647 offset += sizeof(*lookahead) * lookahead_count;
5649 mc.match_func = opentype_match_coverage_func;
5650 mc.backtrack_offset = subtable_offset;
5651 mc.lookahead_offset = subtable_offset;
5653 if (opentype_layout_context_match_backtrack(&mc, backtrack_count, backtrack, &start_index) &&
5654 opentype_layout_context_match_lookahead(&mc, lookahead_count, lookahead, 1, &end_index))
5656 unsigned int glyph_count = table_read_be_word(table, offset);
5657 if (coverage_index >= glyph_count)
5658 return FALSE;
5659 offset += 2;
5661 glyph = table_read_be_word(table, offset + coverage_index * sizeof(glyph));
5662 opentype_layout_replace_glyph(context, glyph);
5664 return TRUE;
5667 else
5668 WARN("Unknown reverse chaining contextual substitution format %u.\n", format);
5670 return FALSE;
5673 static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
5675 unsigned int i, lookup_type;
5676 BOOL ret = FALSE;
5678 for (i = 0; i < lookup->subtable_count; ++i)
5680 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
5682 if (lookup->type == GSUB_LOOKUP_EXTENSION_SUBST)
5684 lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset);
5685 if (!lookup_type)
5686 continue;
5688 else
5689 lookup_type = lookup->type;
5691 switch (lookup_type)
5693 case GSUB_LOOKUP_SINGLE_SUBST:
5694 ret = opentype_layout_apply_gsub_single_substitution(context, lookup, subtable_offset);
5695 break;
5696 case GSUB_LOOKUP_MULTIPLE_SUBST:
5697 ret = opentype_layout_apply_gsub_mult_substitution(context, lookup, subtable_offset);
5698 break;
5699 case GSUB_LOOKUP_ALTERNATE_SUBST:
5700 ret = opentype_layout_apply_gsub_alt_substitution(context, lookup, subtable_offset);
5701 break;
5702 case GSUB_LOOKUP_LIGATURE_SUBST:
5703 ret = opentype_layout_apply_gsub_lig_substitution(context, lookup, subtable_offset);
5704 break;
5705 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
5706 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
5707 break;
5708 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
5709 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
5710 break;
5711 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
5712 ret = opentype_layout_apply_gsub_reverse_chain_context_substitution(context, lookup, subtable_offset);
5713 break;
5714 case GSUB_LOOKUP_EXTENSION_SUBST:
5715 WARN("Invalid lookup type for extension substitution %#x.\n", lookup_type);
5716 break;
5717 default:
5718 WARN("Unknown lookup type %u.\n", lookup_type);
5721 if (ret)
5722 break;
5725 return ret;
5728 static unsigned int unicode_get_mirrored_char(unsigned int codepoint)
5730 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
5731 WCHAR mirror;
5732 /* TODO: check if mirroring for higher planes makes sense at all */
5733 if (codepoint > 0xffff) return codepoint;
5734 mirror = get_table_entry(wine_mirror_map, codepoint);
5735 return mirror ? mirror : codepoint;
5739 * 034F # Mn COMBINING GRAPHEME JOINER
5740 * 061C # Cf ARABIC LETTER MARK
5741 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5742 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5743 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5744 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5746 static unsigned int opentype_is_default_ignorable(unsigned int codepoint)
5748 return codepoint == 0x34f || codepoint == 0x61c || codepoint == 0xfeff ||
5749 (codepoint >= 0x180b && codepoint <= 0x180e) || (codepoint >= 0x200b && codepoint <= 0x200f);
5752 static unsigned int opentype_is_diacritic(unsigned int codepoint)
5754 WCHAR ch = codepoint;
5755 WORD type = 0;
5756 /* Ignore higher planes for now. */
5757 if (codepoint > 0xffff) return 0;
5758 GetStringTypeW(CT_CTYPE3, &ch, 1, &type);
5759 return !!(type & C3_DIACRITIC);
5762 static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, const struct shaping_features *features)
5764 unsigned int rtlm_mask = shaping_features_get_mask(features, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), NULL);
5765 const struct shaping_font_ops *font = context->cache->font;
5766 unsigned int i, g, c, codepoint, cluster_start_idx = 0;
5767 UINT16 *clustermap = context->u.subst.clustermap;
5768 const WCHAR *text = context->text;
5769 BOOL bmp;
5771 memset(context->u.subst.glyph_props, 0, context->u.subst.max_glyph_count * sizeof(*context->u.subst.glyph_props));
5772 memset(context->u.buffer.text_props, 0, context->length * sizeof(*context->u.buffer.text_props));
5774 for (i = 0; i < context->length; ++i)
5776 g = context->glyph_count;
5778 if ((bmp = !(IS_HIGH_SURROGATE(text[i]) && (i < context->length - 1) && IS_LOW_SURROGATE(text[i + 1]))))
5780 codepoint = text[i];
5782 else
5784 codepoint = 0x10000 + ((text[i] - 0xd800) << 10) + (text[i + 1] - 0xdc00);
5787 if (context->is_rtl)
5789 c = unicode_get_mirrored_char(codepoint);
5790 if (c != codepoint && font->has_glyph(context->cache->context, c))
5791 codepoint = c;
5792 else
5793 context->glyph_infos[i].mask |= rtlm_mask;
5796 /* TODO: should this check for glyph availability? */
5797 if (*context->u.subst.digits && codepoint >= '0' && codepoint <= '9')
5798 codepoint = context->u.subst.digits[codepoint - '0'];
5800 context->u.buffer.glyphs[g] = font->get_glyph(context->cache->context, codepoint);
5801 context->u.buffer.glyph_props[g].justification = SCRIPT_JUSTIFY_CHARACTER;
5802 opentype_set_subst_glyph_props(context, g);
5804 /* Group diacritics with preceding base. Glyph class is ignored here. */
5805 if (!g || !opentype_is_diacritic(codepoint))
5807 context->u.buffer.glyph_props[g].isClusterStart = 1;
5808 context->glyph_infos[g].start_text_idx = i;
5809 cluster_start_idx = g;
5812 if (opentype_is_default_ignorable(codepoint))
5813 context->u.buffer.glyph_props[g].isZeroWidthSpace = 1;
5814 context->u.buffer.glyph_props[g].components = 1;
5815 context->glyph_count++;
5817 /* Set initial cluster map here, it's used for setting user features masks. */
5818 clustermap[i] = cluster_start_idx;
5819 if (bmp)
5820 context->u.buffer.text_props[i].canBreakShapingAfter = 1;
5821 else
5823 clustermap[i + 1] = cluster_start_idx;
5824 context->u.buffer.text_props[i + 1].canBreakShapingAfter = 1;
5825 ++i;
5830 static BOOL opentype_is_gsub_lookup_reversed(const struct scriptshaping_context *context, const struct lookup *lookup)
5832 unsigned int subtable_offset, lookup_type = lookup->type;
5834 if (lookup->type == GSUB_LOOKUP_EXTENSION_SUBST)
5836 subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, 0);
5837 /* Assumes format 1. */
5838 lookup_type = table_read_be_word(&context->table->table, subtable_offset + 2);
5840 return lookup_type == GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST;
5843 static void opentype_layout_apply_gsub_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
5845 struct lookup lookup = { 0 };
5846 /* Feature mask is intentionally zero, it's not used outside of main loop. */
5847 if (opentype_layout_init_lookup(context->table, lookup_index, 0, &lookup))
5848 opentype_layout_apply_gsub_lookup(context, &lookup);
5851 void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int script_index,
5852 unsigned int language_index, const struct shaping_features *features)
5854 struct lookups lookups = { 0 };
5855 unsigned int i, j, start_idx;
5856 BOOL ret;
5858 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
5859 context->u.buffer.apply_context_lookup = opentype_layout_apply_gsub_context_lookup;
5860 opentype_layout_collect_lookups(context, script_index, language_index, features, context->table, &lookups);
5862 opentype_get_nominal_glyphs(context, features);
5863 opentype_layout_set_glyph_masks(context, features);
5865 for (i = 0; i < lookups.count; ++i)
5867 const struct lookup *lookup = &lookups.lookups[i];
5869 context->lookup_mask = lookup->mask;
5871 if (!opentype_is_gsub_lookup_reversed(context, lookup))
5873 context->cur = 0;
5874 while (context->cur < context->glyph_count)
5876 ret = FALSE;
5878 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
5879 lookup_is_glyph_match(context, context->cur, lookup->flags))
5881 ret = opentype_layout_apply_gsub_lookup(context, lookup);
5884 if (!ret)
5885 context->cur++;
5888 else
5890 context->cur = context->glyph_count - 1;
5892 for (;;)
5894 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
5895 lookup_is_glyph_match(context, context->cur, lookup->flags))
5897 opentype_layout_apply_gsub_lookup(context, lookup);
5900 if (context->cur == 0) break;
5901 --context->cur;
5906 /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding
5907 text span to start_idx. */
5908 start_idx = 0;
5909 for (i = 1; i < context->glyph_count; ++i)
5911 if (context->u.buffer.glyph_props[i].isClusterStart)
5913 unsigned int start_text, end_text;
5915 start_text = context->glyph_infos[start_idx].start_text_idx;
5916 end_text = context->glyph_infos[i].start_text_idx;
5918 for (j = start_text; j < end_text; ++j)
5919 context->u.buffer.clustermap[j] = start_idx;
5921 start_idx = i;
5925 /* Fill the tail. */
5926 for (j = context->glyph_infos[start_idx].start_text_idx; j < context->length; ++j)
5927 context->u.buffer.clustermap[j] = start_idx;
5929 heap_free(lookups.lookups);
5932 static BOOL opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5933 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
5935 const struct dwrite_fonttable *table = &context->table->table;
5936 const UINT16 *offsets;
5937 unsigned int count;
5939 if (format == 1 || format == 2)
5941 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5942 return TRUE;
5944 else if (format == 3)
5946 count = table_read_be_word(table, subtable_offset + 2);
5947 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6, count * sizeof(*offsets))))
5948 return FALSE;
5950 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
5951 return TRUE;
5954 return FALSE;
5957 static BOOL opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5958 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
5960 const struct dwrite_fonttable *table = &context->table->table;
5961 unsigned int count, backtrack_count;
5962 const UINT16 *offsets;
5964 if (format == 1 || format == 2)
5966 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5967 return TRUE;
5969 else if (format == 3)
5971 backtrack_count = table_read_be_word(table, subtable_offset + 2);
5973 count = table_read_be_word(table, subtable_offset + 4 + backtrack_count * sizeof(*offsets));
5975 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6 + backtrack_count * sizeof(*offsets),
5976 count * sizeof(*offsets))))
5977 return FALSE;
5979 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
5980 return TRUE;
5983 return FALSE;
5986 static BOOL opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5987 const struct lookup *lookup)
5989 const struct dwrite_fonttable *gsub = &context->table->table;
5990 static const unsigned short gsub_formats[] =
5992 0, /* Unused */
5993 1, /* SingleSubst */
5994 1, /* MultipleSubst */
5995 1, /* AlternateSubst */
5996 1, /* LigatureSubst */
5997 3, /* ContextSubst */
5998 3, /* ChainContextSubst */
5999 0, /* Extension, unused */
6000 1, /* ReverseChainSubst */
6002 unsigned int i, coverage, lookup_type, format;
6004 for (i = 0; i < lookup->subtable_count; ++i)
6006 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
6008 if (lookup->type == GSUB_LOOKUP_EXTENSION_SUBST)
6010 lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset);
6011 if (!lookup_type)
6012 continue;
6014 else
6015 lookup_type = lookup->type;
6017 format = table_read_be_word(gsub, subtable_offset);
6019 if (!format || format > ARRAY_SIZE(gsub_formats) || format > gsub_formats[lookup_type])
6020 break;
6022 coverage = table_read_be_word(gsub, subtable_offset + 2);
6024 switch (lookup_type)
6026 case GSUB_LOOKUP_SINGLE_SUBST:
6027 case GSUB_LOOKUP_MULTIPLE_SUBST:
6028 case GSUB_LOOKUP_ALTERNATE_SUBST:
6029 case GSUB_LOOKUP_LIGATURE_SUBST:
6030 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
6032 if (opentype_layout_is_glyph_covered(gsub, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6033 return TRUE;
6035 break;
6037 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
6039 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6040 return TRUE;
6042 break;
6044 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
6046 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6047 return TRUE;
6049 break;
6051 default:
6052 WARN("Unknown lookup type %u.\n", lookup_type);
6056 return FALSE;
6059 static BOOL opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6060 const struct lookup *lookup)
6062 const struct dwrite_fonttable *gpos = &context->table->table;
6063 static const unsigned short gpos_formats[] =
6065 0, /* Unused */
6066 2, /* SinglePos */
6067 2, /* PairPos */
6068 1, /* CursivePos */
6069 1, /* MarkBasePos */
6070 1, /* MarkLigPos */
6071 1, /* MarkMarkPos */
6072 3, /* ContextPos */
6073 3, /* ChainContextPos */
6074 0, /* Extension, unused */
6076 unsigned int i, coverage, lookup_type, format;
6078 for (i = 0; i < lookup->subtable_count; ++i)
6080 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
6082 if (lookup->type == GPOS_LOOKUP_EXTENSION_POSITION)
6084 lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset);
6085 if (!lookup_type)
6086 continue;
6088 else
6089 lookup_type = lookup->type;
6091 format = table_read_be_word(gpos, subtable_offset);
6093 if (!format || format > ARRAY_SIZE(gpos_formats) || format > gpos_formats[lookup_type])
6094 break;
6096 coverage = table_read_be_word(gpos, subtable_offset + 2);
6098 switch (lookup_type)
6100 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
6101 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
6102 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
6103 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
6104 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
6105 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
6107 if (opentype_layout_is_glyph_covered(gpos, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6108 return TRUE;
6110 break;
6112 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
6114 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6115 return TRUE;
6117 break;
6119 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
6121 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6122 return TRUE;
6124 break;
6126 default:
6127 WARN("Unknown lookup type %u.\n", lookup_type);
6131 return FALSE;
6134 typedef BOOL (*p_lookup_is_glyph_covered_func)(struct scriptshaping_context *context, UINT16 glyph, const struct lookup *lookup);
6136 BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index,
6137 unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count,
6138 const UINT16 *glyphs, UINT8 *feature_applies)
6140 p_lookup_is_glyph_covered_func func_is_covered;
6141 struct shaping_features features = { 0 };
6142 struct lookups lookups = { 0 };
6143 BOOL ret = FALSE, is_covered;
6144 unsigned int i, j, applies;
6146 features.features = feature;
6147 features.count = 1;
6149 for (i = 0; i < context->glyph_count; ++i)
6150 opentype_set_glyph_props(context, i);
6152 opentype_layout_collect_lookups(context, script_index, language_index, &features, context->table, &lookups);
6154 func_is_covered = context->table == &context->cache->gsub ? opentype_layout_gsub_lookup_is_glyph_covered :
6155 opentype_layout_gpos_lookup_is_glyph_covered;
6157 for (i = 0; i < lookups.count; ++i)
6159 struct lookup *lookup = &lookups.lookups[i];
6161 applies = 0;
6162 for (j = 0; j < context->glyph_count; ++j)
6164 if (lookup_is_glyph_match(context, j, lookup->flags))
6166 if ((is_covered = func_is_covered(context, glyphs[i], lookup)))
6167 ++applies;
6168 feature_applies[j] |= is_covered;
6172 if ((ret = (applies == context->glyph_count)))
6173 break;
6176 heap_free(lookups.lookups);
6178 return ret;
6181 HRESULT opentype_get_vertical_glyph_variants(struct dwrite_fontface *fontface, unsigned int glyph_count,
6182 const UINT16 *nominal_glyphs, UINT16 *glyphs)
6184 struct shaping_features features = { 0 };
6185 struct shaping_feature vert_feature = { 0 };
6186 struct scriptshaping_context context = { 0 };
6187 struct lookups lookups = { 0 };
6188 unsigned int i;
6190 memcpy(glyphs, nominal_glyphs, glyph_count * sizeof(*glyphs));
6192 if (!(fontface->flags & FONTFACE_HAS_VERTICAL_VARIANTS))
6193 return S_OK;
6195 context.cache = fontface_get_shaping_cache(fontface);
6196 context.u.subst.glyphs = glyphs;
6197 context.u.subst.glyph_props = heap_calloc(glyph_count, sizeof(*context.u.subst.glyph_props));
6198 context.u.subst.max_glyph_count = glyph_count;
6199 context.u.subst.capacity = glyph_count;
6200 context.glyph_infos = heap_alloc_zero(sizeof(*context.glyph_infos) * glyph_count);
6201 context.table = &context.cache->gsub;
6203 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6204 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6205 vert_feature.max_value = 1;
6206 vert_feature.default_value = 1;
6208 features.features = &vert_feature;
6209 features.count = features.capacity = 1;
6211 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6212 opentype_layout_set_glyph_masks(&context, &features);
6214 for (i = 0; i < lookups.count; ++i)
6216 const struct lookup *lookup = &lookups.lookups[i];
6218 if (lookup->type != GSUB_LOOKUP_SINGLE_SUBST)
6219 continue;
6221 context.cur = 0;
6222 while (context.cur < context.glyph_count)
6224 BOOL ret = FALSE;
6226 if (lookup_is_glyph_match(&context, context.cur, lookup->flags))
6227 ret = opentype_layout_apply_gsub_lookup(&context, lookup);
6229 if (!ret)
6230 context.cur++;
6234 heap_free(context.u.subst.glyph_props);
6235 heap_free(context.glyph_infos);
6236 heap_free(lookups.lookups);
6238 return S_OK;