wined3d: Use glFinish() for synchronisation when cleaning up a destroyed context...
[wine.git] / dlls / dwrite / opentype.c
blob260a0a8f5db0bcce99b984b5c970dfa0ceec6373
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 struct tt_head
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;
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 struct tt_post
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;
175 struct tt_os2
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;
222 struct tt_hhea
224 USHORT majorVersion;
225 USHORT minorVersion;
226 SHORT ascender;
227 SHORT descender;
228 SHORT linegap;
229 USHORT advanceWidthMax;
230 SHORT minLeftSideBearing;
231 SHORT minRightSideBearing;
232 SHORT xMaxExtent;
233 SHORT caretSlopeRise;
234 SHORT caretSlopeRun;
235 SHORT caretOffset;
236 SHORT reserved[4];
237 SHORT metricDataFormat;
238 USHORT numberOfHMetrics;
241 struct sbix_header
243 WORD version;
244 WORD flags;
245 DWORD num_strikes;
246 DWORD strike_offset[1];
249 struct sbix_strike
251 WORD ppem;
252 WORD ppi;
253 DWORD glyphdata_offsets[1];
256 struct sbix_glyph_data
258 WORD originOffsetX;
259 WORD originOffsetY;
260 DWORD graphic_type;
261 BYTE data[1];
264 struct maxp
266 DWORD version;
267 WORD num_glyphs;
270 struct cblc_header
272 WORD major_version;
273 WORD minor_version;
274 DWORD num_sizes;
277 typedef struct {
278 BYTE res[12];
279 } sbitLineMetrics;
281 struct cblc_bitmapsize_table
283 DWORD indexSubTableArrayOffset;
284 DWORD indexTablesSize;
285 DWORD numberofIndexSubTables;
286 DWORD colorRef;
287 sbitLineMetrics hori;
288 sbitLineMetrics vert;
289 WORD startGlyphIndex;
290 WORD endGlyphIndex;
291 BYTE ppemX;
292 BYTE ppemY;
293 BYTE bit_depth;
294 BYTE flags;
297 struct gasp_range
299 WORD max_ppem;
300 WORD flags;
303 struct gasp_header
305 WORD version;
306 WORD num_ranges;
307 struct gasp_range ranges[1];
310 enum OS2_FSSELECTION {
311 OS2_FSSELECTION_ITALIC = 1 << 0,
312 OS2_FSSELECTION_UNDERSCORE = 1 << 1,
313 OS2_FSSELECTION_NEGATIVE = 1 << 2,
314 OS2_FSSELECTION_OUTLINED = 1 << 3,
315 OS2_FSSELECTION_STRIKEOUT = 1 << 4,
316 OS2_FSSELECTION_BOLD = 1 << 5,
317 OS2_FSSELECTION_REGULAR = 1 << 6,
318 OS2_FSSELECTION_USE_TYPO_METRICS = 1 << 7,
319 OS2_FSSELECTION_WWS = 1 << 8,
320 OS2_FSSELECTION_OBLIQUE = 1 << 9
323 typedef struct {
324 WORD platformID;
325 WORD encodingID;
326 WORD languageID;
327 WORD nameID;
328 WORD length;
329 WORD offset;
330 } TT_NameRecord;
332 typedef struct {
333 WORD format;
334 WORD count;
335 WORD stringOffset;
336 TT_NameRecord nameRecord[1];
337 } TT_NAME_V0;
339 struct vdmx_header
341 WORD version;
342 WORD num_recs;
343 WORD num_ratios;
346 struct vdmx_ratio
348 BYTE bCharSet;
349 BYTE xRatio;
350 BYTE yStartRatio;
351 BYTE yEndRatio;
354 struct vdmx_vtable
356 WORD yPelHeight;
357 SHORT yMax;
358 SHORT yMin;
361 struct vdmx_group
363 WORD recs;
364 BYTE startsz;
365 BYTE endsz;
366 struct vdmx_vtable entries[1];
369 struct ot_feature_record
371 DWORD tag;
372 WORD offset;
375 struct ot_feature_list
377 WORD feature_count;
378 struct ot_feature_record features[1];
381 struct ot_langsys
383 WORD lookup_order; /* Reserved */
384 WORD required_feature_index;
385 WORD feature_count;
386 WORD feature_index[1];
389 struct ot_langsys_record
391 CHAR tag[4];
392 WORD langsys;
395 struct ot_script
397 WORD default_langsys;
398 WORD langsys_count;
399 struct ot_langsys_record langsys[1];
400 } OT_Script;
402 struct ot_script_record
404 CHAR tag[4];
405 WORD script;
408 struct ot_script_list
410 WORD script_count;
411 struct ot_script_record scripts[1];
414 enum ot_gdef_class
416 GDEF_CLASS_UNCLASSIFIED = 0,
417 GDEF_CLASS_BASE = 1,
418 GDEF_CLASS_LIGATURE = 2,
419 GDEF_CLASS_MARK = 3,
420 GDEF_CLASS_COMPONENT = 4,
421 GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
424 struct gdef_header
426 DWORD version;
427 UINT16 classdef;
428 UINT16 attach_list;
429 UINT16 ligcaret_list;
430 UINT16 markattach_classdef;
431 UINT16 markglyphsetdef;
434 struct ot_gdef_classdef_format1
436 WORD format;
437 WORD start_glyph;
438 WORD glyph_count;
439 WORD classes[1];
442 struct ot_gdef_class_range
444 WORD start_glyph;
445 WORD end_glyph;
446 WORD glyph_class;
449 struct ot_gdef_classdef_format2
451 WORD format;
452 WORD range_count;
453 struct ot_gdef_class_range ranges[1];
456 struct gpos_gsub_header
458 DWORD version;
459 WORD script_list;
460 WORD feature_list;
461 WORD lookup_list;
464 enum gsub_gpos_lookup_flags
466 LOOKUP_FLAG_RTL = 0x1, /* Only used for GPOS cursive attachments. */
468 LOOKUP_FLAG_IGNORE_BASE = 0x2,
469 LOOKUP_FLAG_IGNORE_LIGATURES = 0x4,
470 LOOKUP_FLAG_IGNORE_MARKS = 0x8,
471 LOOKUP_FLAG_IGNORE_MASK = 0xe,
473 LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x10,
474 LOOKUP_FLAG_MARK_ATTACHMENT_TYPE = 0xff00,
477 enum glyph_prop_flags
479 GLYPH_PROP_BASE = LOOKUP_FLAG_IGNORE_BASE,
480 GLYPH_PROP_LIGATURE = LOOKUP_FLAG_IGNORE_LIGATURES,
481 GLYPH_PROP_MARK = LOOKUP_FLAG_IGNORE_MARKS,
484 enum gpos_lookup_type
486 GPOS_LOOKUP_SINGLE_ADJUSTMENT = 1,
487 GPOS_LOOKUP_PAIR_ADJUSTMENT = 2,
488 GPOS_LOOKUP_CURSIVE_ATTACHMENT = 3,
489 GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT = 4,
490 GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT = 5,
491 GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT = 6,
492 GPOS_LOOKUP_CONTEXTUAL_POSITION = 7,
493 GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION = 8,
494 GPOS_LOOKUP_EXTENSION_POSITION = 9,
497 enum gsub_lookup_type
499 GSUB_LOOKUP_SINGLE_SUBST = 1,
500 GSUB_LOOKUP_MULTIPLE_SUBST = 2,
501 GSUB_LOOKUP_ALTERNATE_SUBST = 3,
502 GSUB_LOOKUP_LIGATURE_SUBST = 4,
503 GSUB_LOOKUP_CONTEXTUAL_SUBST = 5,
504 GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST = 6,
505 GSUB_LOOKUP_EXTENSION_SUBST = 7,
506 GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST = 8,
509 enum gpos_value_format
511 GPOS_VALUE_X_PLACEMENT = 0x1,
512 GPOS_VALUE_Y_PLACEMENT = 0x2,
513 GPOS_VALUE_X_ADVANCE = 0x4,
514 GPOS_VALUE_Y_ADVANCE = 0x8,
515 GPOS_VALUE_X_PLACEMENT_DEVICE = 0x10,
516 GPOS_VALUE_Y_PLACEMENT_DEVICE = 0x20,
517 GPOS_VALUE_X_ADVANCE_DEVICE = 0x40,
518 GPOS_VALUE_Y_ADVANCE_DEVICE = 0x80,
521 enum OPENTYPE_PLATFORM_ID
523 OPENTYPE_PLATFORM_UNICODE = 0,
524 OPENTYPE_PLATFORM_MAC,
525 OPENTYPE_PLATFORM_ISO,
526 OPENTYPE_PLATFORM_WIN,
527 OPENTYPE_PLATFORM_CUSTOM
530 struct ot_gsubgpos_extension_format1
532 UINT16 format;
533 UINT16 lookup_type;
534 DWORD extension_offset;
537 struct ot_gsub_singlesubst_format1
539 UINT16 format;
540 UINT16 coverage;
541 short delta;
544 struct ot_gsub_singlesubst_format2
546 UINT16 format;
547 UINT16 coverage;
548 UINT16 count;
549 UINT16 substitutes[1];
552 struct ot_gsub_multsubst_format1
554 UINT16 format;
555 UINT16 coverage;
556 UINT16 seq_count;
557 UINT16 seq[1];
560 struct ot_gsub_altsubst_format1
562 UINT16 format;
563 UINT16 coverage;
564 UINT16 count;
565 UINT16 sets[1];
568 struct ot_gsub_ligsubst_format1
570 UINT16 format;
571 UINT16 coverage;
572 UINT16 lig_set_count;
573 UINT16 lig_sets[1];
576 struct ot_gsub_ligset
578 UINT16 count;
579 UINT16 offsets[1];
582 struct ot_gsub_lig
584 UINT16 lig_glyph;
585 UINT16 comp_count;
586 UINT16 components[1];
589 struct ot_gsubgpos_context_format1
591 UINT16 format;
592 UINT16 coverage;
593 UINT16 ruleset_count;
594 UINT16 rulesets[1];
597 struct ot_gsubgpos_ruleset
599 UINT16 count;
600 UINT16 offsets[1];
603 struct ot_feature
605 WORD feature_params;
606 WORD lookup_count;
607 WORD lookuplist_index[1];
610 struct ot_lookup_list
612 WORD lookup_count;
613 WORD lookup[1];
616 struct ot_lookup_table
618 WORD lookup_type;
619 WORD flags;
620 WORD subtable_count;
621 WORD subtable[1];
624 #define GLYPH_NOT_COVERED (~0u)
626 struct ot_coverage_format1
628 WORD format;
629 WORD glyph_count;
630 WORD glyphs[1];
633 struct ot_coverage_range
635 WORD start_glyph;
636 WORD end_glyph;
637 WORD startcoverage_index;
640 struct ot_coverage_format2
642 WORD format;
643 WORD range_count;
644 struct ot_coverage_range ranges[1];
647 struct ot_gpos_device_table
649 WORD start_size;
650 WORD end_size;
651 WORD format;
652 WORD values[1];
655 struct ot_gpos_singlepos_format1
657 WORD format;
658 WORD coverage;
659 WORD value_format;
660 WORD value[1];
663 struct ot_gpos_singlepos_format2
665 WORD format;
666 WORD coverage;
667 WORD value_format;
668 WORD value_count;
669 WORD values[1];
672 struct ot_gpos_pairvalue
674 WORD second_glyph;
675 BYTE data[1];
678 struct ot_gpos_pairset
680 WORD pairvalue_count;
681 struct ot_gpos_pairvalue pairvalues[1];
684 struct ot_gpos_pairpos_format1
686 WORD format;
687 WORD coverage;
688 WORD value_format1;
689 WORD value_format2;
690 WORD pairset_count;
691 WORD pairsets[1];
694 struct ot_gpos_pairpos_format2
696 WORD format;
697 WORD coverage;
698 WORD value_format1;
699 WORD value_format2;
700 WORD class_def1;
701 WORD class_def2;
702 WORD class1_count;
703 WORD class2_count;
704 WORD values[1];
707 struct ot_gpos_anchor_format1
709 WORD format;
710 short x_coord;
711 short y_coord;
714 struct ot_gpos_anchor_format2
716 WORD format;
717 short x_coord;
718 short y_coord;
719 WORD anchor_point;
722 struct ot_gpos_anchor_format3
724 WORD format;
725 short x_coord;
726 short y_coord;
727 WORD x_dev_offset;
728 WORD y_dev_offset;
731 struct ot_gpos_cursive_format1
733 WORD format;
734 WORD coverage;
735 WORD count;
736 WORD anchors[1];
739 struct ot_gpos_mark_record
741 WORD mark_class;
742 WORD mark_anchor;
745 struct ot_gpos_mark_array
747 WORD count;
748 struct ot_gpos_mark_record records[1];
751 struct ot_gpos_base_array
753 WORD count;
754 WORD offsets[1];
757 struct ot_gpos_mark_to_base_format1
759 WORD format;
760 WORD mark_coverage;
761 WORD base_coverage;
762 WORD mark_class_count;
763 WORD mark_array;
764 WORD base_array;
767 struct ot_gpos_mark_to_lig_format1
769 WORD format;
770 WORD mark_coverage;
771 WORD lig_coverage;
772 WORD mark_class_count;
773 WORD mark_array;
774 WORD lig_array;
777 struct ot_gpos_mark_to_mark_format1
779 WORD format;
780 WORD mark1_coverage;
781 WORD mark2_coverage;
782 WORD mark_class_count;
783 WORD mark1_array;
784 WORD mark2_array;
787 typedef struct {
788 WORD SubstFormat;
789 WORD Coverage;
790 WORD DeltaGlyphID;
791 } GSUB_SingleSubstFormat1;
793 typedef struct {
794 WORD SubstFormat;
795 WORD Coverage;
796 WORD GlyphCount;
797 WORD Substitute[1];
798 } GSUB_SingleSubstFormat2;
800 typedef struct {
801 WORD SubstFormat;
802 WORD ExtensionLookupType;
803 DWORD ExtensionOffset;
804 } GSUB_ExtensionPosFormat1;
806 #include "poppack.h"
808 enum TT_NAME_WINDOWS_ENCODING_ID
810 TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
811 TT_NAME_WINDOWS_ENCODING_UNICODE_BMP,
812 TT_NAME_WINDOWS_ENCODING_SJIS,
813 TT_NAME_WINDOWS_ENCODING_PRC,
814 TT_NAME_WINDOWS_ENCODING_BIG5,
815 TT_NAME_WINDOWS_ENCODING_WANSUNG,
816 TT_NAME_WINDOWS_ENCODING_JOHAB,
817 TT_NAME_WINDOWS_ENCODING_RESERVED1,
818 TT_NAME_WINDOWS_ENCODING_RESERVED2,
819 TT_NAME_WINDOWS_ENCODING_RESERVED3,
820 TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
823 enum TT_NAME_MAC_ENCODING_ID
825 TT_NAME_MAC_ENCODING_ROMAN = 0,
826 TT_NAME_MAC_ENCODING_JAPANESE,
827 TT_NAME_MAC_ENCODING_TRAD_CHINESE,
828 TT_NAME_MAC_ENCODING_KOREAN,
829 TT_NAME_MAC_ENCODING_ARABIC,
830 TT_NAME_MAC_ENCODING_HEBREW,
831 TT_NAME_MAC_ENCODING_GREEK,
832 TT_NAME_MAC_ENCODING_RUSSIAN,
833 TT_NAME_MAC_ENCODING_RSYMBOL,
834 TT_NAME_MAC_ENCODING_DEVANAGARI,
835 TT_NAME_MAC_ENCODING_GURMUKHI,
836 TT_NAME_MAC_ENCODING_GUJARATI,
837 TT_NAME_MAC_ENCODING_ORIYA,
838 TT_NAME_MAC_ENCODING_BENGALI,
839 TT_NAME_MAC_ENCODING_TAMIL,
840 TT_NAME_MAC_ENCODING_TELUGU,
841 TT_NAME_MAC_ENCODING_KANNADA,
842 TT_NAME_MAC_ENCODING_MALAYALAM,
843 TT_NAME_MAC_ENCODING_SINHALESE,
844 TT_NAME_MAC_ENCODING_BURMESE,
845 TT_NAME_MAC_ENCODING_KHMER,
846 TT_NAME_MAC_ENCODING_THAI,
847 TT_NAME_MAC_ENCODING_LAOTIAN,
848 TT_NAME_MAC_ENCODING_GEORGIAN,
849 TT_NAME_MAC_ENCODING_ARMENIAN,
850 TT_NAME_MAC_ENCODING_SIMPL_CHINESE,
851 TT_NAME_MAC_ENCODING_TIBETAN,
852 TT_NAME_MAC_ENCODING_MONGOLIAN,
853 TT_NAME_MAC_ENCODING_GEEZ,
854 TT_NAME_MAC_ENCODING_SLAVIC,
855 TT_NAME_MAC_ENCODING_VIETNAMESE,
856 TT_NAME_MAC_ENCODING_SINDHI,
857 TT_NAME_MAC_ENCODING_UNINTERPRETED
860 enum TT_NAME_MAC_LANGUAGE_ID
862 TT_NAME_MAC_LANGID_ENGLISH = 0,
863 TT_NAME_MAC_LANGID_FRENCH,
864 TT_NAME_MAC_LANGID_GERMAN,
865 TT_NAME_MAC_LANGID_ITALIAN,
866 TT_NAME_MAC_LANGID_DUTCH,
867 TT_NAME_MAC_LANGID_SWEDISH,
868 TT_NAME_MAC_LANGID_SPANISH,
869 TT_NAME_MAC_LANGID_DANISH,
870 TT_NAME_MAC_LANGID_PORTUGUESE,
871 TT_NAME_MAC_LANGID_NORWEGIAN,
872 TT_NAME_MAC_LANGID_HEBREW,
873 TT_NAME_MAC_LANGID_JAPANESE,
874 TT_NAME_MAC_LANGID_ARABIC,
875 TT_NAME_MAC_LANGID_FINNISH,
876 TT_NAME_MAC_LANGID_GREEK,
877 TT_NAME_MAC_LANGID_ICELANDIC,
878 TT_NAME_MAC_LANGID_MALTESE,
879 TT_NAME_MAC_LANGID_TURKISH,
880 TT_NAME_MAC_LANGID_CROATIAN,
881 TT_NAME_MAC_LANGID_TRAD_CHINESE,
882 TT_NAME_MAC_LANGID_URDU,
883 TT_NAME_MAC_LANGID_HINDI,
884 TT_NAME_MAC_LANGID_THAI,
885 TT_NAME_MAC_LANGID_KOREAN,
886 TT_NAME_MAC_LANGID_LITHUANIAN,
887 TT_NAME_MAC_LANGID_POLISH,
888 TT_NAME_MAC_LANGID_HUNGARIAN,
889 TT_NAME_MAC_LANGID_ESTONIAN,
890 TT_NAME_MAC_LANGID_LATVIAN,
891 TT_NAME_MAC_LANGID_SAMI,
892 TT_NAME_MAC_LANGID_FAROESE,
893 TT_NAME_MAC_LANGID_FARSI,
894 TT_NAME_MAC_LANGID_RUSSIAN,
895 TT_NAME_MAC_LANGID_SIMPL_CHINESE,
896 TT_NAME_MAC_LANGID_FLEMISH,
897 TT_NAME_MAC_LANGID_GAELIC,
898 TT_NAME_MAC_LANGID_ALBANIAN,
899 TT_NAME_MAC_LANGID_ROMANIAN,
900 TT_NAME_MAC_LANGID_CZECH,
901 TT_NAME_MAC_LANGID_SLOVAK,
902 TT_NAME_MAC_LANGID_SLOVENIAN,
903 TT_NAME_MAC_LANGID_YIDDISH,
904 TT_NAME_MAC_LANGID_SERBIAN,
905 TT_NAME_MAC_LANGID_MACEDONIAN,
906 TT_NAME_MAC_LANGID_BULGARIAN,
907 TT_NAME_MAC_LANGID_UKRAINIAN,
908 TT_NAME_MAC_LANGID_BYELORUSSIAN,
909 TT_NAME_MAC_LANGID_UZBEK,
910 TT_NAME_MAC_LANGID_KAZAKH,
911 TT_NAME_MAC_LANGID_AZERB_CYR,
912 TT_NAME_MAC_LANGID_AZERB_ARABIC,
913 TT_NAME_MAC_LANGID_ARMENIAN,
914 TT_NAME_MAC_LANGID_GEORGIAN,
915 TT_NAME_MAC_LANGID_MOLDAVIAN,
916 TT_NAME_MAC_LANGID_KIRGHIZ,
917 TT_NAME_MAC_LANGID_TAJIKI,
918 TT_NAME_MAC_LANGID_TURKMEN,
919 TT_NAME_MAC_LANGID_MONGOLIAN,
920 TT_NAME_MAC_LANGID_MONGOLIAN_CYR,
921 TT_NAME_MAC_LANGID_PASHTO,
922 TT_NAME_MAC_LANGID_KURDISH,
923 TT_NAME_MAC_LANGID_KASHMIRI,
924 TT_NAME_MAC_LANGID_SINDHI,
925 TT_NAME_MAC_LANGID_TIBETAN,
926 TT_NAME_MAC_LANGID_NEPALI,
927 TT_NAME_MAC_LANGID_SANSKRIT,
928 TT_NAME_MAC_LANGID_MARATHI,
929 TT_NAME_MAC_LANGID_BENGALI,
930 TT_NAME_MAC_LANGID_ASSAMESE,
931 TT_NAME_MAC_LANGID_GUJARATI,
932 TT_NAME_MAC_LANGID_PUNJABI,
933 TT_NAME_MAC_LANGID_ORIYA,
934 TT_NAME_MAC_LANGID_MALAYALAM,
935 TT_NAME_MAC_LANGID_KANNADA,
936 TT_NAME_MAC_LANGID_TAMIL,
937 TT_NAME_MAC_LANGID_TELUGU,
938 TT_NAME_MAC_LANGID_SINHALESE,
939 TT_NAME_MAC_LANGID_BURMESE,
940 TT_NAME_MAC_LANGID_KHMER,
941 TT_NAME_MAC_LANGID_LAO,
942 TT_NAME_MAC_LANGID_VIETNAMESE,
943 TT_NAME_MAC_LANGID_INDONESIAN,
944 TT_NAME_MAC_LANGID_TAGALOG,
945 TT_NAME_MAC_LANGID_MALAY_ROMAN,
946 TT_NAME_MAC_LANGID_MALAY_ARABIC,
947 TT_NAME_MAC_LANGID_AMHARIC,
948 TT_NAME_MAC_LANGID_TIGRINYA,
949 TT_NAME_MAC_LANGID_GALLA,
950 TT_NAME_MAC_LANGID_SOMALI,
951 TT_NAME_MAC_LANGID_SWAHILI,
952 TT_NAME_MAC_LANGID_KINYARWANDA,
953 TT_NAME_MAC_LANGID_RUNDI,
954 TT_NAME_MAC_LANGID_NYANJA,
955 TT_NAME_MAC_LANGID_MALAGASY,
956 TT_NAME_MAC_LANGID_ESPERANTO,
957 TT_NAME_MAC_LANGID_WELSH = 128,
958 TT_NAME_MAC_LANGID_BASQUE,
959 TT_NAME_MAC_LANGID_CATALAN,
960 TT_NAME_MAC_LANGID_LATIN,
961 TT_NAME_MAC_LANGID_QUECHUA,
962 TT_NAME_MAC_LANGID_GUARANI,
963 TT_NAME_MAC_LANGID_AYMARA,
964 TT_NAME_MAC_LANGID_TATAR,
965 TT_NAME_MAC_LANGID_UIGHUR,
966 TT_NAME_MAC_LANGID_DZONGKHA,
967 TT_NAME_MAC_LANGID_JAVANESE,
968 TT_NAME_MAC_LANGID_SUNDANESE,
969 TT_NAME_MAC_LANGID_GALICIAN,
970 TT_NAME_MAC_LANGID_AFRIKAANS,
971 TT_NAME_MAC_LANGID_BRETON,
972 TT_NAME_MAC_LANGID_INUKTITUT,
973 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC,
974 TT_NAME_MAC_LANGID_MANX_GAELIC,
975 TT_NAME_MAC_LANGID_IRISH_GAELIC,
976 TT_NAME_MAC_LANGID_TONGAN,
977 TT_NAME_MAC_LANGID_GREEK_POLYTONIC,
978 TT_NAME_MAC_LANGID_GREENLANDIC,
979 TT_NAME_MAC_LANGID_AZER_ROMAN
982 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
983 static const char name_mac_langid_to_locale[][10] = {
984 "en-US",
985 "fr-FR",
986 "de-DE",
987 "it-IT",
988 "nl-NL",
989 "sv-SE",
990 "es-ES",
991 "da-DA",
992 "pt-PT",
993 "no-NO",
994 "he-IL",
995 "ja-JP",
996 "ar-AR",
997 "fi-FI",
998 "el-GR",
999 "is-IS",
1000 "mt-MT",
1001 "tr-TR",
1002 "hr-HR",
1003 "zh-HK",
1004 "ur-PK",
1005 "hi-IN",
1006 "th-TH",
1007 "ko-KR",
1008 "lt-LT",
1009 "pl-PL",
1010 "hu-HU",
1011 "et-EE",
1012 "lv-LV",
1013 "se-NO",
1014 "fo-FO",
1015 "fa-IR",
1016 "ru-RU",
1017 "zh-CN",
1018 "nl-BE",
1019 "gd-GB",
1020 "sq-AL",
1021 "ro-RO",
1022 "cs-CZ",
1023 "sk-SK",
1024 "sl-SI",
1026 "sr-Latn",
1027 "mk-MK",
1028 "bg-BG",
1029 "uk-UA",
1030 "be-BY",
1031 "uz-Latn",
1032 "kk-KZ",
1033 "az-Cyrl-AZ",
1034 "az-AZ",
1035 "hy-AM",
1036 "ka-GE",
1039 "tg-TJ",
1040 "tk-TM",
1041 "mn-Mong",
1042 "mn-MN",
1043 "ps-AF",
1044 "ku-Arab",
1046 "sd-Arab",
1047 "bo-CN",
1048 "ne-NP",
1049 "sa-IN",
1050 "mr-IN",
1051 "bn-IN",
1052 "as-IN",
1053 "gu-IN",
1054 "pa-Arab",
1055 "or-IN",
1056 "ml-IN",
1057 "kn-IN",
1058 "ta-LK",
1059 "te-IN",
1060 "si-LK",
1062 "km-KH",
1063 "lo-LA",
1064 "vi-VN",
1065 "id-ID",
1067 "ms-MY",
1068 "ms-Arab",
1069 "am-ET",
1070 "ti-ET",
1073 "sw-KE",
1074 "rw-RW",
1112 "cy-GB",
1113 "eu-ES",
1114 "ca-ES",
1119 "tt-RU",
1120 "ug-CN",
1124 "gl-ES",
1125 "af-ZA",
1126 "br-FR",
1127 "iu-Latn-CA",
1128 "gd-GB",
1130 "ga-IE",
1133 "kl-GL",
1134 "az-Latn"
1137 enum OPENTYPE_STRING_ID
1139 OPENTYPE_STRING_COPYRIGHT_NOTICE = 0,
1140 OPENTYPE_STRING_FAMILY_NAME,
1141 OPENTYPE_STRING_SUBFAMILY_NAME,
1142 OPENTYPE_STRING_UNIQUE_IDENTIFIER,
1143 OPENTYPE_STRING_FULL_FONTNAME,
1144 OPENTYPE_STRING_VERSION_STRING,
1145 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1146 OPENTYPE_STRING_TRADEMARK,
1147 OPENTYPE_STRING_MANUFACTURER,
1148 OPENTYPE_STRING_DESIGNER,
1149 OPENTYPE_STRING_DESCRIPTION,
1150 OPENTYPE_STRING_VENDOR_URL,
1151 OPENTYPE_STRING_DESIGNER_URL,
1152 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1153 OPENTYPE_STRING_LICENSE_INFO_URL,
1154 OPENTYPE_STRING_RESERVED_ID15,
1155 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1156 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1157 OPENTYPE_STRING_COMPATIBLE_FULLNAME,
1158 OPENTYPE_STRING_SAMPLE_TEXT,
1159 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1160 OPENTYPE_STRING_WWS_FAMILY_NAME,
1161 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
1164 static const UINT16 dwriteid_to_opentypeid[DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME + 1] =
1166 (UINT16)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
1167 OPENTYPE_STRING_COPYRIGHT_NOTICE,
1168 OPENTYPE_STRING_VERSION_STRING,
1169 OPENTYPE_STRING_TRADEMARK,
1170 OPENTYPE_STRING_MANUFACTURER,
1171 OPENTYPE_STRING_DESIGNER,
1172 OPENTYPE_STRING_DESIGNER_URL,
1173 OPENTYPE_STRING_DESCRIPTION,
1174 OPENTYPE_STRING_VENDOR_URL,
1175 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1176 OPENTYPE_STRING_LICENSE_INFO_URL,
1177 OPENTYPE_STRING_FAMILY_NAME,
1178 OPENTYPE_STRING_SUBFAMILY_NAME,
1179 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1180 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1181 OPENTYPE_STRING_SAMPLE_TEXT,
1182 OPENTYPE_STRING_FULL_FONTNAME,
1183 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1184 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1185 OPENTYPE_STRING_WWS_FAMILY_NAME,
1188 /* CPAL table */
1189 struct cpal_header_0
1191 USHORT version;
1192 USHORT num_palette_entries;
1193 USHORT num_palettes;
1194 USHORT num_color_records;
1195 ULONG offset_first_color_record;
1196 USHORT color_record_indices[1];
1199 struct cpal_color_record
1201 BYTE blue;
1202 BYTE green;
1203 BYTE red;
1204 BYTE alpha;
1207 /* COLR table */
1208 struct colr_header
1210 USHORT version;
1211 USHORT num_baseglyph_records;
1212 ULONG offset_baseglyph_records;
1213 ULONG offset_layer_records;
1214 USHORT num_layer_records;
1217 struct colr_baseglyph_record
1219 USHORT glyph;
1220 USHORT first_layer_index;
1221 USHORT num_layers;
1224 struct colr_layer_record
1226 USHORT glyph;
1227 USHORT palette_index;
1230 struct meta_data_map
1232 DWORD tag;
1233 DWORD offset;
1234 DWORD length;
1237 struct meta_header
1239 DWORD version;
1240 DWORD flags;
1241 DWORD reserved;
1242 DWORD data_maps_count;
1243 struct meta_data_map maps[1];
1246 static const void *table_read_ensure(const struct dwrite_fonttable *table, unsigned int offset, unsigned int size)
1248 if (size > table->size || offset > table->size - size)
1249 return NULL;
1251 return table->data + offset;
1254 static WORD table_read_be_word(const struct dwrite_fonttable *table, unsigned int offset)
1256 const WORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1257 return ptr ? GET_BE_WORD(*ptr) : 0;
1260 static DWORD table_read_be_dword(const struct dwrite_fonttable *table, unsigned int offset)
1262 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1263 return ptr ? GET_BE_DWORD(*ptr) : 0;
1266 static DWORD table_read_dword(const struct dwrite_fonttable *table, unsigned int offset)
1268 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1269 return ptr ? *ptr : 0;
1272 BOOL is_face_type_supported(DWRITE_FONT_FACE_TYPE type)
1274 return (type == DWRITE_FONT_FACE_TYPE_CFF) ||
1275 (type == DWRITE_FONT_FACE_TYPE_TRUETYPE) ||
1276 (type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) ||
1277 (type == DWRITE_FONT_FACE_TYPE_RAW_CFF);
1280 typedef HRESULT (*dwrite_fontfile_analyzer)(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1281 DWRITE_FONT_FACE_TYPE *face_type);
1283 static HRESULT opentype_ttc_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1284 DWRITE_FONT_FACE_TYPE *face_type)
1286 static const DWORD ttctag = MS_TTCF_TAG;
1287 const TTC_Header_V1 *header;
1288 void *context;
1289 HRESULT hr;
1291 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(header), &context);
1292 if (FAILED(hr))
1293 return hr;
1295 if (!memcmp(header->TTCTag, &ttctag, sizeof(ttctag))) {
1296 *font_count = GET_BE_DWORD(header->numFonts);
1297 *file_type = DWRITE_FONT_FILE_TYPE_OPENTYPE_COLLECTION;
1298 *face_type = DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION;
1301 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1303 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1306 static HRESULT opentype_ttf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1307 DWRITE_FONT_FACE_TYPE *face_type)
1309 const DWORD *header;
1310 void *context;
1311 HRESULT hr;
1313 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1314 if (FAILED(hr))
1315 return hr;
1317 if (GET_BE_DWORD(*header) == 0x10000) {
1318 *font_count = 1;
1319 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
1320 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
1323 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1325 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1328 static HRESULT opentype_otf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1329 DWRITE_FONT_FACE_TYPE *face_type)
1331 const DWORD *header;
1332 void *context;
1333 HRESULT hr;
1335 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1336 if (FAILED(hr))
1337 return hr;
1339 if (GET_BE_DWORD(*header) == MS_OTTO_TAG) {
1340 *font_count = 1;
1341 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
1342 *face_type = DWRITE_FONT_FACE_TYPE_CFF;
1345 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1347 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1350 static HRESULT opentype_type1_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1351 DWRITE_FONT_FACE_TYPE *face_type)
1353 #include "pshpack1.h"
1354 /* Specified in Adobe TechNote #5178 */
1355 struct pfm_header {
1356 WORD dfVersion;
1357 DWORD dfSize;
1358 char data0[95];
1359 DWORD dfDevice;
1360 char data1[12];
1362 #include "poppack.h"
1363 struct type1_header {
1364 WORD tag;
1365 char data[14];
1367 const struct type1_header *header;
1368 void *context;
1369 HRESULT hr;
1371 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1372 if (FAILED(hr))
1373 return hr;
1375 /* tag is followed by plain text section */
1376 if (header->tag == 0x8001 &&
1377 (!memcmp(header->data, "%!PS-AdobeFont", 14) ||
1378 !memcmp(header->data, "%!FontType", 10))) {
1379 *font_count = 1;
1380 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFB;
1381 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1384 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1386 /* let's see if it's a .pfm metrics file */
1387 if (*file_type == DWRITE_FONT_FILE_TYPE_UNKNOWN) {
1388 const struct pfm_header *pfm_header;
1389 UINT64 filesize;
1390 DWORD offset;
1391 BOOL header_checked;
1393 hr = IDWriteFontFileStream_GetFileSize(stream, &filesize);
1394 if (FAILED(hr))
1395 return hr;
1397 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&pfm_header, 0, sizeof(*pfm_header), &context);
1398 if (FAILED(hr))
1399 return hr;
1401 offset = pfm_header->dfDevice;
1402 header_checked = pfm_header->dfVersion == 0x100 && pfm_header->dfSize == filesize;
1403 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1405 /* as a last test check static string in PostScript information section */
1406 if (header_checked) {
1407 static const char postscript[] = "PostScript";
1408 char *devtype_name;
1410 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&devtype_name, offset, sizeof(postscript), &context);
1411 if (FAILED(hr))
1412 return hr;
1414 if (!memcmp(devtype_name, postscript, sizeof(postscript))) {
1415 *font_count = 1;
1416 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFM;
1417 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1420 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1424 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1427 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, BOOL *supported, DWRITE_FONT_FILE_TYPE *file_type,
1428 DWRITE_FONT_FACE_TYPE *face_type, UINT32 *face_count)
1430 static dwrite_fontfile_analyzer fontfile_analyzers[] = {
1431 opentype_ttf_analyzer,
1432 opentype_otf_analyzer,
1433 opentype_ttc_analyzer,
1434 opentype_type1_analyzer,
1435 NULL
1437 dwrite_fontfile_analyzer *analyzer = fontfile_analyzers;
1438 DWRITE_FONT_FACE_TYPE face;
1439 HRESULT hr;
1441 if (!face_type)
1442 face_type = &face;
1444 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
1445 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
1446 *face_count = 0;
1448 while (*analyzer) {
1449 hr = (*analyzer)(stream, face_count, file_type, face_type);
1450 if (FAILED(hr))
1451 return hr;
1453 if (hr == S_OK)
1454 break;
1456 analyzer++;
1459 *supported = is_face_type_supported(*face_type);
1460 return S_OK;
1463 HRESULT opentype_try_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag, const void **table_data,
1464 void **table_context, UINT32 *table_size, BOOL *found)
1466 void *table_directory_context, *sfnt_context;
1467 TT_TableRecord *table_record = NULL;
1468 TTC_SFNT_V1 *font_header = NULL;
1469 UINT32 table_offset = 0;
1470 UINT16 table_count;
1471 HRESULT hr;
1473 if (found) *found = FALSE;
1474 if (table_size) *table_size = 0;
1476 *table_data = NULL;
1477 *table_context = NULL;
1479 if (stream_desc->face_type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) {
1480 const TTC_Header_V1 *ttc_header;
1481 void * ttc_context;
1482 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
1483 if (SUCCEEDED(hr)) {
1484 if (stream_desc->face_index >= GET_BE_DWORD(ttc_header->numFonts))
1485 hr = E_INVALIDARG;
1486 else {
1487 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[stream_desc->face_index]);
1488 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
1490 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, ttc_context);
1493 else
1494 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
1496 if (FAILED(hr))
1497 return hr;
1499 table_count = GET_BE_WORD(font_header->numTables);
1500 table_offset += sizeof(*font_header);
1502 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, sfnt_context);
1504 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void **)&table_record, table_offset,
1505 table_count * sizeof(*table_record), &table_directory_context);
1506 if (hr == S_OK) {
1507 UINT16 i;
1509 for (i = 0; i < table_count; i++) {
1510 if (table_record->tag == tag) {
1511 UINT32 offset = GET_BE_DWORD(table_record->offset);
1512 UINT32 length = GET_BE_DWORD(table_record->length);
1514 if (found)
1515 *found = TRUE;
1516 if (table_size)
1517 *table_size = length;
1518 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, table_data, offset,
1519 length, table_context);
1520 break;
1522 table_record++;
1525 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, table_directory_context);
1528 return hr;
1531 static HRESULT opentype_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag,
1532 struct dwrite_fonttable *table)
1534 return opentype_try_get_font_table(stream_desc, tag, (const void **)&table->data, &table->context, &table->size, &table->exists);
1537 /**********
1538 * CMAP
1539 **********/
1541 static UINT16 opentype_cmap_format0_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1543 const UINT8 *glyphs = cmap->data;
1544 return (ch < 0xff) ? glyphs[ch] : 0;
1547 static unsigned int opentype_cmap_format0_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1548 DWRITE_UNICODE_RANGE *ranges)
1550 if (count > 0)
1552 ranges->first = 0;
1553 ranges->last = 255;
1556 return 1;
1559 struct cmap_format4_compare_context
1561 const struct dwrite_cmap *cmap;
1562 unsigned int ch;
1565 static int cmap_format4_compare_range(const void *a, const void *b)
1567 const struct cmap_format4_compare_context *key = a;
1568 const UINT16 *end = b;
1569 unsigned int idx;
1571 if (key->ch > GET_BE_WORD(*end))
1572 return 1;
1574 idx = end - key->cmap->u.format4.ends;
1575 if (key->ch < GET_BE_WORD(key->cmap->u.format4.starts[idx]))
1576 return -1;
1578 return 0;
1581 static UINT16 opentype_cmap_format4_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1583 struct cmap_format4_compare_context key = { .cmap = cmap, .ch = ch };
1584 unsigned int glyph, idx, range_offset;
1585 const UINT16 *end_found;
1587 /* Look up range. */
1588 end_found = bsearch(&key, cmap->u.format4.ends, cmap->u.format4.seg_count, sizeof(*cmap->u.format4.ends),
1589 cmap_format4_compare_range);
1590 if (!end_found)
1591 return 0;
1593 idx = end_found - cmap->u.format4.ends;
1595 range_offset = GET_BE_WORD(cmap->u.format4.id_range_offset[idx]);
1597 if (!range_offset)
1599 glyph = ch + GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1601 else
1603 unsigned int index = range_offset / 2 + (ch - GET_BE_WORD(cmap->u.format4.starts[idx])) + idx - cmap->u.format4.seg_count;
1604 if (index >= cmap->u.format4.glyph_id_array_len)
1605 return 0;
1606 glyph = GET_BE_WORD(cmap->u.format4.glyph_id_array[index]);
1607 if (!glyph)
1608 return 0;
1609 glyph += GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1612 return glyph & 0xffff;
1615 static unsigned int opentype_cmap_format4_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1616 DWRITE_UNICODE_RANGE *ranges)
1618 unsigned int i;
1620 count = min(count, cmap->u.format4.seg_count);
1622 for (i = 0; i < count; ++i)
1624 ranges[i].first = GET_BE_WORD(cmap->u.format4.starts[i]);
1625 ranges[i].last = GET_BE_WORD(cmap->u.format4.ends[i]);
1628 return cmap->u.format4.seg_count;
1631 static UINT16 opentype_cmap_format6_10_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1633 const UINT16 *glyphs = cmap->data;
1634 if (ch < cmap->u.format6_10.first || ch > cmap->u.format6_10.last) return 0;
1635 return glyphs[ch - cmap->u.format6_10.first];
1638 static unsigned int opentype_cmap_format6_10_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1639 DWRITE_UNICODE_RANGE *ranges)
1641 if (count > 0)
1643 ranges->first = cmap->u.format6_10.first;
1644 ranges->last = cmap->u.format6_10.last;
1647 return 1;
1650 static int cmap_format12_13_compare_group(const void *a, const void *b)
1652 const unsigned int *ch = a;
1653 const UINT32 *group = b;
1655 if (*ch > GET_BE_DWORD(group[1]))
1656 return 1;
1658 if (*ch < GET_BE_DWORD(group[0]))
1659 return -1;
1661 return 0;
1664 static UINT16 opentype_cmap_format12_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1666 const UINT32 *groups = cmap->data;
1667 const UINT32 *group_found;
1669 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1670 cmap_format12_13_compare_group)))
1671 return 0;
1673 return GET_BE_DWORD(group_found[0]) <= GET_BE_DWORD(group_found[1]) ?
1674 GET_BE_DWORD(group_found[2]) + (ch - GET_BE_DWORD(group_found[0])) : 0;
1677 static unsigned int opentype_cmap_format12_13_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1678 DWRITE_UNICODE_RANGE *ranges)
1680 unsigned int i, group_count = cmap->u.format12_13.group_count;
1681 const UINT32 *groups = cmap->data;
1683 count = min(count, group_count);
1685 for (i = 0; i < count; ++i)
1687 ranges[i].first = GET_BE_DWORD(groups[3 * i]);
1688 ranges[i].last = GET_BE_DWORD(groups[3 * i + 1]);
1691 return group_count;
1694 static UINT16 opentype_cmap_format13_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1696 const UINT32 *groups = cmap->data;
1697 const UINT32 *group_found;
1699 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1700 cmap_format12_13_compare_group)))
1701 return 0;
1703 return GET_BE_DWORD(group_found[2]);
1706 static UINT16 opentype_cmap_dummy_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1708 return 0;
1711 static unsigned int opentype_cmap_dummy_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1712 DWRITE_UNICODE_RANGE *ranges)
1714 return 0;
1717 UINT16 opentype_cmap_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1719 UINT16 glyph;
1721 if (!cmap->get_glyph) return 0;
1722 glyph = cmap->get_glyph(cmap, ch);
1723 if (!glyph && cmap->symbol && ch <= 0xff)
1724 glyph = cmap->get_glyph(cmap, ch + 0xf000);
1725 return glyph;
1728 static int cmap_header_compare(const void *a, const void *b)
1730 const UINT16 *key = a;
1731 const UINT16 *record = b;
1733 /* Platform. */
1734 if (key[0] < GET_BE_WORD(record[0])) return -1;
1735 if (key[0] > GET_BE_WORD(record[0])) return 1;
1736 /* Encoding. */
1737 if (key[1] < GET_BE_WORD(record[1])) return -1;
1738 if (key[1] > GET_BE_WORD(record[1])) return 1;
1740 return 0;
1743 void dwrite_cmap_init(struct dwrite_cmap *cmap, IDWriteFontFile *file, unsigned int face_index,
1744 DWRITE_FONT_FACE_TYPE face_type)
1746 static const UINT16 encodings[][2] =
1748 { 3, 0 }, /* MS Symbol encoding is preferred. */
1749 { 3, 10 },
1750 { 0, 6 },
1751 { 0, 4 },
1752 { 3, 1 },
1753 { 0, 3 },
1754 { 0, 2 },
1755 { 0, 1 },
1756 { 0, 0 },
1758 const struct cmap_encoding_record *records, *found_record = NULL;
1759 unsigned int length, offset, format, count, f, i, num_records;
1760 struct file_stream_desc stream_desc;
1761 struct dwrite_fonttable table;
1762 const UINT16 *pair = NULL;
1763 HRESULT hr;
1765 if (cmap->data) return;
1767 /* For fontface stream is already available and preset. */
1768 if (!cmap->stream && FAILED(hr = get_filestream_from_file(file, &cmap->stream)))
1770 WARN("Failed to get file stream, hr %#x.\n", hr);
1771 goto failed;
1774 stream_desc.stream = cmap->stream;
1775 stream_desc.face_type = face_type;
1776 stream_desc.face_index = face_index;
1778 opentype_get_font_table(&stream_desc, MS_CMAP_TAG, &table);
1779 if (!table.exists)
1780 goto failed;
1781 cmap->table_context = table.context;
1783 num_records = table_read_be_word(&table, 2);
1784 records = table_read_ensure(&table, 4, sizeof(*records) * num_records);
1786 for (i = 0; i < ARRAY_SIZE(encodings); ++i)
1788 pair = encodings[i];
1789 if ((found_record = bsearch(pair, records, num_records, sizeof(*records), cmap_header_compare)))
1790 break;
1793 if (!found_record)
1795 WARN("No suitable cmap table were found.\n");
1796 goto failed;
1799 /* Symbol encoding. */
1800 cmap->symbol = pair[0] == 3 && pair[1] == 0;
1801 offset = GET_BE_DWORD(found_record->offset);
1803 format = table_read_be_word(&table, offset);
1805 switch (format)
1807 case 0:
1808 cmap->data = table_read_ensure(&table, offset + 6, 256);
1809 cmap->get_glyph = opentype_cmap_format0_get_glyph;
1810 cmap->get_ranges = opentype_cmap_format0_get_ranges;
1811 break;
1812 case 4:
1813 length = table_read_be_word(&table, offset + 2);
1814 cmap->u.format4.seg_count = count = table_read_be_word(&table, offset + 6) / 2;
1815 cmap->u.format4.ends = table_read_ensure(&table, offset + 14, count * 2);
1816 cmap->u.format4.starts = cmap->u.format4.ends + count + 1;
1817 cmap->u.format4.id_delta = cmap->u.format4.starts + count;
1818 cmap->u.format4.id_range_offset = cmap->u.format4.id_delta + count;
1819 cmap->u.format4.glyph_id_array = cmap->data = cmap->u.format4.id_range_offset + count;
1820 cmap->u.format4.glyph_id_array_len = (length - 16 - 8 * count) / 2;
1821 cmap->get_glyph = opentype_cmap_format4_get_glyph;
1822 cmap->get_ranges = opentype_cmap_format4_get_ranges;
1823 break;
1824 case 6:
1825 case 10:
1826 /* Format 10 uses 4 byte fields. */
1827 f = format == 6 ? 1 : 2;
1828 cmap->u.format6_10.first = table_read_be_word(&table, offset + f * 6);
1829 count = table_read_be_word(&table, offset + f * 8);
1830 cmap->u.format6_10.last = cmap->u.format6_10.first + count;
1831 cmap->data = table_read_ensure(&table, offset + f * 10, count * 2);
1832 cmap->get_glyph = opentype_cmap_format6_10_get_glyph;
1833 cmap->get_ranges = opentype_cmap_format6_10_get_ranges;
1834 break;
1835 case 12:
1836 case 13:
1837 cmap->u.format12_13.group_count = count = table_read_be_dword(&table, offset + 12);
1838 cmap->data = table_read_ensure(&table, offset + 16, count * 3 * 4);
1839 cmap->get_glyph = format == 12 ? opentype_cmap_format12_get_glyph : opentype_cmap_format13_get_glyph;
1840 cmap->get_ranges = opentype_cmap_format12_13_get_ranges;
1841 break;
1842 default:
1843 WARN("Unhandled subtable format %u.\n", format);
1846 failed:
1848 if (!cmap->data)
1850 /* Dummy implementation, returns 0 unconditionally. */
1851 cmap->data = cmap;
1852 cmap->get_glyph = opentype_cmap_dummy_get_glyph;
1853 cmap->get_ranges = opentype_cmap_dummy_get_ranges;
1857 void dwrite_cmap_release(struct dwrite_cmap *cmap)
1859 if (cmap->stream)
1861 IDWriteFontFileStream_ReleaseFileFragment(cmap->stream, cmap->table_context);
1862 IDWriteFontFileStream_Release(cmap->stream);
1864 cmap->data = NULL;
1865 cmap->stream = NULL;
1868 HRESULT opentype_cmap_get_unicode_ranges(const struct dwrite_cmap *cmap, unsigned int max_count, DWRITE_UNICODE_RANGE *ranges,
1869 unsigned int *count)
1871 if (!cmap->data)
1872 return E_FAIL;
1874 *count = cmap->get_ranges(cmap, max_count, ranges);
1876 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
1879 void opentype_get_font_typo_metrics(struct file_stream_desc *stream_desc, unsigned int *ascent, unsigned int *descent)
1881 struct dwrite_fonttable os2;
1883 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1885 *ascent = *descent = 0;
1887 if (os2.size >= FIELD_OFFSET(struct tt_os2, sTypoLineGap))
1889 SHORT value = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoDescender));
1890 *ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoAscender));
1891 *descent = value < 0 ? -value : 0;
1894 if (os2.data)
1895 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
1898 void opentype_get_font_metrics(struct file_stream_desc *stream_desc, DWRITE_FONT_METRICS1 *metrics, DWRITE_CARET_METRICS *caret)
1900 struct dwrite_fonttable os2, head, post, hhea;
1902 memset(metrics, 0, sizeof(*metrics));
1904 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1905 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
1906 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
1907 opentype_get_font_table(stream_desc, MS_HHEA_TAG, &hhea);
1909 if (head.data)
1911 metrics->designUnitsPerEm = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, unitsPerEm));
1912 metrics->glyphBoxLeft = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, xMin));
1913 metrics->glyphBoxTop = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, yMax));
1914 metrics->glyphBoxRight = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, xMax));
1915 metrics->glyphBoxBottom = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, yMin));
1918 if (caret)
1920 if (hhea.data)
1922 caret->slopeRise = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretSlopeRise));
1923 caret->slopeRun = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretSlopeRun));
1924 caret->offset = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretOffset));
1926 else
1927 memset(caret, 0, sizeof(*caret));
1930 if (os2.data)
1932 USHORT version = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, version));
1934 metrics->ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWinAscent));
1935 /* Some fonts have usWinDescent value stored as signed short, which could be wrongly
1936 interpreted as large unsigned value. */
1937 metrics->descent = abs((SHORT)table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWinDescent)));
1939 /* Line gap is estimated using two sets of ascender/descender values and 'hhea' line gap. */
1940 if (hhea.data)
1942 SHORT descender = (SHORT)table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, descender));
1943 INT32 linegap;
1945 linegap = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, ascender)) + abs(descender) +
1946 table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, linegap)) - metrics->ascent - metrics->descent;
1947 metrics->lineGap = linegap > 0 ? linegap : 0;
1950 metrics->strikethroughPosition = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, yStrikeoutPosition));
1951 metrics->strikethroughThickness = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, yStrikeoutSize));
1952 metrics->subscriptPositionX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptXOffset));
1953 /* Y offset is stored as positive offset below baseline */
1954 metrics->subscriptPositionY = -table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptYOffset));
1955 metrics->subscriptSizeX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptXSize));
1956 metrics->subscriptSizeY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptYSize));
1957 metrics->superscriptPositionX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptXOffset));
1958 metrics->superscriptPositionY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptYOffset));
1959 metrics->superscriptSizeX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptXSize));
1960 metrics->superscriptSizeY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptYSize));
1962 /* version 2 fields */
1963 if (version >= 2)
1965 metrics->capHeight = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sCapHeight));
1966 metrics->xHeight = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sxHeight));
1969 if (table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) & OS2_FSSELECTION_USE_TYPO_METRICS)
1971 SHORT descent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoDescender));
1972 metrics->ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoAscender));
1973 metrics->descent = descent < 0 ? -descent : 0;
1974 metrics->lineGap = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoLineGap));
1975 metrics->hasTypographicMetrics = TRUE;
1978 else
1980 metrics->strikethroughPosition = metrics->designUnitsPerEm / 3;
1981 if (hhea.data)
1983 metrics->ascent = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, ascender));
1984 metrics->descent = abs((SHORT)table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, descender)));
1988 if (post.data)
1990 metrics->underlinePosition = table_read_be_word(&post, FIELD_OFFSET(struct tt_post, underlinePosition));
1991 metrics->underlineThickness = table_read_be_word(&post, FIELD_OFFSET(struct tt_post, underlineThickness));
1994 if (metrics->underlineThickness == 0)
1995 metrics->underlineThickness = metrics->designUnitsPerEm / 14;
1996 if (metrics->strikethroughThickness == 0)
1997 metrics->strikethroughThickness = metrics->underlineThickness;
1999 /* estimate missing metrics */
2000 if (metrics->xHeight == 0)
2001 metrics->xHeight = metrics->designUnitsPerEm / 2;
2002 if (metrics->capHeight == 0)
2003 metrics->capHeight = metrics->designUnitsPerEm * 7 / 10;
2005 if (os2.data)
2006 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2007 if (head.data)
2008 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2009 if (post.data)
2010 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2011 if (hhea.data)
2012 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, hhea.context);
2015 void opentype_get_font_properties(struct file_stream_desc *stream_desc, struct dwrite_font_props *props)
2017 struct dwrite_fonttable os2, head, colr, cpal;
2018 BOOL is_symbol, is_monospaced;
2020 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2021 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
2023 /* default stretch, weight and style to normal */
2024 props->stretch = DWRITE_FONT_STRETCH_NORMAL;
2025 props->weight = DWRITE_FONT_WEIGHT_NORMAL;
2026 props->style = DWRITE_FONT_STYLE_NORMAL;
2027 memset(&props->panose, 0, sizeof(props->panose));
2028 memset(&props->fontsig, 0, sizeof(props->fontsig));
2029 memset(&props->lf, 0, sizeof(props->lf));
2030 props->flags = 0;
2032 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
2033 if (os2.data)
2035 USHORT version = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, version));
2036 USHORT fsSelection = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection));
2037 USHORT usWeightClass = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWeightClass));
2038 USHORT usWidthClass = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWidthClass));
2039 const void *panose;
2041 if (usWidthClass > DWRITE_FONT_STRETCH_UNDEFINED && usWidthClass <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
2042 props->stretch = usWidthClass;
2044 if (usWeightClass >= 1 && usWeightClass <= 9)
2045 usWeightClass *= 100;
2047 if (usWeightClass > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
2048 props->weight = DWRITE_FONT_WEIGHT_ULTRA_BLACK;
2049 else if (usWeightClass > 0)
2050 props->weight = usWeightClass;
2052 if (version >= 4 && (fsSelection & OS2_FSSELECTION_OBLIQUE))
2053 props->style = DWRITE_FONT_STYLE_OBLIQUE;
2054 else if (fsSelection & OS2_FSSELECTION_ITALIC)
2055 props->style = DWRITE_FONT_STYLE_ITALIC;
2056 props->lf.lfItalic = !!(fsSelection & OS2_FSSELECTION_ITALIC);
2058 if ((panose = table_read_ensure(&os2, FIELD_OFFSET(struct tt_os2, panose), sizeof(props->panose))))
2059 memcpy(&props->panose, panose, sizeof(props->panose));
2061 /* FONTSIGNATURE */
2062 props->fontsig.fsUsb[0] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange1));
2063 props->fontsig.fsUsb[1] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange2));
2064 props->fontsig.fsUsb[2] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange3));
2065 props->fontsig.fsUsb[3] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange4));
2067 if (version)
2069 props->fontsig.fsCsb[0] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulCodePageRange1));
2070 props->fontsig.fsCsb[1] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulCodePageRange2));
2073 else if (head.data)
2075 USHORT macStyle = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, macStyle));
2077 if (macStyle & TT_HEAD_MACSTYLE_CONDENSED)
2078 props->stretch = DWRITE_FONT_STRETCH_CONDENSED;
2079 else if (macStyle & TT_HEAD_MACSTYLE_EXTENDED)
2080 props->stretch = DWRITE_FONT_STRETCH_EXPANDED;
2082 if (macStyle & TT_HEAD_MACSTYLE_BOLD)
2083 props->weight = DWRITE_FONT_WEIGHT_BOLD;
2085 if (macStyle & TT_HEAD_MACSTYLE_ITALIC) {
2086 props->style = DWRITE_FONT_STYLE_ITALIC;
2087 props->lf.lfItalic = 1;
2091 props->lf.lfWeight = props->weight;
2093 /* FONT_IS_SYMBOL */
2094 if (!(is_symbol = props->panose.familyKind == DWRITE_PANOSE_FAMILY_SYMBOL))
2096 struct dwrite_fonttable cmap;
2097 int i, offset, num_tables;
2099 opentype_get_font_table(stream_desc, MS_CMAP_TAG, &cmap);
2101 if (cmap.data)
2103 num_tables = table_read_be_word(&cmap, FIELD_OFFSET(struct cmap_header, num_tables));
2104 offset = FIELD_OFFSET(struct cmap_header, tables);
2106 for (i = 0; !is_symbol && i < num_tables; ++i)
2108 WORD platform, encoding;
2110 platform = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2111 FIELD_OFFSET(struct cmap_encoding_record, platformID));
2112 encoding = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2113 FIELD_OFFSET(struct cmap_encoding_record, encodingID));
2115 is_symbol = platform == OPENTYPE_CMAP_TABLE_PLATFORM_WIN &&
2116 encoding == OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL;
2119 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cmap.context);
2122 if (is_symbol)
2123 props->flags |= FONT_IS_SYMBOL;
2125 /* FONT_IS_MONOSPACED */
2126 if (!(is_monospaced = props->panose.text.proportion == DWRITE_PANOSE_PROPORTION_MONOSPACED))
2128 struct dwrite_fonttable post;
2130 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
2132 if (post.data)
2134 is_monospaced = !!table_read_dword(&post, FIELD_OFFSET(struct tt_post, fixed_pitch));
2136 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2139 if (is_monospaced)
2140 props->flags |= FONT_IS_MONOSPACED;
2142 /* FONT_IS_COLORED */
2143 opentype_get_font_table(stream_desc, MS_COLR_TAG, &colr);
2144 if (colr.data)
2146 opentype_get_font_table(stream_desc, MS_CPAL_TAG, &cpal);
2147 if (cpal.data)
2149 props->flags |= FONT_IS_COLORED;
2150 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cpal.context);
2153 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, colr.context);
2156 TRACE("stretch=%d, weight=%d, style %d\n", props->stretch, props->weight, props->style);
2158 if (os2.data)
2159 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2160 if (head.data)
2161 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2164 static UINT get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform, USHORT encoding)
2166 UINT codepage = 0;
2168 switch (platform) {
2169 case OPENTYPE_PLATFORM_UNICODE:
2170 break;
2171 case OPENTYPE_PLATFORM_MAC:
2172 switch (encoding)
2174 case TT_NAME_MAC_ENCODING_ROMAN:
2175 codepage = 10000;
2176 break;
2177 case TT_NAME_MAC_ENCODING_JAPANESE:
2178 codepage = 10001;
2179 break;
2180 case TT_NAME_MAC_ENCODING_TRAD_CHINESE:
2181 codepage = 10002;
2182 break;
2183 case TT_NAME_MAC_ENCODING_KOREAN:
2184 codepage = 10003;
2185 break;
2186 case TT_NAME_MAC_ENCODING_ARABIC:
2187 codepage = 10004;
2188 break;
2189 case TT_NAME_MAC_ENCODING_HEBREW:
2190 codepage = 10005;
2191 break;
2192 case TT_NAME_MAC_ENCODING_GREEK:
2193 codepage = 10006;
2194 break;
2195 case TT_NAME_MAC_ENCODING_RUSSIAN:
2196 codepage = 10007;
2197 break;
2198 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE:
2199 codepage = 10008;
2200 break;
2201 case TT_NAME_MAC_ENCODING_THAI:
2202 codepage = 10021;
2203 break;
2204 default:
2205 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2206 break;
2208 break;
2209 case OPENTYPE_PLATFORM_WIN:
2210 switch (encoding)
2212 case TT_NAME_WINDOWS_ENCODING_SYMBOL:
2213 case TT_NAME_WINDOWS_ENCODING_UNICODE_BMP:
2214 case TT_NAME_WINDOWS_ENCODING_UNICODE_FULL:
2215 break;
2216 case TT_NAME_WINDOWS_ENCODING_SJIS:
2217 codepage = 932;
2218 break;
2219 case TT_NAME_WINDOWS_ENCODING_PRC:
2220 codepage = 936;
2221 break;
2222 case TT_NAME_WINDOWS_ENCODING_BIG5:
2223 codepage = 950;
2224 break;
2225 case TT_NAME_WINDOWS_ENCODING_WANSUNG:
2226 codepage = 20949;
2227 break;
2228 case TT_NAME_WINDOWS_ENCODING_JOHAB:
2229 codepage = 1361;
2230 break;
2231 default:
2232 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2233 break;
2235 break;
2236 default:
2237 FIXME("unknown platform %d\n", platform);
2240 return codepage;
2243 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform, USHORT lang_id, WCHAR *locale, USHORT locale_len)
2245 static const WCHAR enusW[] = {'e','n','-','U','S',0};
2247 switch (platform) {
2248 case OPENTYPE_PLATFORM_MAC:
2250 const char *locale_name = NULL;
2252 if (lang_id > TT_NAME_MAC_LANGID_AZER_ROMAN)
2253 WARN("invalid mac lang id %d\n", lang_id);
2254 else if (!name_mac_langid_to_locale[lang_id][0])
2255 FIXME("failed to map mac lang id %d to locale name\n", lang_id);
2256 else
2257 locale_name = name_mac_langid_to_locale[lang_id];
2259 if (locale_name)
2260 MultiByteToWideChar(CP_ACP, 0, name_mac_langid_to_locale[lang_id], -1, locale, locale_len);
2261 else
2262 strcpyW(locale, enusW);
2263 break;
2265 case OPENTYPE_PLATFORM_WIN:
2266 if (!LCIDToLocaleName(MAKELCID(lang_id, SORT_DEFAULT), locale, locale_len, 0)) {
2267 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id, SORT_DEFAULT));
2268 strcpyW(locale, enusW);
2270 break;
2271 case OPENTYPE_PLATFORM_UNICODE:
2272 strcpyW(locale, enusW);
2273 break;
2274 default:
2275 FIXME("unknown platform %d\n", platform);
2279 static BOOL opentype_decode_namerecord(const TT_NAME_V0 *header, BYTE *storage_area, USHORT recid, IDWriteLocalizedStrings *strings)
2281 const TT_NameRecord *record = &header->nameRecord[recid];
2282 USHORT lang_id, length, offset, encoding, platform;
2283 BOOL ret = FALSE;
2285 platform = GET_BE_WORD(record->platformID);
2286 lang_id = GET_BE_WORD(record->languageID);
2287 length = GET_BE_WORD(record->length);
2288 offset = GET_BE_WORD(record->offset);
2289 encoding = GET_BE_WORD(record->encodingID);
2291 if (lang_id < 0x8000) {
2292 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
2293 WCHAR *name_string;
2294 UINT codepage;
2296 codepage = get_name_record_codepage(platform, encoding);
2297 get_name_record_locale(platform, lang_id, locale, ARRAY_SIZE(locale));
2299 if (codepage) {
2300 DWORD len = MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, NULL, 0);
2301 name_string = heap_alloc(sizeof(WCHAR) * (len+1));
2302 MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, name_string, len);
2303 name_string[len] = 0;
2305 else {
2306 int i;
2308 length /= sizeof(WCHAR);
2309 name_string = heap_strdupnW((LPWSTR)(storage_area + offset), length);
2310 for (i = 0; i < length; i++)
2311 name_string[i] = GET_BE_WORD(name_string[i]);
2314 TRACE("string %s for locale %s found\n", debugstr_w(name_string), debugstr_w(locale));
2315 add_localizedstring(strings, locale, name_string);
2316 heap_free(name_string);
2317 ret = TRUE;
2319 else
2320 FIXME("handle NAME format 1\n");
2322 return ret;
2325 static HRESULT opentype_get_font_strings_from_id(const void *table_data, enum OPENTYPE_STRING_ID id, IDWriteLocalizedStrings **strings)
2327 int i, count, candidate_mac, candidate_unicode;
2328 const TT_NAME_V0 *header;
2329 BYTE *storage_area = 0;
2330 WORD format;
2331 BOOL exists;
2332 HRESULT hr;
2334 if (!table_data)
2335 return E_FAIL;
2337 hr = create_localizedstrings(strings);
2338 if (FAILED(hr)) return hr;
2340 header = table_data;
2341 format = GET_BE_WORD(header->format);
2343 switch (format) {
2344 case 0:
2345 case 1:
2346 break;
2347 default:
2348 FIXME("unsupported NAME format %d\n", format);
2351 storage_area = (LPBYTE)table_data + GET_BE_WORD(header->stringOffset);
2352 count = GET_BE_WORD(header->count);
2354 exists = FALSE;
2355 candidate_unicode = candidate_mac = -1;
2356 for (i = 0; i < count; i++) {
2357 const TT_NameRecord *record = &header->nameRecord[i];
2358 USHORT platform;
2360 if (GET_BE_WORD(record->nameID) != id)
2361 continue;
2363 platform = GET_BE_WORD(record->platformID);
2364 switch (platform)
2366 /* Skip Unicode or Mac entries for now, fonts tend to duplicate those
2367 strings as WIN platform entries. If font does not have WIN entry for
2368 this id, we will use Mac or Unicode platform entry while assuming
2369 en-US locale. */
2370 case OPENTYPE_PLATFORM_UNICODE:
2371 if (candidate_unicode == -1)
2372 candidate_unicode = i;
2373 break;
2374 case OPENTYPE_PLATFORM_MAC:
2375 if (candidate_mac == -1)
2376 candidate_mac = i;
2377 break;
2378 case OPENTYPE_PLATFORM_WIN:
2379 if (opentype_decode_namerecord(header, storage_area, i, *strings))
2380 exists = TRUE;
2381 break;
2382 default:
2383 FIXME("platform %i not supported\n", platform);
2384 break;
2388 if (!exists)
2390 if (candidate_mac != -1)
2391 exists = opentype_decode_namerecord(header, storage_area, candidate_mac, *strings);
2392 if (!exists && candidate_unicode != -1)
2393 exists = opentype_decode_namerecord(header, storage_area, candidate_unicode, *strings);
2395 if (!exists)
2397 IDWriteLocalizedStrings_Release(*strings);
2398 *strings = NULL;
2402 if (*strings)
2403 sort_localizedstrings(*strings);
2405 return exists ? S_OK : E_FAIL;
2408 static WCHAR *meta_get_lng_name(WCHAR *str, WCHAR **ctx)
2410 static const WCHAR delimW[] = {',',' ',0};
2411 WCHAR *ret;
2413 if (!str) str = *ctx;
2414 while (*str && strchrW(delimW, *str)) str++;
2415 if (!*str) return NULL;
2416 ret = str++;
2417 while (*str && !strchrW(delimW, *str)) str++;
2418 if (*str) *str++ = 0;
2419 *ctx = str;
2421 return ret;
2424 static HRESULT opentype_get_font_strings_from_meta(const struct file_stream_desc *stream_desc,
2425 DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **ret)
2427 static const WCHAR emptyW[] = { 0 };
2428 const struct meta_data_map *maps;
2429 IDWriteLocalizedStrings *strings;
2430 struct dwrite_fonttable meta;
2431 DWORD version, i, count, tag;
2432 HRESULT hr;
2434 *ret = NULL;
2436 switch (id)
2438 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2439 tag = MS_DLNG_TAG;
2440 break;
2441 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2442 tag = MS_SLNG_TAG;
2443 break;
2444 default:
2445 WARN("Unexpected id %d.\n", id);
2446 return S_OK;
2449 if (FAILED(hr = create_localizedstrings(&strings)))
2450 return hr;
2452 opentype_get_font_table(stream_desc, MS_META_TAG, &meta);
2454 if (meta.data)
2456 version = table_read_be_dword(&meta, 0);
2457 if (version != 1)
2459 WARN("Unexpected meta table version %d.\n", version);
2460 goto end;
2463 count = table_read_be_dword(&meta, FIELD_OFFSET(struct meta_header, data_maps_count));
2464 if (!(maps = table_read_ensure(&meta, FIELD_OFFSET(struct meta_header, maps),
2465 count * sizeof(struct meta_data_map))))
2466 goto end;
2468 for (i = 0; i < count; ++i)
2470 const char *data;
2472 if (maps[i].tag == tag && maps[i].length)
2474 DWORD length = GET_BE_DWORD(maps[i].length), j;
2476 if ((data = table_read_ensure(&meta, GET_BE_DWORD(maps[i].offset), length)))
2478 WCHAR *ptrW = heap_alloc((length + 1) * sizeof(WCHAR)), *ctx, *token;
2480 if (!ptrW)
2482 hr = E_OUTOFMEMORY;
2483 goto end;
2486 /* Data is stored in comma separated list, ASCII range only. */
2487 for (j = 0; j < length; ++j)
2488 ptrW[j] = data[j];
2489 ptrW[length] = 0;
2491 token = meta_get_lng_name(ptrW, &ctx);
2493 while (token)
2495 add_localizedstring(strings, emptyW, token);
2496 token = meta_get_lng_name(NULL, &ctx);
2499 heap_free(ptrW);
2503 end:
2504 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, meta.context);
2507 if (IDWriteLocalizedStrings_GetCount(strings))
2508 *ret = strings;
2509 else
2510 IDWriteLocalizedStrings_Release(strings);
2512 return hr;
2515 HRESULT opentype_get_font_info_strings(const struct file_stream_desc *stream_desc, DWRITE_INFORMATIONAL_STRING_ID id,
2516 IDWriteLocalizedStrings **strings)
2518 struct dwrite_fonttable name;
2520 switch (id)
2522 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2523 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2524 opentype_get_font_strings_from_meta(stream_desc, id, strings);
2525 break;
2526 default:
2527 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2528 opentype_get_font_strings_from_id(name.data, dwriteid_to_opentypeid[id], strings);
2529 if (name.context)
2530 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2533 return S_OK;
2536 /* FamilyName locating order is WWS Family Name -> Preferred Family Name -> Family Name. If font claims to
2537 have 'Preferred Family Name' in WWS format, then WWS name is not used. */
2538 HRESULT opentype_get_font_familyname(struct file_stream_desc *stream_desc, IDWriteLocalizedStrings **names)
2540 struct dwrite_fonttable os2, name;
2541 const void *name_table;
2542 UINT16 fsselection;
2543 HRESULT hr;
2545 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2546 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2548 name_table = (const void *)name.data;
2550 *names = NULL;
2552 /* If Preferred Family doesn't conform to WWS model try WWS name. */
2553 fsselection = os2.data ? table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) : 0;
2554 if (os2.data && !(fsselection & OS2_FSSELECTION_WWS))
2555 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_WWS_FAMILY_NAME, names);
2556 else
2557 hr = E_FAIL;
2559 if (FAILED(hr))
2560 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME, names);
2561 if (FAILED(hr))
2562 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_FAMILY_NAME, names);
2564 if (os2.context)
2565 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2566 if (name.context)
2567 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2569 return hr;
2572 /* FaceName locating order is WWS Face Name -> Preferred Face Name -> Face Name. If font claims to
2573 have 'Preferred Face Name' in WWS format, then WWS name is not used. */
2574 HRESULT opentype_get_font_facename(struct file_stream_desc *stream_desc, WCHAR *lfname, IDWriteLocalizedStrings **names)
2576 struct dwrite_fonttable os2, name;
2577 IDWriteLocalizedStrings *lfnames;
2578 const void *name_table;
2579 UINT16 fsselection;
2580 HRESULT hr;
2582 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2583 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2585 name_table = name.data;
2587 *names = NULL;
2589 /* if Preferred Family doesn't conform to WWS model try WWS name */
2590 fsselection = os2.data ? table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) : 0;
2591 if (os2.data && !(fsselection & OS2_FSSELECTION_WWS))
2592 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_WWS_SUBFAMILY_NAME, names);
2593 else
2594 hr = E_FAIL;
2596 if (FAILED(hr))
2597 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME, names);
2598 if (FAILED(hr))
2599 hr = opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_SUBFAMILY_NAME, names);
2601 /* User locale is preferred, with fallback to en-us. */
2602 *lfname = 0;
2603 if (SUCCEEDED(opentype_get_font_strings_from_id(name_table, OPENTYPE_STRING_FAMILY_NAME, &lfnames))) {
2604 static const WCHAR enusW[] = {'e','n','-','u','s',0};
2605 WCHAR localeW[LOCALE_NAME_MAX_LENGTH];
2606 UINT32 index;
2607 BOOL exists;
2609 exists = FALSE;
2610 if (GetSystemDefaultLocaleName(localeW, ARRAY_SIZE(localeW)))
2611 IDWriteLocalizedStrings_FindLocaleName(lfnames, localeW, &index, &exists);
2613 if (!exists)
2614 IDWriteLocalizedStrings_FindLocaleName(lfnames, enusW, &index, &exists);
2616 if (exists) {
2617 UINT32 length = 0;
2618 WCHAR *nameW;
2620 IDWriteLocalizedStrings_GetStringLength(lfnames, index, &length);
2621 nameW = heap_alloc((length + 1) * sizeof(WCHAR));
2622 if (nameW) {
2623 *nameW = 0;
2624 IDWriteLocalizedStrings_GetString(lfnames, index, nameW, length + 1);
2625 lstrcpynW(lfname, nameW, LF_FACESIZE);
2626 heap_free(nameW);
2630 IDWriteLocalizedStrings_Release(lfnames);
2633 if (os2.context)
2634 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2635 if (name.context)
2636 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2638 return hr;
2641 static const struct ot_langsys *opentype_get_langsys(const struct ot_gsubgpos_table *table, unsigned int script_index,
2642 unsigned int language_index, unsigned int *feature_count)
2644 unsigned int table_offset, langsys_offset;
2645 const struct ot_langsys *langsys = NULL;
2647 *feature_count = 0;
2649 if (!table->table.data || script_index == ~0u)
2650 return NULL;
2652 /* ScriptTable offset. */
2653 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
2654 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
2655 if (!table_offset)
2656 return NULL;
2658 if (language_index == ~0u)
2659 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
2660 else
2661 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
2662 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
2663 FIELD_OFFSET(struct ot_langsys_record, langsys));
2664 langsys_offset += table->script_list + table_offset;
2666 *feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
2667 if (*feature_count)
2668 langsys = table_read_ensure(&table->table, langsys_offset, FIELD_OFFSET(struct ot_langsys, feature_index[*feature_count]));
2669 if (!langsys)
2670 *feature_count = 0;
2672 return langsys;
2675 void opentype_get_typographic_features(struct ot_gsubgpos_table *table, unsigned int script_index,
2676 unsigned int language_index, struct tag_array *t)
2678 unsigned int i, total_feature_count, script_feature_count;
2679 const struct ot_feature_list *feature_list;
2680 const struct ot_langsys *langsys = NULL;
2682 langsys = opentype_get_langsys(table, script_index, language_index, &script_feature_count);
2684 total_feature_count = table_read_be_word(&table->table, table->feature_list);
2685 if (!total_feature_count)
2686 return;
2688 feature_list = table_read_ensure(&table->table, table->feature_list,
2689 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
2690 if (!feature_list)
2691 return;
2693 for (i = 0; i < script_feature_count; ++i)
2695 unsigned int feature_index = GET_BE_WORD(langsys->feature_index[i]);
2696 if (feature_index >= total_feature_count)
2697 continue;
2699 if (!dwrite_array_reserve((void **)&t->tags, &t->capacity, t->count + 1, sizeof(*t->tags)))
2700 return;
2702 t->tags[t->count++] = feature_list->features[feature_index].tag;
2706 static unsigned int find_vdmx_group(const struct vdmx_header *hdr)
2708 WORD num_ratios, i;
2709 const struct vdmx_ratio *ratios = (struct vdmx_ratio *)(hdr + 1);
2710 BYTE dev_x_ratio = 1, dev_y_ratio = 1;
2711 unsigned int group_offset = 0;
2713 num_ratios = GET_BE_WORD(hdr->num_ratios);
2715 for (i = 0; i < num_ratios; i++) {
2717 if (!ratios[i].bCharSet) continue;
2719 if ((ratios[i].xRatio == 0 && ratios[i].yStartRatio == 0 &&
2720 ratios[i].yEndRatio == 0) ||
2721 (ratios[i].xRatio == dev_x_ratio && ratios[i].yStartRatio <= dev_y_ratio &&
2722 ratios[i].yEndRatio >= dev_y_ratio))
2724 group_offset = GET_BE_WORD(*((WORD *)(ratios + num_ratios) + i));
2725 break;
2729 return group_offset;
2732 BOOL opentype_get_vdmx_size(const struct dwrite_fonttable *vdmx, INT emsize, UINT16 *ascent, UINT16 *descent)
2734 unsigned int num_ratios, num_recs, group_offset, i;
2735 const struct vdmx_header *header;
2736 const struct vdmx_group *group;
2738 if (!vdmx->exists)
2739 return FALSE;
2741 num_ratios = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_ratios));
2742 num_recs = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_recs));
2744 header = table_read_ensure(vdmx, 0, sizeof(*header) + num_ratios * sizeof(struct vdmx_ratio) +
2745 num_recs * sizeof(*group));
2747 if (!header)
2748 return FALSE;
2750 group_offset = find_vdmx_group(header);
2751 if (!group_offset)
2752 return FALSE;
2754 num_recs = table_read_be_word(vdmx, group_offset);
2755 group = table_read_ensure(vdmx, group_offset, FIELD_OFFSET(struct vdmx_group, entries[num_recs]));
2757 if (!group)
2758 return FALSE;
2760 if (emsize < group->startsz || emsize >= group->endsz)
2761 return FALSE;
2763 for (i = 0; i < num_recs; ++i)
2765 WORD ppem = GET_BE_WORD(group->entries[i].yPelHeight);
2766 if (ppem > emsize) {
2767 FIXME("interpolate %d\n", emsize);
2768 return FALSE;
2771 if (ppem == emsize) {
2772 *ascent = (SHORT)GET_BE_WORD(group->entries[i].yMax);
2773 *descent = -(SHORT)GET_BE_WORD(group->entries[i].yMin);
2774 return TRUE;
2778 return FALSE;
2781 unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable *gasp, float emsize)
2783 unsigned int version, num_ranges, i;
2784 const struct gasp_header *table;
2785 WORD flags = 0;
2787 if (!gasp->exists)
2788 return 0;
2790 num_ranges = table_read_be_word(gasp, FIELD_OFFSET(struct gasp_header, num_ranges));
2792 table = table_read_ensure(gasp, 0, FIELD_OFFSET(struct gasp_header, ranges[num_ranges]));
2793 if (!table)
2794 return 0;
2796 version = GET_BE_WORD(table->version);
2797 if (version > 1)
2799 ERR("Unsupported gasp table format version %u.\n", version);
2800 goto done;
2803 for (i = 0; i < num_ranges; ++i)
2805 flags = GET_BE_WORD(table->ranges[i].flags);
2806 if (emsize <= GET_BE_WORD(table->ranges[i].max_ppem)) break;
2809 done:
2810 return flags;
2813 unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable *cpal)
2815 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palettes));
2818 unsigned int opentype_get_cpal_paletteentrycount(const struct dwrite_fonttable *cpal)
2820 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palette_entries));
2823 HRESULT opentype_get_cpal_entries(const struct dwrite_fonttable *cpal, unsigned int palette,
2824 unsigned int first_entry_index, unsigned int entry_count, DWRITE_COLOR_F *entries)
2826 unsigned int num_palettes, num_palette_entries, i;
2827 const struct cpal_color_record *records;
2828 const struct cpal_header_0 *header;
2830 header = table_read_ensure(cpal, 0, sizeof(*header));
2832 if (!cpal->exists || !header)
2833 return DWRITE_E_NOCOLOR;
2835 num_palettes = GET_BE_WORD(header->num_palettes);
2836 if (palette >= num_palettes)
2837 return DWRITE_E_NOCOLOR;
2839 header = table_read_ensure(cpal, 0, FIELD_OFFSET(struct cpal_header_0, color_record_indices[palette]));
2840 if (!header)
2841 return DWRITE_E_NOCOLOR;
2843 num_palette_entries = GET_BE_WORD(header->num_palette_entries);
2844 if (first_entry_index + entry_count > num_palette_entries)
2845 return E_INVALIDARG;
2847 records = table_read_ensure(cpal, GET_BE_DWORD(header->offset_first_color_record),
2848 sizeof(*records) * GET_BE_WORD(header->num_color_records));
2849 if (!records)
2850 return DWRITE_E_NOCOLOR;
2852 first_entry_index += GET_BE_WORD(header->color_record_indices[palette]);
2854 for (i = 0; i < entry_count; i++) {
2855 entries[i].u1.r = records[first_entry_index + i].red / 255.0f;
2856 entries[i].u2.g = records[first_entry_index + i].green / 255.0f;
2857 entries[i].u3.b = records[first_entry_index + i].blue / 255.0f;
2858 entries[i].u4.a = records[first_entry_index + i].alpha / 255.0f;
2861 return S_OK;
2864 static int colr_compare_gid(const void *g, const void *r)
2866 const struct colr_baseglyph_record *record = r;
2867 UINT16 glyph = *(UINT16*)g, GID = GET_BE_WORD(record->glyph);
2868 int ret = 0;
2870 if (glyph > GID)
2871 ret = 1;
2872 else if (glyph < GID)
2873 ret = -1;
2875 return ret;
2878 HRESULT opentype_get_colr_glyph(const struct dwrite_fonttable *colr, UINT16 glyph, struct dwrite_colorglyph *ret)
2880 unsigned int num_baseglyph_records, offset_baseglyph_records;
2881 const struct colr_baseglyph_record *record;
2882 const struct colr_layer_record *layer;
2883 const struct colr_header *header;
2885 memset(ret, 0, sizeof(*ret));
2886 ret->glyph = glyph;
2887 ret->palette_index = 0xffff;
2889 header = table_read_ensure(colr, 0, sizeof(*header));
2890 if (!header)
2891 return S_FALSE;
2893 num_baseglyph_records = GET_BE_WORD(header->num_baseglyph_records);
2894 offset_baseglyph_records = GET_BE_DWORD(header->offset_baseglyph_records);
2895 if (!table_read_ensure(colr, offset_baseglyph_records, num_baseglyph_records * sizeof(*record)))
2897 return S_FALSE;
2900 record = bsearch(&glyph, colr->data + offset_baseglyph_records, num_baseglyph_records,
2901 sizeof(*record), colr_compare_gid);
2902 if (!record)
2903 return S_FALSE;
2905 ret->first_layer = GET_BE_WORD(record->first_layer_index);
2906 ret->num_layers = GET_BE_WORD(record->num_layers);
2908 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
2909 (ret->first_layer + ret->layer) * sizeof(*layer))))
2911 layer += ret->first_layer + ret->layer;
2912 ret->glyph = GET_BE_WORD(layer->glyph);
2913 ret->palette_index = GET_BE_WORD(layer->palette_index);
2916 return S_OK;
2919 void opentype_colr_next_glyph(const struct dwrite_fonttable *colr, struct dwrite_colorglyph *glyph)
2921 const struct colr_layer_record *layer;
2922 const struct colr_header *header;
2924 /* iterated all the way through */
2925 if (glyph->layer == glyph->num_layers)
2926 return;
2928 if (!(header = table_read_ensure(colr, 0, sizeof(*header))))
2929 return;
2931 glyph->layer++;
2933 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
2934 (glyph->first_layer + glyph->layer) * sizeof(*layer))))
2936 layer += glyph->first_layer + glyph->layer;
2937 glyph->glyph = GET_BE_WORD(layer->glyph);
2938 glyph->palette_index = GET_BE_WORD(layer->palette_index);
2942 static BOOL opentype_has_font_table(IDWriteFontFace5 *fontface, UINT32 tag)
2944 BOOL exists = FALSE;
2945 const void *data;
2946 void *context;
2947 UINT32 size;
2948 HRESULT hr;
2950 hr = IDWriteFontFace5_TryGetFontTable(fontface, tag, &data, &size, &context, &exists);
2951 if (FAILED(hr))
2952 return FALSE;
2954 if (exists)
2955 IDWriteFontFace5_ReleaseFontTable(fontface, context);
2957 return exists;
2960 static unsigned int opentype_get_sbix_formats(IDWriteFontFace5 *fontface)
2962 unsigned int num_strikes, num_glyphs, i, j, ret = 0;
2963 const struct sbix_header *sbix_header;
2964 struct dwrite_fonttable table;
2966 memset(&table, 0, sizeof(table));
2967 table.exists = TRUE;
2969 if (!get_fontface_table(fontface, MS_MAXP_TAG, &table))
2970 return 0;
2972 num_glyphs = table_read_be_word(&table, FIELD_OFFSET(struct maxp, num_glyphs));
2974 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
2976 memset(&table, 0, sizeof(table));
2977 table.exists = TRUE;
2979 if (!get_fontface_table(fontface, MS_SBIX_TAG, &table))
2980 return 0;
2982 num_strikes = table_read_be_dword(&table, FIELD_OFFSET(struct sbix_header, num_strikes));
2983 sbix_header = table_read_ensure(&table, 0, FIELD_OFFSET(struct sbix_header, strike_offset[num_strikes]));
2985 if (sbix_header)
2987 for (i = 0; i < num_strikes; ++i)
2989 unsigned int strike_offset = GET_BE_DWORD(sbix_header->strike_offset[i]);
2990 const struct sbix_strike *strike = table_read_ensure(&table, strike_offset,
2991 FIELD_OFFSET(struct sbix_strike, glyphdata_offsets[num_glyphs + 1]));
2993 if (!strike)
2994 continue;
2996 for (j = 0; j < num_glyphs; j++)
2998 unsigned int offset = GET_BE_DWORD(strike->glyphdata_offsets[j]);
2999 unsigned int next_offset = GET_BE_DWORD(strike->glyphdata_offsets[j + 1]);
3000 const struct sbix_glyph_data *glyph_data;
3002 if (offset == next_offset)
3003 continue;
3005 glyph_data = table_read_ensure(&table, strike_offset + offset, sizeof(*glyph_data));
3006 if (!glyph_data)
3007 continue;
3009 switch (glyph_data->graphic_type)
3011 case MS_PNG__TAG:
3012 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3013 break;
3014 case MS_JPG__TAG:
3015 ret |= DWRITE_GLYPH_IMAGE_FORMATS_JPEG;
3016 break;
3017 case MS_TIFF_TAG:
3018 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TIFF;
3019 break;
3020 default:
3021 FIXME("unexpected bitmap format %s\n", debugstr_tag(GET_BE_DWORD(glyph_data->graphic_type)));
3027 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
3029 return ret;
3032 static unsigned int opentype_get_cblc_formats(IDWriteFontFace5 *fontface)
3034 const unsigned int format_mask = DWRITE_GLYPH_IMAGE_FORMATS_PNG |
3035 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3036 const struct cblc_bitmapsize_table *sizes;
3037 struct dwrite_fonttable cblc = { 0 };
3038 unsigned int num_sizes, i, ret = 0;
3039 const struct cblc_header *header;
3041 cblc.exists = TRUE;
3042 if (!get_fontface_table(fontface, MS_CBLC_TAG, &cblc))
3043 return 0;
3045 num_sizes = table_read_be_dword(&cblc, FIELD_OFFSET(struct cblc_header, num_sizes));
3046 sizes = table_read_ensure(&cblc, sizeof(*header), num_sizes * sizeof(*sizes));
3048 if (sizes)
3050 for (i = 0; i < num_sizes; ++i)
3052 BYTE bpp = sizes[i].bit_depth;
3054 if ((ret & format_mask) == format_mask)
3055 break;
3057 if (bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8)
3058 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3059 else if (bpp == 32)
3060 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3064 IDWriteFontFace5_ReleaseFontTable(fontface, cblc.context);
3066 return ret;
3069 UINT32 opentype_get_glyph_image_formats(IDWriteFontFace5 *fontface)
3071 UINT32 ret = DWRITE_GLYPH_IMAGE_FORMATS_NONE;
3073 if (opentype_has_font_table(fontface, MS_GLYF_TAG))
3074 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
3076 if (opentype_has_font_table(fontface, MS_CFF__TAG) ||
3077 opentype_has_font_table(fontface, MS_CFF2_TAG))
3078 ret |= DWRITE_GLYPH_IMAGE_FORMATS_CFF;
3080 if (opentype_has_font_table(fontface, MS_COLR_TAG))
3081 ret |= DWRITE_GLYPH_IMAGE_FORMATS_COLR;
3083 if (opentype_has_font_table(fontface, MS_SVG__TAG))
3084 ret |= DWRITE_GLYPH_IMAGE_FORMATS_SVG;
3086 if (opentype_has_font_table(fontface, MS_SBIX_TAG))
3087 ret |= opentype_get_sbix_formats(fontface);
3089 if (opentype_has_font_table(fontface, MS_CBLC_TAG))
3090 ret |= opentype_get_cblc_formats(fontface);
3092 return ret;
3095 DWRITE_CONTAINER_TYPE opentype_analyze_container_type(void const *data, UINT32 data_size)
3097 DWORD signature;
3099 if (data_size < sizeof(DWORD))
3100 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3102 /* Both WOFF and WOFF2 start with 4 bytes signature. */
3103 signature = *(DWORD *)data;
3105 switch (signature)
3107 case MS_WOFF_TAG:
3108 return DWRITE_CONTAINER_TYPE_WOFF;
3109 case MS_WOF2_TAG:
3110 return DWRITE_CONTAINER_TYPE_WOFF2;
3111 default:
3112 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3116 void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache)
3118 cache->font->grab_font_table(cache->context, MS_GSUB_TAG, &cache->gsub.table.data, &cache->gsub.table.size,
3119 &cache->gsub.table.context);
3121 if (cache->gsub.table.data)
3123 cache->gsub.script_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, script_list));
3124 cache->gsub.feature_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3125 cache->gsub.lookup_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3128 cache->font->grab_font_table(cache->context, MS_GPOS_TAG, &cache->gpos.table.data, &cache->gpos.table.size,
3129 &cache->gpos.table.context);
3131 if (cache->gpos.table.data)
3133 cache->gpos.script_list = table_read_be_word(&cache->gpos.table,
3134 FIELD_OFFSET(struct gpos_gsub_header, script_list));
3135 cache->gpos.feature_list = table_read_be_word(&cache->gpos.table,
3136 FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3137 cache->gpos.lookup_list = table_read_be_word(&cache->gpos.table,
3138 FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3141 cache->font->grab_font_table(cache->context, MS_GDEF_TAG, &cache->gdef.table.data, &cache->gdef.table.size,
3142 &cache->gdef.table.context);
3144 if (cache->gdef.table.data)
3146 unsigned int version = table_read_be_dword(&cache->gdef.table, 0);
3148 cache->gdef.classdef = table_read_be_word(&cache->gdef.table, FIELD_OFFSET(struct gdef_header, classdef));
3149 cache->gdef.markattachclassdef = table_read_be_word(&cache->gdef.table,
3150 FIELD_OFFSET(struct gdef_header, markattach_classdef));
3151 if (version >= 0x00010002)
3152 cache->gdef.markglyphsetdef = table_read_be_word(&cache->gdef.table,
3153 FIELD_OFFSET(struct gdef_header, markglyphsetdef));
3157 unsigned int opentype_layout_find_script(const struct scriptshaping_cache *cache, unsigned int kind, DWORD script,
3158 unsigned int *script_index)
3160 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3161 UINT16 script_count;
3162 unsigned int i;
3164 *script_index = ~0u;
3166 script_count = table_read_be_word(&table->table, table->script_list);
3167 if (!script_count)
3168 return 0;
3170 for (i = 0; i < script_count; i++)
3172 unsigned int tag = table_read_dword(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3173 i * sizeof(struct ot_script_record));
3174 if (!tag)
3175 continue;
3177 if (tag == script)
3179 *script_index = i;
3180 return script;
3184 return 0;
3187 unsigned int opentype_layout_find_language(const struct scriptshaping_cache *cache, unsigned int kind, DWORD language,
3188 unsigned int script_index, unsigned int *language_index)
3190 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3191 UINT16 table_offset, lang_count;
3192 unsigned int i;
3194 *language_index = ~0u;
3196 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3197 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
3198 if (!table_offset)
3199 return 0;
3201 lang_count = table_read_be_word(&table->table, table->script_list + table_offset +
3202 FIELD_OFFSET(struct ot_script, langsys_count));
3203 for (i = 0; i < lang_count; i++)
3205 unsigned int tag = table_read_dword(&table->table, table->script_list + table_offset +
3206 FIELD_OFFSET(struct ot_script, langsys) + i * sizeof(struct ot_langsys_record));
3208 if (tag == language)
3210 *language_index = i;
3211 return language;
3215 /* Try 'defaultLangSys' if it's set. */
3216 if (table_read_be_word(&table->table, table->script_list + table_offset))
3217 return ~0u;
3219 return 0;
3222 static int gdef_class_compare_format2(const void *g, const void *r)
3224 const struct ot_gdef_class_range *range = r;
3225 UINT16 glyph = *(UINT16 *)g;
3227 if (glyph < GET_BE_WORD(range->start_glyph))
3228 return -1;
3229 else if (glyph > GET_BE_WORD(range->end_glyph))
3230 return 1;
3231 else
3232 return 0;
3235 static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable *table,
3236 unsigned int offset, UINT16 glyph)
3238 WORD format = table_read_be_word(table, offset), count;
3239 unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED;
3241 if (format == 1)
3243 const struct ot_gdef_classdef_format1 *format1;
3245 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
3246 format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
3247 if (format1)
3249 WORD start_glyph = GET_BE_WORD(format1->start_glyph);
3250 if (glyph >= start_glyph && (glyph - start_glyph) < count)
3252 glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
3253 if (glyph_class > GDEF_CLASS_MAX)
3254 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3258 else if (format == 2)
3260 const struct ot_gdef_classdef_format2 *format2;
3262 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
3263 format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
3264 if (format2)
3266 const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
3267 sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
3268 glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
3269 GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
3270 if (glyph_class > GDEF_CLASS_MAX)
3271 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3274 else
3275 WARN("Unknown GDEF format %u.\n", format);
3277 return glyph_class;
3280 static unsigned int opentype_set_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3282 struct scriptshaping_cache *cache = context->cache;
3283 unsigned int glyph_class = 0, props;
3285 if (cache->gdef.classdef)
3287 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.classdef,
3288 context->u.buffer.glyphs[idx]);
3291 switch (glyph_class)
3293 case GDEF_CLASS_BASE:
3294 props = GLYPH_PROP_BASE;
3295 break;
3296 case GDEF_CLASS_LIGATURE:
3297 props = GLYPH_PROP_LIGATURE;
3298 break;
3299 case GDEF_CLASS_MARK:
3300 props = GLYPH_PROP_MARK;
3301 if (cache->gdef.markattachclassdef)
3303 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.markattachclassdef,
3304 context->u.buffer.glyphs[idx]);
3305 props |= glyph_class << 8;
3307 break;
3308 default:
3309 props = 0;
3312 context->glyph_infos[idx].props = props;
3314 return props;
3317 static void opentype_set_subst_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3319 unsigned int glyph_props = opentype_set_glyph_props(context, idx) & LOOKUP_FLAG_IGNORE_MASK;
3320 context->u.subst.glyph_props[idx].isDiacritic = !!(glyph_props == GLYPH_PROP_MARK);
3321 context->u.subst.glyph_props[idx].isZeroWidthSpace = !!(glyph_props == GLYPH_PROP_MARK);
3324 struct coverage_compare_format1_context
3326 UINT16 glyph;
3327 const UINT16 *table_base;
3328 unsigned int *coverage_index;
3331 static int coverage_compare_format1(const void *left, const void *right)
3333 const struct coverage_compare_format1_context *context = left;
3334 UINT16 glyph = GET_BE_WORD(*(UINT16 *)right);
3335 int ret;
3337 ret = context->glyph - glyph;
3338 if (!ret)
3339 *context->coverage_index = (UINT16 *)right - context->table_base;
3341 return ret;
3344 static int coverage_compare_format2(const void *g, const void *r)
3346 const struct ot_coverage_range *range = r;
3347 UINT16 glyph = *(UINT16 *)g;
3349 if (glyph < GET_BE_WORD(range->start_glyph))
3350 return -1;
3351 else if (glyph > GET_BE_WORD(range->end_glyph))
3352 return 1;
3353 else
3354 return 0;
3357 static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable *table, unsigned int coverage,
3358 UINT16 glyph)
3360 WORD format = table_read_be_word(table, coverage), count;
3362 count = table_read_be_word(table, coverage + 2);
3364 if (format == 1)
3366 const struct ot_coverage_format1 *format1 = table_read_ensure(table, coverage,
3367 FIELD_OFFSET(struct ot_coverage_format1, glyphs[count]));
3368 struct coverage_compare_format1_context context;
3369 unsigned int coverage_index = GLYPH_NOT_COVERED;
3371 if (format1)
3373 context.glyph = glyph;
3374 context.table_base = format1->glyphs;
3375 context.coverage_index = &coverage_index;
3377 bsearch(&context, format1->glyphs, count, sizeof(glyph), coverage_compare_format1);
3380 return coverage_index;
3382 else if (format == 2)
3384 const struct ot_coverage_format2 *format2 = table_read_ensure(table, coverage,
3385 FIELD_OFFSET(struct ot_coverage_format2, ranges[count]));
3386 if (format2)
3388 const struct ot_coverage_range *range = bsearch(&glyph, format2->ranges, count,
3389 sizeof(struct ot_coverage_range), coverage_compare_format2);
3390 return range && glyph <= GET_BE_WORD(range->end_glyph) ?
3391 GET_BE_WORD(range->startcoverage_index) + glyph - GET_BE_WORD(range->start_glyph) :
3392 GLYPH_NOT_COVERED;
3395 else
3396 WARN("Unknown coverage format %u.\n", format);
3398 return -1;
3401 static inline unsigned int dwrite_popcount(unsigned int x)
3403 #ifdef HAVE___BUILTIN_POPCOUNT
3404 return __builtin_popcount(x);
3405 #else
3406 x -= x >> 1 & 0x55555555;
3407 x = (x & 0x33333333) + (x >> 2 & 0x33333333);
3408 return ((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
3409 #endif
3412 static float opentype_scale_gpos_be_value(WORD value, float emsize, UINT16 upem)
3414 return (short)GET_BE_WORD(value) * emsize / upem;
3417 static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context *context, unsigned int offset)
3419 const struct dwrite_fonttable *table = &context->table->table;
3420 unsigned int start_size, end_size, format, value_word;
3421 unsigned int index, ppem, mask;
3422 int value;
3424 if (!offset)
3425 return 0;
3427 start_size = table_read_be_word(table, offset);
3428 end_size = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, end_size));
3430 ppem = context->emsize;
3431 if (ppem < start_size || ppem > end_size)
3432 return 0;
3434 format = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, format));
3436 if (format < 1 || format > 3)
3437 return 0;
3439 index = ppem - start_size;
3441 value_word = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, values[index >> (4 - format)]));
3442 mask = 0xffff >> (16 - (1 << format));
3444 value = (value_word >> ((index % (4 - format)) * (1 << format))) & mask;
3446 if ((unsigned int)value >= ((mask + 1) >> 1))
3447 value -= mask + 1;
3449 return value;
3452 static void opentype_layout_apply_gpos_value(struct scriptshaping_context *context, unsigned int table_offset,
3453 WORD value_format, const WORD *values, unsigned int glyph)
3455 const struct scriptshaping_cache *cache = context->cache;
3456 DWRITE_GLYPH_OFFSET *offset = &context->offsets[glyph];
3457 float *advance = &context->advances[glyph];
3459 if (!value_format)
3460 return;
3462 if (value_format & GPOS_VALUE_X_PLACEMENT)
3464 offset->advanceOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3465 values++;
3467 if (value_format & GPOS_VALUE_Y_PLACEMENT)
3469 offset->ascenderOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3470 values++;
3472 if (value_format & GPOS_VALUE_X_ADVANCE)
3474 *advance += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3475 values++;
3477 if (value_format & GPOS_VALUE_Y_ADVANCE)
3479 values++;
3481 if (value_format & GPOS_VALUE_X_PLACEMENT_DEVICE)
3483 offset->advanceOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3484 values++;
3486 if (value_format & GPOS_VALUE_Y_PLACEMENT_DEVICE)
3488 offset->ascenderOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3489 values++;
3491 if (value_format & GPOS_VALUE_X_ADVANCE_DEVICE)
3493 *advance += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3494 values++;
3496 if (value_format & GPOS_VALUE_Y_ADVANCE_DEVICE)
3498 values++;
3502 struct lookup
3504 unsigned short index;
3505 unsigned short type;
3506 unsigned short flags;
3507 unsigned short subtable_count;
3509 unsigned int mask;
3510 unsigned int offset;
3513 static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context *context,
3514 const struct lookup *lookup, unsigned int subtable, unsigned int *lookup_type)
3516 unsigned int subtable_offset = table_read_be_word(&context->table->table, lookup->offset +
3517 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
3518 const struct ot_gsubgpos_extension_format1 *format1;
3520 subtable_offset += lookup->offset;
3522 if ((context->table == &context->cache->gsub && lookup->type != GSUB_LOOKUP_EXTENSION_SUBST) ||
3523 (context->table == &context->cache->gpos && lookup->type != GPOS_LOOKUP_EXTENSION_POSITION))
3525 *lookup_type = lookup->type;
3526 return subtable_offset;
3529 *lookup_type = 0;
3531 if (!(format1 = table_read_ensure(&context->table->table, subtable_offset, sizeof(*format1))))
3532 return 0;
3534 if (GET_BE_WORD(format1->format) != 1)
3536 WARN("Unexpected extension table format %#x.\n", format1->format);
3537 return 0;
3540 *lookup_type = GET_BE_WORD(format1->lookup_type);
3541 return subtable_offset + GET_BE_DWORD(format1->extension_offset);
3544 struct ot_lookup
3546 unsigned int offset;
3547 unsigned int subtable_count;
3548 unsigned int flags;
3551 enum iterator_match
3553 /* First two to fit matching callback result. */
3554 ITER_NO = 0,
3555 ITER_YES = 1,
3556 ITER_MAYBE,
3559 struct match_context;
3560 struct match_data
3562 const struct match_context *mc;
3563 unsigned int subtable_offset;
3566 typedef BOOL (*p_match_func)(UINT16 glyph, UINT16 glyph_data, const struct match_data *match_data);
3568 struct match_context
3570 struct scriptshaping_context *context;
3571 unsigned int backtrack_offset;
3572 unsigned int input_offset;
3573 unsigned int lookahead_offset;
3574 p_match_func match_func;
3575 const struct lookup *lookup;
3578 struct glyph_iterator
3580 struct scriptshaping_context *context;
3581 unsigned int flags;
3582 unsigned int pos;
3583 unsigned int len;
3584 unsigned int mask;
3585 p_match_func match_func;
3586 const UINT16 *glyph_data;
3587 const struct match_data *match_data;
3590 static void glyph_iterator_init(struct scriptshaping_context *context, unsigned int flags, unsigned int pos,
3591 unsigned int len, struct glyph_iterator *iter)
3593 iter->context = context;
3594 iter->flags = flags;
3595 iter->pos = pos;
3596 iter->len = len;
3597 iter->mask = ~0u;
3598 iter->match_func = NULL;
3599 iter->match_data = NULL;
3600 iter->glyph_data = NULL;
3603 struct ot_gdef_mark_glyph_sets
3605 UINT16 format;
3606 UINT16 count;
3607 DWORD offsets[1];
3610 static BOOL opentype_match_glyph_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3612 return glyph == glyph_data;
3615 static BOOL opentype_match_class_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3617 const struct match_context *mc = data->mc;
3618 UINT16 glyph_class = opentype_layout_get_glyph_class(&mc->context->table->table, data->subtable_offset, glyph);
3619 return glyph_class == glyph_data;
3622 static BOOL opentype_match_coverage_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3624 const struct match_context *mc = data->mc;
3625 return opentype_layout_is_glyph_covered(&mc->context->table->table, data->subtable_offset + glyph_data, glyph)
3626 != GLYPH_NOT_COVERED;
3629 static BOOL opentype_layout_mark_set_covers(const struct scriptshaping_cache *cache, unsigned int set_index,
3630 UINT16 glyph)
3632 unsigned int format, offset = cache->gdef.markglyphsetdef, coverage_offset, set_count;
3634 if (!offset)
3635 return FALSE;
3637 format = table_read_be_word(&cache->gdef.table, offset);
3639 if (format == 1)
3641 set_count = table_read_be_word(&cache->gdef.table, offset + 2);
3642 if (!set_count || set_index >= set_count)
3643 return FALSE;
3645 coverage_offset = table_read_be_dword(&cache->gdef.table, offset + 2 + set_index * sizeof(coverage_offset));
3646 return opentype_layout_is_glyph_covered(&cache->gdef.table, offset + coverage_offset, glyph) != GLYPH_NOT_COVERED;
3648 else
3649 WARN("Unexpected MarkGlyphSets format %#x.\n", format);
3651 return FALSE;
3654 static BOOL lookup_is_glyph_match(const struct scriptshaping_context *context, unsigned int idx, unsigned int match_props)
3656 unsigned int glyph_props = context->glyph_infos[idx].props;
3657 UINT16 glyph = context->u.buffer.glyphs[idx];
3659 if (glyph_props & match_props & LOOKUP_FLAG_IGNORE_MASK)
3660 return FALSE;
3662 if (!(glyph_props & GLYPH_PROP_MARK))
3663 return TRUE;
3665 if (match_props & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
3666 return opentype_layout_mark_set_covers(context->cache, match_props >> 16, glyph);
3668 if (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE)
3669 return (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE) == (glyph_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE);
3671 return TRUE;
3674 static enum iterator_match glyph_iterator_may_skip(const struct glyph_iterator *iter)
3676 if (!lookup_is_glyph_match(iter->context, iter->pos, iter->flags))
3677 return ITER_YES;
3679 return ITER_NO;
3682 static enum iterator_match glyph_iterator_may_match(const struct glyph_iterator *iter)
3684 if (!(iter->mask & iter->context->glyph_infos[iter->pos].mask))
3685 return ITER_NO;
3687 /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that
3688 in all matching functions. */
3689 if (iter->match_func)
3690 return !!iter->match_func(iter->context->u.buffer.glyphs[iter->pos], GET_BE_WORD(*iter->glyph_data), iter->match_data);
3692 return ITER_MAYBE;
3695 static BOOL glyph_iterator_next(struct glyph_iterator *iter)
3697 enum iterator_match skip, match;
3699 while (iter->pos + iter->len < iter->context->glyph_count)
3701 ++iter->pos;
3703 skip = glyph_iterator_may_skip(iter);
3704 if (skip == ITER_YES)
3705 continue;
3707 match = glyph_iterator_may_match(iter);
3708 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3710 --iter->len;
3711 if (iter->glyph_data)
3712 ++iter->glyph_data;
3713 return TRUE;
3716 if (skip == ITER_NO)
3717 return FALSE;
3720 return FALSE;
3723 static BOOL glyph_iterator_prev(struct glyph_iterator *iter)
3725 enum iterator_match skip, match;
3727 while (iter->pos > iter->len - 1)
3729 --iter->pos;
3731 skip = glyph_iterator_may_skip(iter);
3732 if (skip == ITER_YES)
3733 continue;
3735 match = glyph_iterator_may_match(iter);
3736 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3738 --iter->len;
3739 if (iter->glyph_data)
3740 ++iter->glyph_data;
3741 return TRUE;
3744 if (skip == ITER_NO)
3745 return FALSE;
3748 return FALSE;
3751 static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context,
3752 const struct lookup *lookup, unsigned int subtable_offset)
3754 const struct dwrite_fonttable *table = &context->table->table;
3755 UINT16 format, value_format, value_len, coverage, glyph;
3757 unsigned int coverage_index;
3759 format = table_read_be_word(table, subtable_offset);
3761 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage));
3762 value_format = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format));
3763 value_len = dwrite_popcount(value_format);
3765 glyph = context->u.pos.glyphs[context->cur];
3767 if (format == 1)
3769 const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(table, subtable_offset,
3770 FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len]));
3772 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3773 if (coverage_index == GLYPH_NOT_COVERED)
3774 return FALSE;
3776 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, context->cur);
3778 else if (format == 2)
3780 WORD value_count = table_read_be_word(table, subtable_offset +
3781 FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count));
3782 const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(table, subtable_offset,
3783 FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD));
3785 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3786 if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count)
3787 return FALSE;
3789 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, &format2->values[coverage_index * value_len],
3790 context->cur);
3792 else
3794 WARN("Unknown single adjustment format %u.\n", format);
3795 return FALSE;
3798 context->cur++;
3800 return TRUE;
3803 static int gpos_pair_adjustment_compare_format1(const void *g, const void *r)
3805 const struct ot_gpos_pairvalue *pairvalue = r;
3806 UINT16 second_glyph = GET_BE_WORD(pairvalue->second_glyph);
3807 return *(UINT16 *)g - second_glyph;
3810 static BOOL opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context *context,
3811 const struct lookup *lookup, unsigned int subtable_offset)
3813 const struct dwrite_fonttable *table = &context->table->table;
3814 unsigned int first_glyph, second_glyph;
3815 struct glyph_iterator iter_pair;
3816 WORD format, coverage;
3818 WORD value_format1, value_format2, value_len1, value_len2;
3819 unsigned int coverage_index;
3821 glyph_iterator_init(context, lookup->flags, context->cur, 1, &iter_pair);
3822 if (!glyph_iterator_next(&iter_pair))
3823 return FALSE;
3825 if (context->is_rtl)
3827 first_glyph = iter_pair.pos;
3828 second_glyph = context->cur;
3830 else
3832 first_glyph = context->cur;
3833 second_glyph = iter_pair.pos;
3836 format = table_read_be_word(table, subtable_offset);
3838 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1, coverage));
3839 if (!coverage)
3840 return FALSE;
3842 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, context->u.pos.glyphs[first_glyph]);
3843 if (coverage_index == GLYPH_NOT_COVERED)
3844 return FALSE;
3846 if (format == 1)
3848 const struct ot_gpos_pairpos_format1 *format1;
3849 WORD pairset_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1,
3850 pairset_count));
3851 unsigned int pairvalue_len, pairset_offset;
3852 const struct ot_gpos_pairset *pairset;
3853 const WORD *pairvalue;
3854 WORD pairvalue_count;
3856 if (!pairset_count || coverage_index >= pairset_count)
3857 return FALSE;
3859 format1 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairsets[pairset_count]));
3860 if (!format1)
3861 return FALSE;
3863 /* Ordered paired values. */
3864 pairvalue_count = table_read_be_word(table, subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]));
3865 if (!pairvalue_count)
3866 return FALSE;
3868 /* Structure length is variable, but does not change across the subtable. */
3869 value_format1 = GET_BE_WORD(format1->value_format1) & 0xff;
3870 value_format2 = GET_BE_WORD(format1->value_format2) & 0xff;
3872 value_len1 = dwrite_popcount(value_format1);
3873 value_len2 = dwrite_popcount(value_format2);
3874 pairvalue_len = FIELD_OFFSET(struct ot_gpos_pairvalue, data) + value_len1 * sizeof(WORD) +
3875 value_len2 * sizeof(WORD);
3877 pairset_offset = subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]);
3878 pairset = table_read_ensure(table, subtable_offset + pairset_offset, pairvalue_len * pairvalue_count);
3879 if (!pairset)
3880 return FALSE;
3882 pairvalue = bsearch(&context->u.pos.glyphs[second_glyph], pairset->pairvalues, pairvalue_count,
3883 pairvalue_len, gpos_pair_adjustment_compare_format1);
3884 if (!pairvalue)
3885 return FALSE;
3887 pairvalue += 1; /* Skip SecondGlyph. */
3888 opentype_layout_apply_gpos_value(context, pairset_offset, value_format1, pairvalue, first_glyph);
3889 opentype_layout_apply_gpos_value(context, pairset_offset, value_format2, pairvalue + value_len1,
3890 second_glyph);
3892 context->cur = iter_pair.pos;
3893 if (value_len2)
3894 context->cur++;
3896 else if (format == 2)
3898 const struct ot_gpos_pairpos_format2 *format2;
3899 WORD class1_count, class2_count;
3900 unsigned int class1, class2;
3901 const WCHAR *values;
3903 value_format1 = table_read_be_word(table, subtable_offset +
3904 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format1)) & 0xff;
3905 value_format2 = table_read_be_word(table, subtable_offset +
3906 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format2)) & 0xff;
3908 class1_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class1_count));
3909 class2_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class2_count));
3911 value_len1 = dwrite_popcount(value_format1);
3912 value_len2 = dwrite_popcount(value_format2);
3914 format2 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format2,
3915 values[class1_count * class2_count * (value_len1 + value_len2)]));
3916 if (!format2)
3917 return FALSE;
3919 class1 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def1),
3920 context->u.pos.glyphs[first_glyph]);
3921 class2 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def2),
3922 context->u.pos.glyphs[second_glyph]);
3924 if (!(class1 < class1_count && class2 < class2_count))
3925 return FALSE;
3927 values = &format2->values[(class1 * class2_count + class2) * (value_len1 + value_len2)];
3928 opentype_layout_apply_gpos_value(context, subtable_offset, value_format1, values, first_glyph);
3929 opentype_layout_apply_gpos_value(context, subtable_offset, value_format2, values + value_len1,
3930 second_glyph);
3932 context->cur = iter_pair.pos;
3933 if (value_len2)
3934 context->cur++;
3936 else
3938 WARN("Unknown pair adjustment format %u.\n", format);
3939 return FALSE;
3942 return TRUE;
3945 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context *context, unsigned int anchor_offset,
3946 unsigned int glyph_index, float *x, float *y)
3948 const struct scriptshaping_cache *cache = context->cache;
3949 const struct dwrite_fonttable *table = &context->table->table;
3951 WORD format = table_read_be_word(table, anchor_offset);
3953 *x = *y = 0.0f;
3955 if (format == 1)
3957 const struct ot_gpos_anchor_format1 *format1 = table_read_ensure(table, anchor_offset, sizeof(*format1));
3959 if (format1)
3961 *x = opentype_scale_gpos_be_value(format1->x_coord, context->emsize, cache->upem);
3962 *y = opentype_scale_gpos_be_value(format1->y_coord, context->emsize, cache->upem);
3965 else if (format == 2)
3967 const struct ot_gpos_anchor_format2 *format2 = table_read_ensure(table, anchor_offset, sizeof(*format2));
3969 if (format2)
3971 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
3972 FIXME("Use outline anchor point for glyph %u.\n", context->u.pos.glyphs[glyph_index]);
3974 *x = opentype_scale_gpos_be_value(format2->x_coord, context->emsize, cache->upem);
3975 *y = opentype_scale_gpos_be_value(format2->y_coord, context->emsize, cache->upem);
3978 else if (format == 3)
3980 const struct ot_gpos_anchor_format3 *format3 = table_read_ensure(table, anchor_offset, sizeof(*format3));
3982 if (format3)
3984 *x = opentype_scale_gpos_be_value(format3->x_coord, context->emsize, cache->upem);
3985 *y = opentype_scale_gpos_be_value(format3->y_coord, context->emsize, cache->upem);
3987 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
3989 if (format3->x_dev_offset)
3990 *x += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->x_dev_offset));
3991 if (format3->y_dev_offset)
3992 *y += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->y_dev_offset));
3996 else
3997 WARN("Unknown anchor format %u.\n", format);
4000 static BOOL opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context *context,
4001 const struct lookup *lookup, unsigned int subtable_offset)
4003 const struct dwrite_fonttable *table = &context->table->table;
4004 UINT16 format, glyph;
4006 format = table_read_be_word(table, subtable_offset);
4007 glyph = context->u.pos.glyphs[context->cur];
4009 if (format == 1)
4011 WORD coverage_offset = table_read_be_word(table, subtable_offset +
4012 FIELD_OFFSET(struct ot_gpos_cursive_format1, coverage));
4013 unsigned int glyph_index, entry_count, entry_anchor, exit_anchor;
4014 float entry_x, entry_y, exit_x, exit_y, delta;
4015 struct glyph_iterator prev_iter;
4017 if (!coverage_offset)
4018 return FALSE;
4020 entry_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_cursive_format1, count));
4022 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset, glyph);
4023 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4024 return FALSE;
4026 entry_anchor = table_read_be_word(table, subtable_offset +
4027 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2]));
4028 if (!entry_anchor)
4029 return FALSE;
4031 glyph_iterator_init(context, lookup->flags, context->cur, 1, &prev_iter);
4032 if (!glyph_iterator_prev(&prev_iter))
4033 return FALSE;
4035 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset,
4036 context->u.pos.glyphs[prev_iter.pos]);
4037 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4038 return FALSE;
4040 exit_anchor = table_read_be_word(table, subtable_offset +
4041 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2 + 1]));
4042 if (!exit_anchor)
4043 return FALSE;
4045 opentype_layout_gpos_get_anchor(context, subtable_offset + exit_anchor, prev_iter.pos, &exit_x, &exit_y);
4046 opentype_layout_gpos_get_anchor(context, subtable_offset + entry_anchor, context->cur, &entry_x, &entry_y);
4048 if (context->is_rtl)
4050 delta = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4051 context->advances[prev_iter.pos] -= delta;
4052 context->advances[context->cur] = entry_x + context->offsets[context->cur].advanceOffset;
4053 context->offsets[prev_iter.pos].advanceOffset -= delta;
4055 else
4057 delta = entry_x + context->offsets[context->cur].advanceOffset;
4058 context->advances[prev_iter.pos] = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4059 context->advances[context->cur] -= delta;
4060 context->offsets[context->cur].advanceOffset -= delta;
4063 if (lookup->flags & LOOKUP_FLAG_RTL)
4064 context->offsets[prev_iter.pos].ascenderOffset = entry_y - exit_y;
4065 else
4066 context->offsets[context->cur].ascenderOffset = exit_y - entry_y;
4068 context->cur++;
4070 else
4072 WARN("Unknown cursive attachment format %u.\n", format);
4073 return FALSE;
4076 return TRUE;
4079 static BOOL opentype_layout_apply_mark_array(struct scriptshaping_context *context, unsigned int subtable_offset,
4080 unsigned int mark_array, unsigned int mark_index, unsigned int glyph_index, unsigned int anchors_matrix,
4081 unsigned int class_count, unsigned int glyph_pos)
4083 const struct dwrite_fonttable *table = &context->table->table;
4084 unsigned int mark_class, mark_count, glyph_count;
4085 const struct ot_gpos_mark_record *record;
4086 float mark_x, mark_y, base_x, base_y;
4087 const UINT16 *anchors;
4089 mark_count = table_read_be_word(table, subtable_offset + mark_array);
4090 if (mark_index >= mark_count) return FALSE;
4092 if (!(record = table_read_ensure(table, subtable_offset + mark_array +
4093 FIELD_OFFSET(struct ot_gpos_mark_array, records[mark_index]), sizeof(*record))))
4095 return FALSE;
4098 mark_class = GET_BE_WORD(record->mark_class);
4099 if (mark_class >= class_count) return FALSE;
4101 glyph_count = table_read_be_word(table, subtable_offset + anchors_matrix);
4102 if (glyph_index >= glyph_count) return FALSE;
4104 /* Anchors data is stored as two dimensional array [glyph_count][class_count], starting with row count field. */
4105 anchors = table_read_ensure(table, subtable_offset + anchors_matrix + 2, glyph_count * class_count * sizeof(*anchors));
4106 if (!anchors) return FALSE;
4108 opentype_layout_gpos_get_anchor(context, subtable_offset + mark_array + GET_BE_WORD(record->mark_anchor),
4109 context->cur, &mark_x, &mark_y);
4110 opentype_layout_gpos_get_anchor(context, subtable_offset + anchors_matrix +
4111 GET_BE_WORD(anchors[glyph_index * class_count + mark_class]), glyph_pos, &base_x, &base_y);
4113 if (context->is_rtl)
4114 context->offsets[context->cur].advanceOffset = mark_x - base_x;
4115 else
4116 context->offsets[context->cur].advanceOffset = -context->advances[glyph_pos] + base_x - mark_x;
4118 context->offsets[context->cur].ascenderOffset = base_y - mark_y;
4119 context->cur++;
4121 return TRUE;
4124 static BOOL opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context *context,
4125 const struct lookup *lookup, unsigned int subtable_offset)
4127 const struct dwrite_fonttable *table = &context->table->table;
4128 WORD format;
4130 format = table_read_be_word(table, subtable_offset);
4132 if (format == 1)
4134 const struct ot_gpos_mark_to_base_format1 *format1;
4135 unsigned int base_index, mark_index;
4136 struct glyph_iterator base_iter;
4138 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4140 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4141 context->u.pos.glyphs[context->cur]);
4142 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4144 /* Look back for first base glyph. */
4145 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &base_iter);
4146 if (!glyph_iterator_prev(&base_iter))
4147 return FALSE;
4149 base_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->base_coverage),
4150 context->u.pos.glyphs[base_iter.pos]);
4151 if (base_index == GLYPH_NOT_COVERED) return FALSE;
4153 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4154 base_index, GET_BE_WORD(format1->base_array), GET_BE_WORD(format1->mark_class_count), base_iter.pos);
4156 else
4158 WARN("Unknown mark-to-base format %u.\n", format);
4159 return FALSE;
4162 return TRUE;
4165 static const UINT16 * table_read_array_be_word(const struct dwrite_fonttable *table, unsigned int offset,
4166 unsigned int index, UINT16 *data)
4168 unsigned int count = table_read_be_word(table, offset);
4169 const UINT16 *array;
4171 if (index != ~0u && index >= count) return NULL;
4172 if (!(array = table_read_ensure(table, offset + 2, count * sizeof(*array)))) return FALSE;
4173 *data = index == ~0u ? count : GET_BE_WORD(array[index]);
4174 return array;
4177 static BOOL opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context *context,
4178 const struct lookup *lookup, unsigned int subtable_offset)
4180 const struct dwrite_fonttable *table = &context->table->table;
4181 WORD format;
4183 format = table_read_be_word(table, subtable_offset);
4185 if (format == 1)
4187 unsigned int mark_index, lig_index, comp_index, class_count, comp_count;
4188 const struct ot_gpos_mark_to_lig_format1 *format1;
4189 struct glyph_iterator lig_iter;
4190 unsigned int lig_array;
4191 UINT16 lig_attach;
4193 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4195 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4196 context->u.pos.glyphs[context->cur]);
4197 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4199 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &lig_iter);
4200 if (!glyph_iterator_prev(&lig_iter))
4201 return FALSE;
4203 lig_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->lig_coverage),
4204 context->u.pos.glyphs[lig_iter.pos]);
4205 if (lig_index == GLYPH_NOT_COVERED) return FALSE;
4207 class_count = GET_BE_WORD(format1->mark_class_count);
4209 lig_array = GET_BE_WORD(format1->lig_array);
4211 if (!table_read_array_be_word(table, subtable_offset + lig_array, lig_index, &lig_attach)) return FALSE;
4213 comp_count = table_read_be_word(table, subtable_offset + lig_array + lig_attach);
4214 if (!comp_count) return FALSE;
4216 comp_index = context->u.buffer.glyph_props[lig_iter.pos].components -
4217 context->u.buffer.glyph_props[context->cur].lig_component - 1;
4218 if (comp_index >= comp_count) return FALSE;
4220 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4221 comp_index, lig_array + lig_attach, class_count, lig_iter.pos);
4223 else
4224 WARN("Unknown mark-to-ligature format %u.\n", format);
4226 return FALSE;
4229 static BOOL opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context *context,
4230 const struct lookup *lookup, unsigned int subtable_offset)
4232 const struct dwrite_fonttable *table = &context->table->table;
4233 WORD format;
4235 format = table_read_be_word(table, subtable_offset);
4237 if (format == 1)
4239 const struct ot_gpos_mark_to_mark_format1 *format1;
4240 unsigned int mark1_index, mark2_index;
4241 struct glyph_iterator mark_iter;
4243 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4245 mark1_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark1_coverage),
4246 context->u.pos.glyphs[context->cur]);
4247 if (mark1_index == GLYPH_NOT_COVERED) return FALSE;
4249 glyph_iterator_init(context, lookup->flags & ~LOOKUP_FLAG_IGNORE_MASK, context->cur, 1, &mark_iter);
4250 if (!glyph_iterator_prev(&mark_iter))
4251 return FALSE;
4253 if (!context->u.pos.glyph_props[mark_iter.pos].isDiacritic)
4254 return FALSE;
4256 mark2_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark2_coverage),
4257 context->u.pos.glyphs[mark_iter.pos]);
4258 if (mark2_index == GLYPH_NOT_COVERED) return FALSE;
4260 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark1_array), mark1_index,
4261 mark2_index, GET_BE_WORD(format1->mark2_array), GET_BE_WORD(format1->mark_class_count), mark_iter.pos);
4263 else
4265 WARN("Unknown mark-to-mark format %u.\n", format);
4266 return FALSE;
4269 return TRUE;
4272 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
4273 unsigned int subtable_offset);
4274 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
4275 unsigned int subtable_offset);
4277 static BOOL opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
4279 unsigned int i, lookup_type;
4280 BOOL ret = FALSE;
4282 for (i = 0; i < lookup->subtable_count; ++i)
4284 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
4286 switch (lookup_type)
4288 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
4289 ret = opentype_layout_apply_gpos_single_adjustment(context, lookup, subtable_offset);
4290 break;
4291 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
4292 ret = opentype_layout_apply_gpos_pair_adjustment(context, lookup, subtable_offset);
4293 break;
4294 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
4295 ret = opentype_layout_apply_gpos_cursive_attachment(context, lookup, subtable_offset);
4296 break;
4297 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
4298 ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, lookup, subtable_offset);
4299 break;
4300 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
4301 ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, lookup, subtable_offset);
4302 break;
4303 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
4304 ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, lookup, subtable_offset);
4305 break;
4306 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
4307 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
4308 break;
4309 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
4310 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
4311 break;
4312 case GPOS_LOOKUP_EXTENSION_POSITION:
4313 WARN("Recursive extension lookup.\n");
4314 break;
4315 default:
4316 WARN("Unknown lookup type %u.\n", lookup_type);
4319 if (ret)
4320 break;
4323 return ret;
4326 struct lookups
4328 struct lookup *lookups;
4329 size_t capacity;
4330 size_t count;
4333 static int lookups_sorting_compare(const void *a, const void *b)
4335 const struct lookup *left = (const struct lookup *)a;
4336 const struct lookup *right = (const struct lookup *)b;
4337 return left->index < right->index ? -1 : left->index > right->index ? 1 : 0;
4340 static BOOL opentype_layout_init_lookup(const struct ot_gsubgpos_table *table, unsigned short lookup_index, unsigned int mask,
4341 struct lookup *lookup)
4343 unsigned short subtable_count, lookup_type, flags, mark_filtering_set;
4344 const struct ot_lookup_table *lookup_table;
4345 unsigned int offset;
4347 if (!(offset = table_read_be_word(&table->table, table->lookup_list +
4348 FIELD_OFFSET(struct ot_lookup_list, lookup[lookup_index]))))
4350 return FALSE;
4353 offset += table->lookup_list;
4355 if (!(lookup_table = table_read_ensure(&table->table, offset, sizeof(*lookup_table))))
4356 return FALSE;
4358 if (!(subtable_count = GET_BE_WORD(lookup_table->subtable_count)))
4359 return FALSE;
4361 lookup_type = GET_BE_WORD(lookup_table->lookup_type);
4362 flags = GET_BE_WORD(lookup_table->flags);
4364 if (flags & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
4366 mark_filtering_set = table_read_be_word(&table->table, offset +
4367 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable_count]));
4368 flags |= mark_filtering_set << 16;
4371 lookup->index = lookup_index;
4372 lookup->type = lookup_type;
4373 lookup->flags = flags;
4374 lookup->subtable_count = subtable_count;
4375 lookup->mask = mask;
4376 lookup->offset = offset;
4378 return TRUE;
4381 static void opentype_layout_add_lookups(const struct ot_feature_list *feature_list, UINT16 total_lookup_count,
4382 const struct ot_gsubgpos_table *table, struct shaping_feature *feature, struct lookups *lookups)
4384 UINT16 feature_offset, lookup_count;
4385 unsigned int i;
4387 /* Feature wasn't found */
4388 if (feature->index == 0xffff)
4389 return;
4391 feature_offset = GET_BE_WORD(feature_list->features[feature->index].offset);
4393 lookup_count = table_read_be_word(&table->table, table->feature_list + feature_offset +
4394 FIELD_OFFSET(struct ot_feature, lookup_count));
4395 if (!lookup_count)
4396 return;
4398 if (!dwrite_array_reserve((void **)&lookups->lookups, &lookups->capacity, lookups->count + lookup_count,
4399 sizeof(*lookups->lookups)))
4401 return;
4404 for (i = 0; i < lookup_count; ++i)
4406 UINT16 lookup_index = table_read_be_word(&table->table, table->feature_list + feature_offset +
4407 FIELD_OFFSET(struct ot_feature, lookuplist_index[i]));
4409 if (lookup_index >= total_lookup_count)
4410 continue;
4412 if (opentype_layout_init_lookup(table, lookup_index, feature->mask, &lookups->lookups[lookups->count]))
4413 lookups->count++;
4417 static void opentype_layout_collect_lookups(struct scriptshaping_context *context, unsigned int script_index,
4418 unsigned int language_index, const struct shaping_features *features, const struct ot_gsubgpos_table *table,
4419 struct lookups *lookups)
4421 unsigned int last_num_lookups = 0, stage, script_feature_count = 0;
4422 UINT16 total_feature_count, total_lookup_count;
4423 struct shaping_feature required_feature = { 0 };
4424 const struct ot_feature_list *feature_list;
4425 const struct ot_langsys *langsys = NULL;
4426 struct shaping_feature *feature;
4427 unsigned int i, j, next_bit;
4428 unsigned int global_bit_shift = 1;
4429 unsigned int global_bit_mask = 2;
4430 UINT16 feature_index;
4432 if (!table->table.data)
4433 return;
4435 if (script_index != ~0u)
4437 unsigned int table_offset, langsys_offset;
4439 /* ScriptTable offset. */
4440 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
4441 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
4442 if (!table_offset)
4443 return;
4445 if (language_index == ~0u)
4446 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
4447 else
4448 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
4449 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
4450 FIELD_OFFSET(struct ot_langsys_record, langsys));
4451 langsys_offset += table->script_list + table_offset;
4453 script_feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
4454 if (script_feature_count)
4455 langsys = table_read_ensure(&table->table, langsys_offset,
4456 FIELD_OFFSET(struct ot_langsys, feature_index[script_feature_count]));
4457 if (!langsys)
4458 script_feature_count = 0;
4461 total_feature_count = table_read_be_word(&table->table, table->feature_list);
4462 if (!total_feature_count)
4463 return;
4465 total_lookup_count = table_read_be_word(&table->table, table->lookup_list);
4466 if (!total_lookup_count)
4467 return;
4469 feature_list = table_read_ensure(&table->table, table->feature_list,
4470 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
4471 if (!feature_list)
4472 return;
4474 /* Required feature. */
4475 required_feature.index = langsys ? GET_BE_WORD(langsys->required_feature_index) : 0xffff;
4476 if (required_feature.index < total_feature_count)
4477 required_feature.tag = feature_list->features[required_feature.index].tag;
4478 required_feature.mask = global_bit_mask;
4480 context->global_mask = global_bit_mask;
4481 next_bit = global_bit_shift + 1;
4482 for (i = 0; i < features->count; ++i)
4484 unsigned int bits_needed;
4485 BOOL found = FALSE;
4487 feature = &features->features[i];
4489 feature->index = 0xffff;
4491 if ((feature->flags & FEATURE_GLOBAL) && feature->max_value == 1)
4492 bits_needed = 0;
4493 else
4494 BitScanReverse(&bits_needed, min(feature->max_value, 256));
4496 if (!feature->max_value || next_bit + bits_needed > 8 * sizeof (feature->mask))
4497 continue;
4499 if (required_feature.tag == feature->tag)
4500 required_feature.stage = feature->stage;
4502 for (j = 0; j < script_feature_count; ++j)
4504 feature_index = GET_BE_WORD(langsys->feature_index[j]);
4505 if (feature_index >= total_feature_count)
4506 continue;
4507 if ((found = feature_list->features[feature_index].tag == feature->tag))
4509 feature->index = feature_index;
4510 break;
4514 if (!found && (features->features[i].flags & FEATURE_GLOBAL_SEARCH))
4516 for (j = 0; j < total_feature_count; ++j)
4518 if ((found = (feature_list->features[j].tag == feature->tag)))
4520 feature->index = j;
4521 break;
4526 if (!found)
4527 continue;
4529 if (feature->flags & FEATURE_GLOBAL && feature->max_value == 1)
4531 feature->shift = global_bit_shift;
4532 feature->mask = global_bit_mask;
4534 else
4536 feature->shift = next_bit;
4537 feature->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
4538 next_bit += bits_needed;
4539 context->global_mask |= (feature->default_value << feature->shift) & feature->mask;
4543 for (stage = 0; stage <= features->stage; ++stage)
4545 if (required_feature.index != 0xffff && required_feature.stage == stage)
4546 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &required_feature, lookups);
4548 for (i = 0; i < features->count; ++i)
4550 if (features->features[i].stage == stage)
4551 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &features->features[i], lookups);
4554 /* Sort and merge lookups for current stage. */
4555 if (last_num_lookups < lookups->count)
4557 qsort(lookups->lookups + last_num_lookups, lookups->count - last_num_lookups, sizeof(*lookups->lookups),
4558 lookups_sorting_compare);
4560 j = last_num_lookups;
4561 for (i = j + 1; i < lookups->count; ++i)
4563 if (lookups->lookups[i].index != lookups->lookups[j].index)
4565 lookups->lookups[++j] = lookups->lookups[i];
4567 else
4569 lookups->lookups[j].mask |= lookups->lookups[i].mask;
4572 lookups->count = j + 1;
4575 last_num_lookups = lookups->count;
4579 static int feature_search_compare(const void *a, const void* b)
4581 unsigned int tag = *(unsigned int *)a;
4582 const struct shaping_feature *feature = b;
4584 return tag < feature->tag ? -1 : tag > feature->tag ? 1 : 0;
4587 static unsigned int shaping_features_get_mask(const struct shaping_features *features, unsigned int tag, unsigned int *shift)
4589 struct shaping_feature *feature;
4591 feature = bsearch(&tag, features->features, features->count, sizeof(*features->features), feature_search_compare);
4593 if (!feature || feature->index == 0xffff)
4594 return 0;
4596 if (shift) *shift = feature->shift;
4597 return feature->mask;
4600 static void opentype_layout_get_glyph_range_for_text(struct scriptshaping_context *context, unsigned int start_char,
4601 unsigned int end_char, unsigned int *start_glyph, unsigned int *end_glyph)
4603 *start_glyph = context->u.buffer.clustermap[start_char];
4604 if (end_char >= context->length - 1)
4605 *end_glyph = context->glyph_count - 1;
4606 else
4607 *end_glyph = context->u.buffer.clustermap[end_char + 1] - 1;
4610 static void opentype_layout_set_glyph_masks(struct scriptshaping_context *context, const struct shaping_features *features)
4612 const DWRITE_TYPOGRAPHIC_FEATURES **user_features = context->user_features.features;
4613 unsigned int f, r, g, start_char, mask, shift, value;
4615 for (g = 0; g < context->glyph_count; ++g)
4616 context->glyph_infos[g].mask = context->global_mask;
4618 /* FIXME: set shaper masks */
4620 for (r = 0, start_char = 0; r < context->user_features.range_count; ++r)
4622 unsigned int start_glyph, end_glyph;
4624 if (start_char >= context->length)
4625 break;
4627 opentype_layout_get_glyph_range_for_text(context, start_char, start_char + context->user_features.range_lengths[r],
4628 &start_glyph, &end_glyph);
4629 start_char += context->user_features.range_lengths[r];
4631 if (start_glyph > end_glyph || end_glyph >= context->glyph_count)
4632 continue;
4634 for (f = 0; f < user_features[r]->featureCount; ++f)
4636 mask = shaping_features_get_mask(features, user_features[r]->features[f].nameTag, &shift);
4637 if (!mask)
4638 continue;
4640 value = (user_features[r]->features[f].parameter << shift) & mask;
4642 for (g = start_glyph; g <= end_glyph; ++g)
4643 context->glyph_infos[g].mask = (context->glyph_infos[g].mask & ~mask) | value;
4648 static void opentype_layout_apply_gpos_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
4650 struct lookup lookup = { 0 };
4651 /* Feature mask is intentionally zero, it's not used outside of main loop. */
4652 if (opentype_layout_init_lookup(context->table, lookup_index, 0, &lookup))
4653 opentype_layout_apply_gpos_lookup(context, &lookup);
4656 void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index,
4657 unsigned int language_index, const struct shaping_features *features)
4659 struct lookups lookups = { 0 };
4660 unsigned int i;
4661 BOOL ret;
4663 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
4664 context->u.buffer.apply_context_lookup = opentype_layout_apply_gpos_context_lookup;
4665 opentype_layout_collect_lookups(context, script_index, language_index, features, &context->cache->gpos, &lookups);
4667 for (i = 0; i < context->glyph_count; ++i)
4668 opentype_set_glyph_props(context, i);
4669 opentype_layout_set_glyph_masks(context, features);
4671 for (i = 0; i < lookups.count; ++i)
4673 const struct lookup *lookup = &lookups.lookups[i];
4675 context->cur = 0;
4676 context->lookup_mask = lookup->mask;
4678 while (context->cur < context->glyph_count)
4680 ret = FALSE;
4682 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
4683 lookup_is_glyph_match(context, context->cur, lookup->flags))
4685 ret = opentype_layout_apply_gpos_lookup(context, lookup);
4688 if (!ret)
4689 context->cur++;
4693 heap_free(lookups.lookups);
4696 static void opentype_layout_replace_glyph(struct scriptshaping_context *context, UINT16 glyph)
4698 UINT16 orig_glyph = context->u.subst.glyphs[context->cur];
4699 if (glyph != orig_glyph)
4701 context->u.subst.glyphs[context->cur] = glyph;
4702 opentype_set_subst_glyph_props(context, context->cur);
4706 static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4707 unsigned int subtable_offset)
4709 const struct dwrite_fonttable *table = &context->table->table;
4710 UINT16 format, coverage, orig_glyph, glyph;
4711 unsigned int coverage_index;
4713 orig_glyph = glyph = context->u.subst.glyphs[context->cur];
4715 format = table_read_be_word(table, subtable_offset);
4717 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, coverage));
4719 if (format == 1)
4721 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4722 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4724 glyph = orig_glyph + table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, delta));
4726 else if (format == 2)
4728 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4729 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4731 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count),
4732 coverage_index, &glyph))
4734 return FALSE;
4737 else
4739 WARN("Unknown single substitution format %u.\n", format);
4740 return FALSE;
4743 opentype_layout_replace_glyph(context, glyph);
4744 context->cur++;
4746 return TRUE;
4749 static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *context, unsigned int count)
4751 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
4752 struct shaping_glyph_info *glyph_infos;
4753 unsigned int new_capacity;
4754 UINT16 *glyphs;
4755 BOOL ret;
4757 if (context->u.subst.capacity >= count)
4758 return TRUE;
4760 new_capacity = context->u.subst.capacity * 2;
4762 if ((glyphs = heap_realloc(context->u.subst.glyphs, new_capacity * sizeof(*glyphs))))
4763 context->u.subst.glyphs = glyphs;
4764 if ((glyph_props = heap_realloc(context->u.subst.glyph_props, new_capacity * sizeof(*glyph_props))))
4765 context->u.subst.glyph_props = glyph_props;
4766 if ((glyph_infos = heap_realloc(context->glyph_infos, new_capacity * sizeof(*glyph_infos))))
4767 context->glyph_infos = glyph_infos;
4769 if ((ret = (glyphs && glyph_props && glyph_infos)))
4770 context->u.subst.capacity = new_capacity;
4772 return ret;
4775 static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4776 unsigned int subtable_offset)
4778 const struct dwrite_fonttable *table = &context->table->table;
4779 UINT16 format, coverage, glyph, glyph_count;
4780 unsigned int i, idx, coverage_index;
4781 const UINT16 *glyphs;
4783 idx = context->cur;
4784 glyph = context->u.subst.glyphs[idx];
4786 format = table_read_be_word(table, subtable_offset);
4788 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, coverage));
4790 if (format == 1)
4792 UINT16 seq_offset;
4794 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4795 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4797 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, seq_count),
4798 coverage_index, &seq_offset))
4800 return FALSE;
4803 if (!(glyphs = table_read_array_be_word(table, subtable_offset + seq_offset, ~0u, &glyph_count))) return FALSE;
4805 if (glyph_count == 1)
4807 /* Equivalent of single substitution. */
4808 opentype_layout_replace_glyph(context, GET_BE_WORD(glyphs[0]));
4809 context->cur++;
4811 else if (glyph_count == 0)
4813 context->cur++;
4815 else
4817 unsigned int shift_len, src_idx, dest_idx, mask;
4819 /* Current glyph is also replaced. */
4820 glyph_count--;
4822 if (!(opentype_layout_gsub_ensure_buffer(context, context->glyph_count + glyph_count)))
4823 return FALSE;
4825 shift_len = context->cur + 1 < context->glyph_count ? context->glyph_count - context->cur - 1 : 0;
4827 if (shift_len)
4829 src_idx = context->cur + 1;
4830 dest_idx = src_idx + glyph_count;
4832 memmove(&context->u.subst.glyphs[dest_idx], &context->u.subst.glyphs[src_idx],
4833 shift_len * sizeof(*context->u.subst.glyphs));
4834 memmove(&context->u.subst.glyph_props[dest_idx], &context->u.subst.glyph_props[src_idx],
4835 shift_len * sizeof(*context->u.subst.glyph_props));
4836 memmove(&context->glyph_infos[dest_idx], &context->glyph_infos[src_idx],
4837 shift_len * sizeof(*context->glyph_infos));
4840 mask = context->glyph_infos[context->cur].mask;
4841 for (i = 0, idx = context->cur; i <= glyph_count; ++i)
4843 glyph = GET_BE_WORD(glyphs[i]);
4844 context->u.subst.glyphs[idx + i] = glyph;
4845 if (i)
4847 context->u.subst.glyph_props[idx + i].isClusterStart = 0;
4848 context->u.buffer.glyph_props[idx + i].components = 0;
4849 context->glyph_infos[idx + i].start_text_idx = 0;
4851 opentype_set_subst_glyph_props(context, idx + i);
4852 /* Inherit feature mask from original matched glyph. */
4853 context->glyph_infos[idx + i].mask = mask;
4856 context->cur += glyph_count + 1;
4857 context->glyph_count += glyph_count;
4860 else
4862 WARN("Unknown multiple substitution format %u.\n", format);
4863 return FALSE;
4866 return TRUE;
4869 static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4870 unsigned int subtable_offset)
4872 const struct dwrite_fonttable *table = &context->table->table;
4873 unsigned int idx, coverage_index;
4874 UINT16 format, coverage, glyph;
4876 idx = context->cur;
4877 glyph = context->u.subst.glyphs[idx];
4879 format = table_read_be_word(table, subtable_offset);
4881 if (format == 1)
4883 const struct ot_gsub_altsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
4884 unsigned int shift, alt_index;
4885 UINT16 set_offset;
4887 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, coverage));
4889 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4890 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4892 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, count),
4893 coverage_index, &set_offset))
4894 return FALSE;
4896 /* Argument is 1-based. */
4897 BitScanForward(&shift, context->lookup_mask);
4898 alt_index = (context->lookup_mask & context->glyph_infos[idx].mask) >> shift;
4899 if (!alt_index) return FALSE;
4901 if (!table_read_array_be_word(table, subtable_offset + set_offset, alt_index - 1, &glyph)) return FALSE;
4903 else
4905 WARN("Unexpected alternate substitution format %d.\n", format);
4906 return FALSE;
4909 opentype_layout_replace_glyph(context, glyph);
4910 context->cur++;
4912 return TRUE;
4915 static BOOL opentype_layout_context_match_input(const struct match_context *mc, unsigned int count, const UINT16 *input,
4916 unsigned int *end_offset, unsigned int *match_positions)
4918 struct match_data match_data = { .mc = mc, .subtable_offset = mc->input_offset };
4919 struct scriptshaping_context *context = mc->context;
4920 struct glyph_iterator iter;
4921 unsigned int i;
4923 if (count > GLYPH_CONTEXT_MAX_LENGTH)
4924 return FALSE;
4926 match_positions[0] = context->cur;
4928 glyph_iterator_init(context, mc->lookup->flags, context->cur, count - 1, &iter);
4929 iter.mask = context->lookup_mask;
4930 iter.match_func = mc->match_func;
4931 iter.match_data = &match_data;
4932 iter.glyph_data = input;
4934 for (i = 1; i < count; ++i)
4936 if (!glyph_iterator_next(&iter))
4937 return FALSE;
4939 match_positions[i] = iter.pos;
4942 *end_offset = iter.pos - context->cur + 1;
4944 return TRUE;
4947 static void opentype_layout_unsafe_to_break(struct scriptshaping_context *context, unsigned int idx)
4949 if (context->u.buffer.glyph_props[idx].isClusterStart)
4950 context->u.buffer.text_props[context->glyph_infos[idx].start_text_idx].canBreakShapingAfter = 0;
4953 static void opentype_layout_delete_glyph(struct scriptshaping_context *context, unsigned int idx)
4955 unsigned int shift_len;
4957 shift_len = context->glyph_count - context->cur - 1;
4959 if (shift_len)
4961 memmove(&context->u.buffer.glyphs[idx], &context->u.buffer.glyphs[idx + 1],
4962 shift_len * sizeof(*context->u.buffer.glyphs));
4963 memmove(&context->u.buffer.glyph_props[idx], &context->u.buffer.glyph_props[idx + 1],
4964 shift_len * sizeof(*context->u.buffer.glyph_props));
4965 memmove(&context->glyph_infos[idx], &context->glyph_infos[idx + 1], shift_len * sizeof(*context->glyph_infos));
4968 context->glyph_count--;
4971 static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset,
4972 const struct lookup *lookup)
4974 struct match_context mc = { .context = context, .lookup = lookup, .match_func = opentype_match_glyph_func };
4975 const struct dwrite_fonttable *gsub = &context->table->table;
4976 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
4977 unsigned int i, j, comp_count, match_length = 0;
4978 const struct ot_gsub_lig *lig;
4979 UINT16 lig_glyph;
4981 comp_count = table_read_be_word(gsub, offset + FIELD_OFFSET(struct ot_gsub_lig, comp_count));
4983 if (!comp_count)
4984 return FALSE;
4986 lig = table_read_ensure(gsub, offset, FIELD_OFFSET(struct ot_gsub_lig, components[comp_count-1]));
4987 if (!lig)
4988 return FALSE;
4990 lig_glyph = GET_BE_WORD(lig->lig_glyph);
4992 if (comp_count == 1)
4994 opentype_layout_replace_glyph(context, lig_glyph);
4995 context->cur++;
4996 return TRUE;
4999 if (!opentype_layout_context_match_input(&mc, comp_count, lig->components, &match_length, match_positions))
5000 return FALSE;
5002 opentype_layout_replace_glyph(context, lig_glyph);
5003 context->u.buffer.glyph_props[context->cur].components = comp_count;
5005 /* Positioning against a ligature implies keeping track of ligature component
5006 glyph should be attached to. Update per-glyph property for interleaving glyphs,
5007 0 means attaching to last component, n - attaching to n-th glyph before last. */
5008 for (i = 1; i < comp_count; ++i)
5010 j = match_positions[i - 1] + 1;
5011 while (j < match_positions[i])
5013 context->u.buffer.glyph_props[j++].lig_component = comp_count - i;
5015 opentype_layout_unsafe_to_break(context, i);
5016 context->u.buffer.glyph_props[i].isClusterStart = 0;
5017 context->glyph_infos[i].start_text_idx = 0;
5020 /* Delete ligated glyphs, backwards to preserve index. */
5021 for (i = 1; i < comp_count; ++i)
5023 opentype_layout_delete_glyph(context, match_positions[comp_count - i]);
5026 /* Skip whole matched sequence, accounting for deleted glyphs. */
5027 context->cur += match_length - (comp_count - 1);
5029 return TRUE;
5032 static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5033 unsigned int subtable_offset)
5035 const struct dwrite_fonttable *table = &context->table->table;
5036 UINT16 format, coverage, glyph, lig_set_offset;
5037 unsigned int coverage_index;
5039 glyph = context->u.subst.glyphs[context->cur];
5041 format = table_read_be_word(table, subtable_offset);
5043 if (format == 1)
5045 const struct ot_gsub_ligsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
5046 unsigned int i;
5047 const UINT16 *offsets;
5048 UINT16 lig_count;
5050 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, coverage));
5052 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5053 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5055 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_set_count),
5056 coverage_index, &lig_set_offset))
5057 return FALSE;
5059 if (!(offsets = table_read_array_be_word(table, subtable_offset + lig_set_offset, ~0u, &lig_count)))
5060 return FALSE;
5062 /* First applicable ligature is used. */
5063 for (i = 0; i < lig_count; ++i)
5065 if (opentype_layout_apply_ligature(context, subtable_offset + lig_set_offset + GET_BE_WORD(offsets[i]), lookup))
5066 return TRUE;
5069 else
5070 WARN("Unexpected ligature substitution format %d.\n", format);
5072 return FALSE;
5075 static BOOL opentype_layout_context_match_backtrack(const struct match_context *mc, unsigned int count,
5076 const UINT16 *backtrack, unsigned int *match_start)
5078 struct match_data match_data = { .mc = mc, .subtable_offset = mc->backtrack_offset };
5079 struct scriptshaping_context *context = mc->context;
5080 struct glyph_iterator iter;
5081 unsigned int i;
5083 glyph_iterator_init(context, mc->lookup->flags, context->cur, count, &iter);
5084 iter.match_func = mc->match_func;
5085 iter.match_data = &match_data;
5086 iter.glyph_data = backtrack;
5088 for (i = 0; i < count; ++i)
5090 if (!glyph_iterator_prev(&iter))
5091 return FALSE;
5094 *match_start = iter.pos;
5096 return TRUE;
5099 static BOOL opentype_layout_context_match_lookahead(const struct match_context *mc, unsigned int count,
5100 const UINT16 *lookahead, unsigned int offset, unsigned int *end_index)
5102 struct match_data match_data = { .mc = mc, .subtable_offset = mc->lookahead_offset };
5103 struct scriptshaping_context *context = mc->context;
5104 struct glyph_iterator iter;
5105 unsigned int i;
5107 glyph_iterator_init(context, mc->lookup->flags, context->cur + offset - 1, count, &iter);
5108 iter.match_func = mc->match_func;
5109 iter.match_data = &match_data;
5110 iter.glyph_data = lookahead;
5112 for (i = 0; i < count; ++i)
5114 if (!glyph_iterator_next(&iter))
5115 return FALSE;
5118 *end_index = iter.pos;
5120 return TRUE;
5123 static BOOL opentype_layout_context_apply_lookup(struct scriptshaping_context *context, unsigned int count,
5124 unsigned int *match_positions, unsigned int lookup_count, const UINT16 *lookup_records, unsigned int match_length)
5126 unsigned int i, j;
5127 int end, delta;
5129 if (!context->nesting_level_left)
5130 return TRUE;
5132 end = context->cur + match_length;
5134 for (i = 0; i < lookup_count; ++i)
5136 unsigned int idx = GET_BE_WORD(lookup_records[i]);
5137 unsigned int orig_len, lookup_index, next;
5139 if (idx >= count)
5140 continue;
5142 context->cur = match_positions[idx];
5144 orig_len = context->glyph_count;
5146 lookup_index = GET_BE_WORD(lookup_records[i+1]);
5148 --context->nesting_level_left;
5149 context->u.buffer.apply_context_lookup(context, lookup_index);
5150 ++context->nesting_level_left;
5152 delta = context->glyph_count - orig_len;
5153 if (!delta)
5154 continue;
5156 end += delta;
5157 if (end <= (int)match_positions[idx])
5159 end = match_positions[idx];
5160 break;
5163 next = idx + 1;
5165 if (delta > 0)
5167 if (delta + count > GLYPH_CONTEXT_MAX_LENGTH)
5168 break;
5170 else
5172 delta = max(delta, (int)next - (int)count);
5173 next -= delta;
5176 memmove(match_positions + next + delta, match_positions + next,
5177 (count - next) * sizeof (*match_positions));
5178 next += delta;
5179 count += delta;
5181 for (j = idx + 1; j < next; j++)
5182 match_positions[j] = match_positions[j - 1] + 1;
5184 for (; next < count; next++)
5185 match_positions[next] += delta;
5188 context->cur = end;
5190 return TRUE;
5193 static BOOL opentype_layout_apply_chain_context_match(unsigned int backtrack_count, const UINT16 *backtrack,
5194 unsigned int input_count, const UINT16 *input, unsigned int lookahead_count, const UINT16 *lookahead,
5195 unsigned int lookup_count, const UINT16 *lookup_records, const struct match_context *mc)
5197 unsigned int start_index = 0, match_length = 0, end_index = 0;
5198 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5200 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5201 opentype_layout_context_match_backtrack(mc, backtrack_count, backtrack, &start_index) &&
5202 opentype_layout_context_match_lookahead(mc, lookahead_count, lookahead, input_count, &end_index) &&
5203 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count, lookup_records, match_length);
5206 static BOOL opentype_layout_apply_chain_rule_set(const struct match_context *mc, unsigned int offset)
5208 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5209 const struct dwrite_fonttable *table = &mc->context->table->table;
5210 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5211 const struct ot_gsubgpos_ruleset *ruleset;
5212 unsigned int i, count;
5214 count = table_read_be_word(table, offset);
5215 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5217 for (i = 0; i < count; ++i)
5219 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5221 backtrack_count = table_read_be_word(table, rule_offset);
5222 rule_offset += 2;
5223 backtrack = table_read_ensure(table, rule_offset, backtrack_count * sizeof(*backtrack));
5224 rule_offset += backtrack_count * sizeof(*backtrack);
5226 if (!(input_count = table_read_be_word(table, rule_offset)))
5227 continue;
5229 rule_offset += 2;
5230 input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input));
5231 rule_offset += (input_count - 1) * sizeof(*input);
5233 lookahead_count = table_read_be_word(table, rule_offset);
5234 rule_offset += 2;
5235 lookahead = table_read_ensure(table, rule_offset, lookahead_count * sizeof(*lookahead));
5236 rule_offset += lookahead_count * sizeof(*lookahead);
5238 lookup_count = table_read_be_word(table, rule_offset);
5239 rule_offset += 2;
5240 lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records));
5242 /* First applicable rule is used. */
5243 if (opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input, lookahead_count,
5244 lookahead, lookup_count, lookup_records, mc))
5246 return TRUE;
5250 return FALSE;
5253 static BOOL opentype_layout_apply_context_match(unsigned int input_count, const UINT16 *input, unsigned int lookup_count,
5254 const UINT16 *lookup_records, const struct match_context *mc)
5256 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5257 unsigned int match_length = 0;
5259 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5260 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count,
5261 lookup_records, match_length);
5264 static BOOL opentype_layout_apply_rule_set(const struct match_context *mc, unsigned int offset)
5266 unsigned int input_count, lookup_count;
5267 const struct dwrite_fonttable *table = &mc->context->table->table;
5268 const UINT16 *input, *lookup_records;
5269 const struct ot_gsubgpos_ruleset *ruleset;
5270 unsigned int i, count;
5272 count = table_read_be_word(table, offset);
5273 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5275 for (i = 0; i < count; ++i)
5277 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5279 if (!(input_count = table_read_be_word(table, rule_offset)))
5280 continue;
5281 rule_offset += 2;
5283 if (!(lookup_count = table_read_be_word(table, rule_offset)))
5284 continue;
5285 rule_offset += 2;
5287 if (!(input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input))))
5288 continue;
5289 rule_offset += (input_count - 1) * sizeof(*input);
5291 if (!(lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records))))
5292 continue;
5294 /* First applicable rule is used. */
5295 if (opentype_layout_apply_context_match(input_count, input, lookup_count, lookup_records, mc))
5296 return TRUE;
5299 return FALSE;
5302 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
5303 unsigned int subtable_offset)
5305 struct match_context mc = { .context = context, .lookup = lookup };
5306 const struct dwrite_fonttable *table = &context->table->table;
5307 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5308 UINT16 glyph, format, coverage;
5309 BOOL ret = FALSE;
5311 glyph = context->u.subst.glyphs[context->cur];
5313 format = table_read_be_word(table, subtable_offset);
5315 if (format == 1)
5317 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5319 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5320 if (coverage_index == GLYPH_NOT_COVERED)
5321 return FALSE;
5323 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5324 if (coverage_index >= count)
5325 return FALSE;
5327 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5328 rulesets[coverage_index]));
5329 offset += subtable_offset;
5331 mc.match_func = opentype_match_glyph_func;
5333 ret = opentype_layout_apply_rule_set(&mc, offset);
5335 else if (format == 2)
5337 unsigned int input_classdef, rule_set_idx;
5339 offset = subtable_offset + 2 /* format */;
5341 coverage = table_read_be_word(table, offset);
5342 offset += 2;
5344 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5345 if (coverage_index == GLYPH_NOT_COVERED)
5346 return FALSE;
5348 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5349 offset += 2;
5351 count = table_read_be_word(table, offset);
5352 offset+= 2;
5354 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5355 if (rule_set_idx >= count)
5356 return FALSE;
5358 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5359 offset += subtable_offset;
5361 mc.input_offset = input_classdef;
5362 mc.match_func = opentype_match_class_func;
5364 ret = opentype_layout_apply_rule_set(&mc, offset);
5366 else if (format == 3)
5368 unsigned int input_count, lookup_count;
5369 const UINT16 *input, *lookup_records;
5371 offset = subtable_offset + 2 /* format */;
5373 input_count = table_read_be_word(table, offset);
5374 offset += 2;
5376 if (!input_count)
5377 return FALSE;
5379 lookup_count = table_read_be_word(table, offset);
5380 offset += 2;
5382 if (!(input = table_read_ensure(table, offset, sizeof(*input) * input_count)))
5383 return FALSE;
5384 offset += sizeof(*input) * input_count;
5386 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5387 if (coverage_index == GLYPH_NOT_COVERED)
5388 return FALSE;
5390 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5392 mc.input_offset = subtable_offset;
5393 mc.match_func = opentype_match_coverage_func;
5395 ret = opentype_layout_apply_context_match(input_count, input + 1, lookup_count, lookup_records, &mc);
5397 else
5398 WARN("Unknown contextual substitution format %u.\n", format);
5400 return ret;
5403 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
5404 unsigned int subtable_offset)
5406 struct match_context mc = { .context = context, .lookup = lookup };
5407 const struct dwrite_fonttable *table = &context->table->table;
5408 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5409 UINT16 glyph, format, coverage;
5410 BOOL ret = FALSE;
5412 glyph = context->u.subst.glyphs[context->cur];
5414 format = table_read_be_word(table, subtable_offset);
5416 if (format == 1)
5418 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5420 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5421 if (coverage_index == GLYPH_NOT_COVERED)
5422 return FALSE;
5424 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5425 if (coverage_index >= count)
5426 return FALSE;
5428 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5429 rulesets[coverage_index]));
5430 offset += subtable_offset;
5432 mc.match_func = opentype_match_glyph_func;
5434 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5436 else if (format == 2)
5438 unsigned int backtrack_classdef, input_classdef, lookahead_classdef, rule_set_idx;
5440 offset = subtable_offset + 2 /* format */;
5442 coverage = table_read_be_word(table, offset);
5443 offset += 2;
5445 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5446 if (coverage_index == GLYPH_NOT_COVERED)
5447 return FALSE;
5449 backtrack_classdef = table_read_be_word(table, offset) + subtable_offset;
5450 offset += 2;
5452 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5453 offset += 2;
5455 lookahead_classdef = table_read_be_word(table, offset) + subtable_offset;
5456 offset += 2;
5458 count = table_read_be_word(table, offset);
5459 offset+= 2;
5461 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5462 if (rule_set_idx >= count)
5463 return FALSE;
5465 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5466 offset += subtable_offset;
5468 mc.backtrack_offset = backtrack_classdef;
5469 mc.input_offset = input_classdef;
5470 mc.lookahead_offset = lookahead_classdef;
5471 mc.match_func = opentype_match_class_func;
5473 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5475 else if (format == 3)
5477 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5478 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5480 offset = subtable_offset + 2 /* format */;
5482 backtrack_count = table_read_be_word(table, offset);
5483 offset += 2;
5484 backtrack = table_read_ensure(table, offset, backtrack_count * sizeof(*backtrack));
5485 offset += backtrack_count * sizeof(*backtrack);
5487 input_count = table_read_be_word(table, offset);
5488 offset += 2;
5489 input = table_read_ensure(table, offset, input_count * sizeof(*input));
5490 offset += input_count * sizeof(*input);
5492 lookahead_count = table_read_be_word(table, offset);
5493 offset += 2;
5494 lookahead = table_read_ensure(table, offset, lookahead_count * sizeof(*lookahead));
5495 offset += lookahead_count * sizeof(*lookahead);
5497 lookup_count = table_read_be_word(table, offset);
5498 offset += 2;
5499 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5501 if (input)
5502 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5504 if (coverage_index == GLYPH_NOT_COVERED)
5505 return FALSE;
5507 mc.backtrack_offset = subtable_offset;
5508 mc.input_offset = subtable_offset;
5509 mc.lookahead_offset = subtable_offset;
5510 mc.match_func = opentype_match_coverage_func;
5512 ret = opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input + 1, lookahead_count,
5513 lookahead, lookup_count, lookup_records, &mc);
5515 else
5516 WARN("Unknown chaining contextual substitution format %u.\n", format);
5518 return ret;
5521 static BOOL opentype_layout_apply_gsub_reverse_chain_context_substitution(struct scriptshaping_context *context,
5522 const struct lookup *lookup, unsigned int subtable_offset)
5524 const struct dwrite_fonttable *table = &context->table->table;
5525 unsigned int offset = subtable_offset;
5526 UINT16 glyph, format;
5528 if (context->nesting_level_left != SHAPE_MAX_NESTING_LEVEL)
5529 return FALSE;
5531 glyph = context->u.subst.glyphs[context->cur];
5533 format = table_read_be_word(table, offset);
5534 offset += 2;
5536 if (format == 1)
5538 struct match_context mc = { .context = context, .lookup = lookup };
5539 unsigned int start_index = 0, end_index = 0, backtrack_count, lookahead_count;
5540 unsigned int coverage, coverage_index;
5541 const UINT16 *backtrack, *lookahead;
5543 coverage = table_read_be_word(table, offset);
5544 offset += 2;
5546 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5547 if (coverage_index == GLYPH_NOT_COVERED)
5548 return FALSE;
5550 backtrack_count = table_read_be_word(table, offset);
5551 offset += 2;
5553 backtrack = table_read_ensure(table, offset, sizeof(*backtrack) * backtrack_count);
5554 offset += sizeof(*backtrack) * backtrack_count;
5556 lookahead_count = table_read_be_word(table, offset);
5557 offset += 2;
5559 lookahead = table_read_ensure(table, offset, sizeof(*lookahead) * lookahead_count);
5560 offset += sizeof(*lookahead) * lookahead_count;
5562 mc.match_func = opentype_match_coverage_func;
5563 mc.backtrack_offset = subtable_offset;
5564 mc.lookahead_offset = subtable_offset;
5566 if (opentype_layout_context_match_backtrack(&mc, backtrack_count, backtrack, &start_index) &&
5567 opentype_layout_context_match_lookahead(&mc, lookahead_count, lookahead, 1, &end_index))
5569 unsigned int glyph_count = table_read_be_word(table, offset);
5570 if (coverage_index >= glyph_count)
5571 return FALSE;
5572 offset += 2;
5574 glyph = table_read_be_word(table, offset + coverage_index * sizeof(glyph));
5575 opentype_layout_replace_glyph(context, glyph);
5577 return TRUE;
5580 else
5581 WARN("Unknown reverse chaining contextual substitution format %u.\n", format);
5583 return FALSE;
5586 static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
5588 unsigned int i, lookup_type;
5589 BOOL ret = FALSE;
5591 for (i = 0; i < lookup->subtable_count; ++i)
5593 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
5595 switch (lookup_type)
5597 case GSUB_LOOKUP_SINGLE_SUBST:
5598 ret = opentype_layout_apply_gsub_single_substitution(context, lookup, subtable_offset);
5599 break;
5600 case GSUB_LOOKUP_MULTIPLE_SUBST:
5601 ret = opentype_layout_apply_gsub_mult_substitution(context, lookup, subtable_offset);
5602 break;
5603 case GSUB_LOOKUP_ALTERNATE_SUBST:
5604 ret = opentype_layout_apply_gsub_alt_substitution(context, lookup, subtable_offset);
5605 break;
5606 case GSUB_LOOKUP_LIGATURE_SUBST:
5607 ret = opentype_layout_apply_gsub_lig_substitution(context, lookup, subtable_offset);
5608 break;
5609 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
5610 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
5611 break;
5612 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
5613 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
5614 break;
5615 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
5616 ret = opentype_layout_apply_gsub_reverse_chain_context_substitution(context, lookup, subtable_offset);
5617 break;
5618 case GSUB_LOOKUP_EXTENSION_SUBST:
5619 WARN("Invalid lookup type for extension substitution %#x.\n", lookup_type);
5620 break;
5621 default:
5622 WARN("Unknown lookup type %u.\n", lookup_type);
5625 if (ret)
5626 break;
5629 return ret;
5632 static unsigned int unicode_get_mirrored_char(unsigned int codepoint)
5634 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
5635 WCHAR mirror;
5636 /* TODO: check if mirroring for higher planes makes sense at all */
5637 if (codepoint > 0xffff) return codepoint;
5638 mirror = get_table_entry(wine_mirror_map, codepoint);
5639 return mirror ? mirror : codepoint;
5643 * 034F # Mn COMBINING GRAPHEME JOINER
5644 * 061C # Cf ARABIC LETTER MARK
5645 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5646 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5647 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5648 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5650 static unsigned int opentype_is_default_ignorable(unsigned int codepoint)
5652 return codepoint == 0x34f || codepoint == 0x61c || codepoint == 0xfeff ||
5653 (codepoint >= 0x180b && codepoint <= 0x180e) || (codepoint >= 0x200b && codepoint <= 0x200f);
5656 static unsigned int opentype_is_diacritic(unsigned int codepoint)
5658 WCHAR ch = codepoint;
5659 WORD type = 0;
5660 /* Ignore higher planes for now. */
5661 if (codepoint > 0xffff) return 0;
5662 GetStringTypeW(CT_CTYPE3, &ch, 1, &type);
5663 return !!(type & C3_DIACRITIC);
5666 static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, const struct shaping_features *features)
5668 unsigned int rtlm_mask = shaping_features_get_mask(features, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), NULL);
5669 const struct shaping_font_ops *font = context->cache->font;
5670 unsigned int i, g, c, codepoint, cluster_start_idx = 0;
5671 UINT16 *clustermap = context->u.subst.clustermap;
5672 const WCHAR *text = context->text;
5673 BOOL bmp;
5675 memset(context->u.subst.glyph_props, 0, context->u.subst.max_glyph_count * sizeof(*context->u.subst.glyph_props));
5676 memset(context->u.buffer.text_props, 0, context->length * sizeof(*context->u.buffer.text_props));
5678 for (i = 0; i < context->length; ++i)
5680 g = context->glyph_count;
5682 if ((bmp = !(IS_HIGH_SURROGATE(text[i]) && (i < context->length - 1) && IS_LOW_SURROGATE(text[i + 1]))))
5684 codepoint = text[i];
5686 else
5688 codepoint = 0x10000 + ((text[i] - 0xd800) << 10) + (text[i + 1] - 0xdc00);
5691 if (context->is_rtl)
5693 c = unicode_get_mirrored_char(codepoint);
5694 if (c != codepoint && font->has_glyph(context->cache->context, c))
5695 codepoint = c;
5696 else
5697 context->glyph_infos[i].mask |= rtlm_mask;
5700 /* TODO: should this check for glyph availability? */
5701 if (*context->u.subst.digits && codepoint >= '0' && codepoint <= '9')
5702 codepoint = context->u.subst.digits[codepoint - '0'];
5704 context->u.buffer.glyphs[g] = font->get_glyph(context->cache->context, codepoint);
5705 context->u.buffer.glyph_props[g].justification = SCRIPT_JUSTIFY_CHARACTER;
5706 opentype_set_subst_glyph_props(context, g);
5708 /* Group diacritics with preceding base. Glyph class is ignored here. */
5709 if (!g || !opentype_is_diacritic(codepoint))
5711 context->u.buffer.glyph_props[g].isClusterStart = 1;
5712 context->glyph_infos[g].start_text_idx = i;
5713 cluster_start_idx = g;
5716 if (opentype_is_default_ignorable(codepoint))
5717 context->u.buffer.glyph_props[g].isZeroWidthSpace = 1;
5718 context->u.buffer.glyph_props[g].components = 1;
5719 context->glyph_count++;
5721 /* Set initial cluster map here, it's used for setting user features masks. */
5722 clustermap[i] = cluster_start_idx;
5723 if (bmp)
5724 context->u.buffer.text_props[i].canBreakShapingAfter = 1;
5725 else
5727 clustermap[i + 1] = cluster_start_idx;
5728 context->u.buffer.text_props[i + 1].canBreakShapingAfter = 1;
5729 ++i;
5734 static BOOL opentype_is_gsub_lookup_reversed(const struct scriptshaping_context *context, const struct lookup *lookup)
5736 unsigned int lookup_type;
5738 opentype_layout_get_gsubgpos_subtable(context, lookup, 0, &lookup_type);
5739 return lookup_type == GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST;
5742 static void opentype_layout_apply_gsub_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
5744 struct lookup lookup = { 0 };
5745 /* Feature mask is intentionally zero, it's not used outside of main loop. */
5746 if (opentype_layout_init_lookup(context->table, lookup_index, 0, &lookup))
5747 opentype_layout_apply_gsub_lookup(context, &lookup);
5750 void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int script_index,
5751 unsigned int language_index, const struct shaping_features *features)
5753 struct lookups lookups = { 0 };
5754 unsigned int i, j, start_idx;
5755 BOOL ret;
5757 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
5758 context->u.buffer.apply_context_lookup = opentype_layout_apply_gsub_context_lookup;
5759 opentype_layout_collect_lookups(context, script_index, language_index, features, context->table, &lookups);
5761 opentype_get_nominal_glyphs(context, features);
5762 opentype_layout_set_glyph_masks(context, features);
5764 for (i = 0; i < lookups.count; ++i)
5766 const struct lookup *lookup = &lookups.lookups[i];
5768 context->lookup_mask = lookup->mask;
5770 if (!opentype_is_gsub_lookup_reversed(context, lookup))
5772 context->cur = 0;
5773 while (context->cur < context->glyph_count)
5775 ret = FALSE;
5777 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
5778 lookup_is_glyph_match(context, context->cur, lookup->flags))
5780 ret = opentype_layout_apply_gsub_lookup(context, lookup);
5783 if (!ret)
5784 context->cur++;
5787 else
5789 context->cur = context->glyph_count - 1;
5791 for (;;)
5793 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
5794 lookup_is_glyph_match(context, context->cur, lookup->flags))
5796 opentype_layout_apply_gsub_lookup(context, lookup);
5799 if (context->cur == 0) break;
5800 --context->cur;
5805 /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding
5806 text span to start_idx. */
5807 start_idx = 0;
5808 for (i = 1; i < context->glyph_count; ++i)
5810 if (context->u.buffer.glyph_props[i].isClusterStart)
5812 unsigned int start_text, end_text;
5814 start_text = context->glyph_infos[start_idx].start_text_idx;
5815 end_text = context->glyph_infos[i].start_text_idx;
5817 for (j = start_text; j < end_text; ++j)
5818 context->u.buffer.clustermap[j] = start_idx;
5820 start_idx = i;
5824 /* Fill the tail. */
5825 for (j = context->glyph_infos[start_idx].start_text_idx; j < context->length; ++j)
5826 context->u.buffer.clustermap[j] = start_idx;
5828 heap_free(lookups.lookups);
5831 static BOOL opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5832 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
5834 const struct dwrite_fonttable *table = &context->table->table;
5835 const UINT16 *offsets;
5836 unsigned int count;
5838 if (format == 1 || format == 2)
5840 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5841 return TRUE;
5843 else if (format == 3)
5845 count = table_read_be_word(table, subtable_offset + 2);
5846 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6, count * sizeof(*offsets))))
5847 return FALSE;
5849 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
5850 return TRUE;
5853 return FALSE;
5856 static BOOL opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5857 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
5859 const struct dwrite_fonttable *table = &context->table->table;
5860 unsigned int count, backtrack_count;
5861 const UINT16 *offsets;
5863 if (format == 1 || format == 2)
5865 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5866 return TRUE;
5868 else if (format == 3)
5870 backtrack_count = table_read_be_word(table, subtable_offset + 2);
5872 count = table_read_be_word(table, subtable_offset + 4 + backtrack_count * sizeof(*offsets));
5874 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6 + backtrack_count * sizeof(*offsets),
5875 count * sizeof(*offsets))))
5876 return FALSE;
5878 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
5879 return TRUE;
5882 return FALSE;
5885 static BOOL opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5886 const struct lookup *lookup)
5888 const struct dwrite_fonttable *gsub = &context->table->table;
5889 static const unsigned short gsub_formats[] =
5891 0, /* Unused */
5892 1, /* SingleSubst */
5893 1, /* MultipleSubst */
5894 1, /* AlternateSubst */
5895 1, /* LigatureSubst */
5896 3, /* ContextSubst */
5897 3, /* ChainContextSubst */
5898 0, /* Extension, unused */
5899 1, /* ReverseChainSubst */
5901 unsigned int i, coverage, lookup_type, format;
5903 for (i = 0; i < lookup->subtable_count; ++i)
5905 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
5907 format = table_read_be_word(gsub, subtable_offset);
5909 if (!format || format > ARRAY_SIZE(gsub_formats) || format > gsub_formats[lookup_type])
5910 break;
5912 coverage = table_read_be_word(gsub, subtable_offset + 2);
5914 switch (lookup_type)
5916 case GSUB_LOOKUP_SINGLE_SUBST:
5917 case GSUB_LOOKUP_MULTIPLE_SUBST:
5918 case GSUB_LOOKUP_ALTERNATE_SUBST:
5919 case GSUB_LOOKUP_LIGATURE_SUBST:
5920 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
5922 if (opentype_layout_is_glyph_covered(gsub, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5923 return TRUE;
5925 break;
5927 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
5929 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
5930 return TRUE;
5932 break;
5934 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
5936 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
5937 return TRUE;
5939 break;
5941 default:
5942 WARN("Unknown lookup type %u.\n", lookup_type);
5946 return FALSE;
5949 static BOOL opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5950 const struct lookup *lookup)
5952 const struct dwrite_fonttable *gpos = &context->table->table;
5953 static const unsigned short gpos_formats[] =
5955 0, /* Unused */
5956 2, /* SinglePos */
5957 2, /* PairPos */
5958 1, /* CursivePos */
5959 1, /* MarkBasePos */
5960 1, /* MarkLigPos */
5961 1, /* MarkMarkPos */
5962 3, /* ContextPos */
5963 3, /* ChainContextPos */
5964 0, /* Extension, unused */
5966 unsigned int i, coverage, lookup_type, format;
5968 for (i = 0; i < lookup->subtable_count; ++i)
5970 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
5972 format = table_read_be_word(gpos, subtable_offset);
5974 if (!format || format > ARRAY_SIZE(gpos_formats) || format > gpos_formats[lookup_type])
5975 break;
5977 coverage = table_read_be_word(gpos, subtable_offset + 2);
5979 switch (lookup_type)
5981 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
5982 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
5983 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
5984 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
5985 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
5986 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
5988 if (opentype_layout_is_glyph_covered(gpos, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5989 return TRUE;
5991 break;
5993 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
5995 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
5996 return TRUE;
5998 break;
6000 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
6002 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6003 return TRUE;
6005 break;
6007 default:
6008 WARN("Unknown lookup type %u.\n", lookup_type);
6012 return FALSE;
6015 typedef BOOL (*p_lookup_is_glyph_covered_func)(struct scriptshaping_context *context, UINT16 glyph, const struct lookup *lookup);
6017 BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index,
6018 unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count,
6019 const UINT16 *glyphs, UINT8 *feature_applies)
6021 p_lookup_is_glyph_covered_func func_is_covered;
6022 struct shaping_features features = { 0 };
6023 struct lookups lookups = { 0 };
6024 BOOL ret = FALSE, is_covered;
6025 unsigned int i, j, applies;
6027 features.features = feature;
6028 features.count = 1;
6030 for (i = 0; i < context->glyph_count; ++i)
6031 opentype_set_glyph_props(context, i);
6033 opentype_layout_collect_lookups(context, script_index, language_index, &features, context->table, &lookups);
6035 func_is_covered = context->table == &context->cache->gsub ? opentype_layout_gsub_lookup_is_glyph_covered :
6036 opentype_layout_gpos_lookup_is_glyph_covered;
6038 for (i = 0; i < lookups.count; ++i)
6040 struct lookup *lookup = &lookups.lookups[i];
6042 applies = 0;
6043 for (j = 0; j < context->glyph_count; ++j)
6045 if (lookup_is_glyph_match(context, j, lookup->flags))
6047 if ((is_covered = func_is_covered(context, glyphs[i], lookup)))
6048 ++applies;
6049 feature_applies[j] |= is_covered;
6053 if ((ret = (applies == context->glyph_count)))
6054 break;
6057 heap_free(lookups.lookups);
6059 return ret;
6062 BOOL opentype_has_vertical_variants(struct dwrite_fontface *fontface)
6064 unsigned int i, j, count = 0, lookup_type, subtable_offset;
6065 struct shaping_features features = { 0 };
6066 struct shaping_feature vert_feature = { 0 };
6067 struct scriptshaping_context context = { 0 };
6068 struct lookups lookups = { 0 };
6069 UINT16 format;
6071 context.cache = fontface_get_shaping_cache(fontface);
6072 context.table = &context.cache->gsub;
6074 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6075 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6076 vert_feature.max_value = 1;
6077 vert_feature.default_value = 1;
6079 features.features = &vert_feature;
6080 features.count = features.capacity = 1;
6082 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6084 for (i = 0; i < lookups.count && !count; ++i)
6086 const struct dwrite_fonttable *table = &context.table->table;
6087 const struct lookup *lookup = &lookups.lookups[i];
6089 for (j = 0; j < lookup->subtable_count && !count; ++j)
6091 subtable_offset = opentype_layout_get_gsubgpos_subtable(&context, lookup, j, &lookup_type);
6093 if (lookup_type != GSUB_LOOKUP_SINGLE_SUBST)
6094 continue;
6096 format = table_read_be_word(table, subtable_offset);
6098 if (format == 1)
6100 count = 1;
6102 else if (format == 2)
6104 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count));
6106 else
6107 WARN("Unrecognized single substitution format %u.\n", format);
6111 heap_free(lookups.lookups);
6113 return !!count;
6116 HRESULT opentype_get_vertical_glyph_variants(struct dwrite_fontface *fontface, unsigned int glyph_count,
6117 const UINT16 *nominal_glyphs, UINT16 *glyphs)
6119 struct shaping_features features = { 0 };
6120 struct shaping_feature vert_feature = { 0 };
6121 struct scriptshaping_context context = { 0 };
6122 struct lookups lookups = { 0 };
6123 unsigned int i;
6125 memcpy(glyphs, nominal_glyphs, glyph_count * sizeof(*glyphs));
6127 if (!(fontface->flags & FONTFACE_HAS_VERTICAL_VARIANTS))
6128 return S_OK;
6130 context.cache = fontface_get_shaping_cache(fontface);
6131 context.u.subst.glyphs = glyphs;
6132 context.u.subst.glyph_props = heap_calloc(glyph_count, sizeof(*context.u.subst.glyph_props));
6133 context.u.subst.max_glyph_count = glyph_count;
6134 context.u.subst.capacity = glyph_count;
6135 context.glyph_infos = heap_alloc_zero(sizeof(*context.glyph_infos) * glyph_count);
6136 context.table = &context.cache->gsub;
6138 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6139 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6140 vert_feature.max_value = 1;
6141 vert_feature.default_value = 1;
6143 features.features = &vert_feature;
6144 features.count = features.capacity = 1;
6146 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6147 opentype_layout_set_glyph_masks(&context, &features);
6149 for (i = 0; i < lookups.count; ++i)
6151 const struct lookup *lookup = &lookups.lookups[i];
6153 context.cur = 0;
6154 while (context.cur < context.glyph_count)
6156 BOOL ret = FALSE;
6158 if (lookup_is_glyph_match(&context, context.cur, lookup->flags))
6159 ret = opentype_layout_apply_gsub_lookup(&context, lookup);
6161 if (!ret)
6162 context.cur++;
6166 heap_free(context.u.subst.glyph_props);
6167 heap_free(context.glyph_infos);
6168 heap_free(lookups.lookups);
6170 return S_OK;