dwrite: Use font data access helpers when reading metrics tables.
[wine.git] / dlls / dwrite / opentype.c
blob68da6e4ecfbd300de6cead60f2d4b96794587704
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 static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context *context,
3503 unsigned int lookup_offset, unsigned int subtable)
3505 unsigned int subtable_offset = table_read_be_word(&context->table->table, lookup_offset +
3506 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
3508 return lookup_offset + subtable_offset;
3511 struct ot_lookup
3513 unsigned int offset;
3514 unsigned int subtable_count;
3515 unsigned int flags;
3518 enum iterator_match
3520 /* First two to fit matching callback result. */
3521 ITER_NO = 0,
3522 ITER_YES = 1,
3523 ITER_MAYBE,
3526 struct match_context;
3527 struct match_data
3529 const struct match_context *mc;
3530 unsigned int subtable_offset;
3533 typedef BOOL (*p_match_func)(UINT16 glyph, UINT16 glyph_data, const struct match_data *match_data);
3535 struct match_context
3537 struct scriptshaping_context *context;
3538 unsigned int backtrack_offset;
3539 unsigned int input_offset;
3540 unsigned int lookahead_offset;
3541 p_match_func match_func;
3542 const struct lookup *lookup;
3545 struct glyph_iterator
3547 struct scriptshaping_context *context;
3548 unsigned int flags;
3549 unsigned int pos;
3550 unsigned int len;
3551 unsigned int mask;
3552 p_match_func match_func;
3553 const UINT16 *glyph_data;
3554 const struct match_data *match_data;
3557 static void glyph_iterator_init(struct scriptshaping_context *context, unsigned int flags, unsigned int pos,
3558 unsigned int len, struct glyph_iterator *iter)
3560 iter->context = context;
3561 iter->flags = flags;
3562 iter->pos = pos;
3563 iter->len = len;
3564 iter->mask = ~0u;
3565 iter->match_func = NULL;
3566 iter->match_data = NULL;
3567 iter->glyph_data = NULL;
3570 struct ot_gdef_mark_glyph_sets
3572 UINT16 format;
3573 UINT16 count;
3574 DWORD offsets[1];
3577 static BOOL opentype_match_glyph_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3579 return glyph == glyph_data;
3582 static BOOL opentype_match_class_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3584 const struct match_context *mc = data->mc;
3585 UINT16 glyph_class = opentype_layout_get_glyph_class(&mc->context->table->table, data->subtable_offset, glyph);
3586 return glyph_class == glyph_data;
3589 static BOOL opentype_match_coverage_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3591 const struct match_context *mc = data->mc;
3592 return opentype_layout_is_glyph_covered(&mc->context->table->table, data->subtable_offset + glyph_data, glyph)
3593 != GLYPH_NOT_COVERED;
3596 static BOOL opentype_layout_mark_set_covers(const struct scriptshaping_cache *cache, unsigned int set_index,
3597 UINT16 glyph)
3599 unsigned int format, offset = cache->gdef.markglyphsetdef, coverage_offset, set_count;
3601 if (!offset)
3602 return FALSE;
3604 format = table_read_be_word(&cache->gdef.table, offset);
3606 if (format == 1)
3608 set_count = table_read_be_word(&cache->gdef.table, offset + 2);
3609 if (!set_count || set_index >= set_count)
3610 return FALSE;
3612 coverage_offset = table_read_be_dword(&cache->gdef.table, offset + 2 + set_index * sizeof(coverage_offset));
3613 return opentype_layout_is_glyph_covered(&cache->gdef.table, offset + coverage_offset, glyph) != GLYPH_NOT_COVERED;
3615 else
3616 WARN("Unexpected MarkGlyphSets format %#x.\n", format);
3618 return FALSE;
3621 static BOOL lookup_is_glyph_match(const struct scriptshaping_context *context, unsigned int idx, unsigned int match_props)
3623 unsigned int glyph_props = context->glyph_infos[idx].props;
3624 UINT16 glyph = context->u.buffer.glyphs[idx];
3626 if (glyph_props & match_props & LOOKUP_FLAG_IGNORE_MASK)
3627 return FALSE;
3629 if (!(glyph_props & GLYPH_PROP_MARK))
3630 return TRUE;
3632 if (match_props & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
3633 return opentype_layout_mark_set_covers(context->cache, match_props >> 16, glyph);
3635 if (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE)
3636 return (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE) == (glyph_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE);
3638 return TRUE;
3641 static enum iterator_match glyph_iterator_may_skip(const struct glyph_iterator *iter)
3643 if (!lookup_is_glyph_match(iter->context, iter->pos, iter->flags))
3644 return ITER_YES;
3646 return ITER_NO;
3649 static enum iterator_match glyph_iterator_may_match(const struct glyph_iterator *iter)
3651 if (!(iter->mask & iter->context->glyph_infos[iter->pos].mask))
3652 return ITER_NO;
3654 /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that
3655 in all matching functions. */
3656 if (iter->match_func)
3657 return !!iter->match_func(iter->context->u.buffer.glyphs[iter->pos], GET_BE_WORD(*iter->glyph_data), iter->match_data);
3659 return ITER_MAYBE;
3662 static BOOL glyph_iterator_next(struct glyph_iterator *iter)
3664 enum iterator_match skip, match;
3666 while (iter->pos + iter->len < iter->context->glyph_count)
3668 ++iter->pos;
3670 skip = glyph_iterator_may_skip(iter);
3671 if (skip == ITER_YES)
3672 continue;
3674 match = glyph_iterator_may_match(iter);
3675 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3677 --iter->len;
3678 if (iter->glyph_data)
3679 ++iter->glyph_data;
3680 return TRUE;
3683 if (skip == ITER_NO)
3684 return FALSE;
3687 return FALSE;
3690 static BOOL glyph_iterator_prev(struct glyph_iterator *iter)
3692 enum iterator_match skip, match;
3694 while (iter->pos > iter->len - 1)
3696 --iter->pos;
3698 skip = glyph_iterator_may_skip(iter);
3699 if (skip == ITER_YES)
3700 continue;
3702 match = glyph_iterator_may_match(iter);
3703 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3705 --iter->len;
3706 if (iter->glyph_data)
3707 ++iter->glyph_data;
3708 return TRUE;
3711 if (skip == ITER_NO)
3712 return FALSE;
3715 return FALSE;
3718 struct lookup
3720 unsigned short index;
3721 unsigned short type;
3722 unsigned short flags;
3723 unsigned short subtable_count;
3725 unsigned int mask;
3726 unsigned int offset;
3729 static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context,
3730 const struct lookup *lookup, unsigned int subtable_offset)
3732 const struct dwrite_fonttable *table = &context->table->table;
3733 UINT16 format, value_format, value_len, coverage, glyph;
3735 unsigned int coverage_index;
3737 format = table_read_be_word(table, subtable_offset);
3739 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage));
3740 value_format = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format));
3741 value_len = dwrite_popcount(value_format);
3743 glyph = context->u.pos.glyphs[context->cur];
3745 if (format == 1)
3747 const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(table, subtable_offset,
3748 FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len]));
3750 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3751 if (coverage_index == GLYPH_NOT_COVERED)
3752 return FALSE;
3754 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, context->cur);
3756 else if (format == 2)
3758 WORD value_count = table_read_be_word(table, subtable_offset +
3759 FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count));
3760 const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(table, subtable_offset,
3761 FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD));
3763 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3764 if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count)
3765 return FALSE;
3767 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, &format2->values[coverage_index * value_len],
3768 context->cur);
3770 else
3772 WARN("Unknown single adjustment format %u.\n", format);
3773 return FALSE;
3776 context->cur++;
3778 return TRUE;
3781 static int gpos_pair_adjustment_compare_format1(const void *g, const void *r)
3783 const struct ot_gpos_pairvalue *pairvalue = r;
3784 UINT16 second_glyph = GET_BE_WORD(pairvalue->second_glyph);
3785 return *(UINT16 *)g - second_glyph;
3788 static BOOL opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context *context,
3789 const struct lookup *lookup, unsigned int subtable_offset)
3791 const struct dwrite_fonttable *table = &context->table->table;
3792 unsigned int first_glyph, second_glyph;
3793 struct glyph_iterator iter_pair;
3794 WORD format, coverage;
3796 WORD value_format1, value_format2, value_len1, value_len2;
3797 unsigned int coverage_index;
3799 glyph_iterator_init(context, lookup->flags, context->cur, 1, &iter_pair);
3800 if (!glyph_iterator_next(&iter_pair))
3801 return FALSE;
3803 if (context->is_rtl)
3805 first_glyph = iter_pair.pos;
3806 second_glyph = context->cur;
3808 else
3810 first_glyph = context->cur;
3811 second_glyph = iter_pair.pos;
3814 format = table_read_be_word(table, subtable_offset);
3816 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1, coverage));
3817 if (!coverage)
3818 return FALSE;
3820 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, context->u.pos.glyphs[first_glyph]);
3821 if (coverage_index == GLYPH_NOT_COVERED)
3822 return FALSE;
3824 if (format == 1)
3826 const struct ot_gpos_pairpos_format1 *format1;
3827 WORD pairset_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1,
3828 pairset_count));
3829 unsigned int pairvalue_len, pairset_offset;
3830 const struct ot_gpos_pairset *pairset;
3831 const WORD *pairvalue;
3832 WORD pairvalue_count;
3834 if (!pairset_count || coverage_index >= pairset_count)
3835 return FALSE;
3837 format1 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairsets[pairset_count]));
3838 if (!format1)
3839 return FALSE;
3841 /* Ordered paired values. */
3842 pairvalue_count = table_read_be_word(table, subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]));
3843 if (!pairvalue_count)
3844 return FALSE;
3846 /* Structure length is variable, but does not change across the subtable. */
3847 value_format1 = GET_BE_WORD(format1->value_format1) & 0xff;
3848 value_format2 = GET_BE_WORD(format1->value_format2) & 0xff;
3850 value_len1 = dwrite_popcount(value_format1);
3851 value_len2 = dwrite_popcount(value_format2);
3852 pairvalue_len = FIELD_OFFSET(struct ot_gpos_pairvalue, data) + value_len1 * sizeof(WORD) +
3853 value_len2 * sizeof(WORD);
3855 pairset_offset = subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]);
3856 pairset = table_read_ensure(table, subtable_offset + pairset_offset, pairvalue_len * pairvalue_count);
3857 if (!pairset)
3858 return FALSE;
3860 pairvalue = bsearch(&context->u.pos.glyphs[second_glyph], pairset->pairvalues, pairvalue_count,
3861 pairvalue_len, gpos_pair_adjustment_compare_format1);
3862 if (!pairvalue)
3863 return FALSE;
3865 pairvalue += 1; /* Skip SecondGlyph. */
3866 opentype_layout_apply_gpos_value(context, pairset_offset, value_format1, pairvalue, first_glyph);
3867 opentype_layout_apply_gpos_value(context, pairset_offset, value_format2, pairvalue + value_len1,
3868 second_glyph);
3870 context->cur = iter_pair.pos;
3871 if (value_len2)
3872 context->cur++;
3874 else if (format == 2)
3876 const struct ot_gpos_pairpos_format2 *format2;
3877 WORD class1_count, class2_count;
3878 unsigned int class1, class2;
3879 const WCHAR *values;
3881 value_format1 = table_read_be_word(table, subtable_offset +
3882 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format1)) & 0xff;
3883 value_format2 = table_read_be_word(table, subtable_offset +
3884 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format2)) & 0xff;
3886 class1_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class1_count));
3887 class2_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class2_count));
3889 value_len1 = dwrite_popcount(value_format1);
3890 value_len2 = dwrite_popcount(value_format2);
3892 format2 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format2,
3893 values[class1_count * class2_count * (value_len1 + value_len2)]));
3894 if (!format2)
3895 return FALSE;
3897 class1 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def1),
3898 context->u.pos.glyphs[first_glyph]);
3899 class2 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def2),
3900 context->u.pos.glyphs[second_glyph]);
3902 if (!(class1 < class1_count && class2 < class2_count))
3903 return FALSE;
3905 values = &format2->values[(class1 * class2_count + class2) * (value_len1 + value_len2)];
3906 opentype_layout_apply_gpos_value(context, subtable_offset, value_format1, values, first_glyph);
3907 opentype_layout_apply_gpos_value(context, subtable_offset, value_format2, values + value_len1,
3908 second_glyph);
3910 context->cur = iter_pair.pos;
3911 if (value_len2)
3912 context->cur++;
3914 else
3916 WARN("Unknown pair adjustment format %u.\n", format);
3917 return FALSE;
3920 return TRUE;
3923 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context *context, unsigned int anchor_offset,
3924 unsigned int glyph_index, float *x, float *y)
3926 const struct scriptshaping_cache *cache = context->cache;
3927 const struct dwrite_fonttable *table = &context->table->table;
3929 WORD format = table_read_be_word(table, anchor_offset);
3931 *x = *y = 0.0f;
3933 if (format == 1)
3935 const struct ot_gpos_anchor_format1 *format1 = table_read_ensure(table, anchor_offset, sizeof(*format1));
3937 if (format1)
3939 *x = opentype_scale_gpos_be_value(format1->x_coord, context->emsize, cache->upem);
3940 *y = opentype_scale_gpos_be_value(format1->y_coord, context->emsize, cache->upem);
3943 else if (format == 2)
3945 const struct ot_gpos_anchor_format2 *format2 = table_read_ensure(table, anchor_offset, sizeof(*format2));
3947 if (format2)
3949 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
3950 FIXME("Use outline anchor point for glyph %u.\n", context->u.pos.glyphs[glyph_index]);
3952 *x = opentype_scale_gpos_be_value(format2->x_coord, context->emsize, cache->upem);
3953 *y = opentype_scale_gpos_be_value(format2->y_coord, context->emsize, cache->upem);
3956 else if (format == 3)
3958 const struct ot_gpos_anchor_format3 *format3 = table_read_ensure(table, anchor_offset, sizeof(*format3));
3960 if (format3)
3962 *x = opentype_scale_gpos_be_value(format3->x_coord, context->emsize, cache->upem);
3963 *y = opentype_scale_gpos_be_value(format3->y_coord, context->emsize, cache->upem);
3965 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
3967 if (format3->x_dev_offset)
3968 *x += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->x_dev_offset));
3969 if (format3->y_dev_offset)
3970 *y += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->y_dev_offset));
3974 else
3975 WARN("Unknown anchor format %u.\n", format);
3978 static BOOL opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context *context,
3979 const struct lookup *lookup, unsigned int subtable_offset)
3981 const struct dwrite_fonttable *table = &context->table->table;
3982 UINT16 format, glyph;
3984 format = table_read_be_word(table, subtable_offset);
3985 glyph = context->u.pos.glyphs[context->cur];
3987 if (format == 1)
3989 WORD coverage_offset = table_read_be_word(table, subtable_offset +
3990 FIELD_OFFSET(struct ot_gpos_cursive_format1, coverage));
3991 unsigned int glyph_index, entry_count, entry_anchor, exit_anchor;
3992 float entry_x, entry_y, exit_x, exit_y, delta;
3993 struct glyph_iterator prev_iter;
3995 if (!coverage_offset)
3996 return FALSE;
3998 entry_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_cursive_format1, count));
4000 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset, glyph);
4001 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4002 return FALSE;
4004 entry_anchor = table_read_be_word(table, subtable_offset +
4005 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2]));
4006 if (!entry_anchor)
4007 return FALSE;
4009 glyph_iterator_init(context, lookup->flags, context->cur, 1, &prev_iter);
4010 if (!glyph_iterator_prev(&prev_iter))
4011 return FALSE;
4013 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset,
4014 context->u.pos.glyphs[prev_iter.pos]);
4015 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4016 return FALSE;
4018 exit_anchor = table_read_be_word(table, subtable_offset +
4019 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2 + 1]));
4020 if (!exit_anchor)
4021 return FALSE;
4023 opentype_layout_gpos_get_anchor(context, subtable_offset + exit_anchor, prev_iter.pos, &exit_x, &exit_y);
4024 opentype_layout_gpos_get_anchor(context, subtable_offset + entry_anchor, context->cur, &entry_x, &entry_y);
4026 if (context->is_rtl)
4028 delta = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4029 context->advances[prev_iter.pos] -= delta;
4030 context->advances[context->cur] = entry_x + context->offsets[context->cur].advanceOffset;
4031 context->offsets[prev_iter.pos].advanceOffset -= delta;
4033 else
4035 delta = entry_x + context->offsets[context->cur].advanceOffset;
4036 context->advances[prev_iter.pos] = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4037 context->advances[context->cur] -= delta;
4038 context->offsets[context->cur].advanceOffset -= delta;
4041 if (lookup->flags & LOOKUP_FLAG_RTL)
4042 context->offsets[prev_iter.pos].ascenderOffset = entry_y - exit_y;
4043 else
4044 context->offsets[context->cur].ascenderOffset = exit_y - entry_y;
4046 context->cur++;
4048 else
4050 WARN("Unknown cursive attachment format %u.\n", format);
4051 return FALSE;
4054 return TRUE;
4057 static BOOL opentype_layout_apply_mark_array(struct scriptshaping_context *context, unsigned int subtable_offset,
4058 unsigned int mark_array, unsigned int mark_index, unsigned int glyph_index, unsigned int anchors_matrix,
4059 unsigned int class_count, unsigned int glyph_pos)
4061 const struct dwrite_fonttable *table = &context->table->table;
4062 unsigned int mark_class, mark_count, glyph_count;
4063 const struct ot_gpos_mark_record *record;
4064 float mark_x, mark_y, base_x, base_y;
4065 const UINT16 *anchors;
4067 mark_count = table_read_be_word(table, subtable_offset + mark_array);
4068 if (mark_index >= mark_count) return FALSE;
4070 if (!(record = table_read_ensure(table, subtable_offset + mark_array +
4071 FIELD_OFFSET(struct ot_gpos_mark_array, records[mark_index]), sizeof(*record))))
4073 return FALSE;
4076 mark_class = GET_BE_WORD(record->mark_class);
4077 if (mark_class >= class_count) return FALSE;
4079 glyph_count = table_read_be_word(table, subtable_offset + anchors_matrix);
4080 if (glyph_index >= glyph_count) return FALSE;
4082 /* Anchors data is stored as two dimensional array [glyph_count][class_count], starting with row count field. */
4083 anchors = table_read_ensure(table, subtable_offset + anchors_matrix + 2, glyph_count * class_count * sizeof(*anchors));
4084 if (!anchors) return FALSE;
4086 opentype_layout_gpos_get_anchor(context, subtable_offset + mark_array + GET_BE_WORD(record->mark_anchor),
4087 context->cur, &mark_x, &mark_y);
4088 opentype_layout_gpos_get_anchor(context, subtable_offset + anchors_matrix +
4089 GET_BE_WORD(anchors[glyph_index * class_count + mark_class]), glyph_pos, &base_x, &base_y);
4091 if (context->is_rtl)
4092 context->offsets[context->cur].advanceOffset = mark_x - base_x;
4093 else
4094 context->offsets[context->cur].advanceOffset = -context->advances[glyph_pos] + base_x - mark_x;
4096 context->offsets[context->cur].ascenderOffset = base_y - mark_y;
4097 context->cur++;
4099 return TRUE;
4102 static BOOL opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context *context,
4103 const struct lookup *lookup, unsigned int subtable_offset)
4105 const struct dwrite_fonttable *table = &context->table->table;
4106 WORD format;
4108 format = table_read_be_word(table, subtable_offset);
4110 if (format == 1)
4112 const struct ot_gpos_mark_to_base_format1 *format1;
4113 unsigned int base_index, mark_index;
4114 struct glyph_iterator base_iter;
4116 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4118 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4119 context->u.pos.glyphs[context->cur]);
4120 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4122 /* Look back for first base glyph. */
4123 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &base_iter);
4124 if (!glyph_iterator_prev(&base_iter))
4125 return FALSE;
4127 base_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->base_coverage),
4128 context->u.pos.glyphs[base_iter.pos]);
4129 if (base_index == GLYPH_NOT_COVERED) return FALSE;
4131 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4132 base_index, GET_BE_WORD(format1->base_array), GET_BE_WORD(format1->mark_class_count), base_iter.pos);
4134 else
4136 WARN("Unknown mark-to-base format %u.\n", format);
4137 return FALSE;
4140 return TRUE;
4143 static const UINT16 * table_read_array_be_word(const struct dwrite_fonttable *table, unsigned int offset,
4144 unsigned int index, UINT16 *data)
4146 unsigned int count = table_read_be_word(table, offset);
4147 const UINT16 *array;
4149 if (index != ~0u && index >= count) return NULL;
4150 if (!(array = table_read_ensure(table, offset + 2, count * sizeof(*array)))) return FALSE;
4151 *data = index == ~0u ? count : GET_BE_WORD(array[index]);
4152 return array;
4155 static BOOL opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context *context,
4156 const struct lookup *lookup, unsigned int subtable_offset)
4158 const struct dwrite_fonttable *table = &context->table->table;
4159 WORD format;
4161 format = table_read_be_word(table, subtable_offset);
4163 if (format == 1)
4165 unsigned int mark_index, lig_index, comp_index, class_count, comp_count;
4166 const struct ot_gpos_mark_to_lig_format1 *format1;
4167 struct glyph_iterator lig_iter;
4168 unsigned int lig_array;
4169 UINT16 lig_attach;
4171 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4173 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4174 context->u.pos.glyphs[context->cur]);
4175 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4177 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &lig_iter);
4178 if (!glyph_iterator_prev(&lig_iter))
4179 return FALSE;
4181 lig_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->lig_coverage),
4182 context->u.pos.glyphs[lig_iter.pos]);
4183 if (lig_index == GLYPH_NOT_COVERED) return FALSE;
4185 class_count = GET_BE_WORD(format1->mark_class_count);
4187 lig_array = GET_BE_WORD(format1->lig_array);
4189 if (!table_read_array_be_word(table, subtable_offset + lig_array, lig_index, &lig_attach)) return FALSE;
4191 comp_count = table_read_be_word(table, subtable_offset + lig_array + lig_attach);
4192 if (!comp_count) return FALSE;
4194 comp_index = context->u.buffer.glyph_props[lig_iter.pos].components -
4195 context->u.buffer.glyph_props[context->cur].lig_component - 1;
4196 if (comp_index >= comp_count) return FALSE;
4198 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4199 comp_index, lig_array + lig_attach, class_count, lig_iter.pos);
4201 else
4202 WARN("Unknown mark-to-ligature format %u.\n", format);
4204 return FALSE;
4207 static BOOL opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context *context,
4208 const struct lookup *lookup, unsigned int subtable_offset)
4210 const struct dwrite_fonttable *table = &context->table->table;
4211 WORD format;
4213 format = table_read_be_word(table, subtable_offset);
4215 if (format == 1)
4217 const struct ot_gpos_mark_to_mark_format1 *format1;
4218 unsigned int mark1_index, mark2_index;
4219 struct glyph_iterator mark_iter;
4221 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4223 mark1_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark1_coverage),
4224 context->u.pos.glyphs[context->cur]);
4225 if (mark1_index == GLYPH_NOT_COVERED) return FALSE;
4227 glyph_iterator_init(context, lookup->flags & ~LOOKUP_FLAG_IGNORE_MASK, context->cur, 1, &mark_iter);
4228 if (!glyph_iterator_prev(&mark_iter))
4229 return FALSE;
4231 if (!context->u.pos.glyph_props[mark_iter.pos].isDiacritic)
4232 return FALSE;
4234 mark2_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark2_coverage),
4235 context->u.pos.glyphs[mark_iter.pos]);
4236 if (mark2_index == GLYPH_NOT_COVERED) return FALSE;
4238 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark1_array), mark1_index,
4239 mark2_index, GET_BE_WORD(format1->mark2_array), GET_BE_WORD(format1->mark_class_count), mark_iter.pos);
4241 else
4243 WARN("Unknown mark-to-mark format %u.\n", format);
4244 return FALSE;
4247 return TRUE;
4250 static unsigned int opentype_layout_adjust_extension_subtable(struct scriptshaping_context *context,
4251 unsigned int *subtable_offset, const struct lookup *lookup)
4253 const struct ot_gsubgpos_extension_format1 *format1;
4255 if ((context->table == &context->cache->gsub && lookup->type != GSUB_LOOKUP_EXTENSION_SUBST) ||
4256 (context->table == &context->cache->gpos && lookup->type != GPOS_LOOKUP_EXTENSION_POSITION))
4258 return lookup->type;
4261 if (!(format1 = table_read_ensure(&context->table->table, *subtable_offset, sizeof(*format1))))
4262 return 0;
4264 if (GET_BE_WORD(format1->format) != 1)
4266 WARN("Unexpected extension table format %#x.\n", format1->format);
4267 return 0;
4270 *subtable_offset = *subtable_offset + GET_BE_DWORD(format1->extension_offset);
4272 return GET_BE_WORD(format1->lookup_type);
4275 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
4276 unsigned int subtable_offset);
4277 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
4278 unsigned int subtable_offset);
4280 static BOOL opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
4282 unsigned int i, lookup_type;
4283 BOOL ret = FALSE;
4285 for (i = 0; i < lookup->subtable_count; ++i)
4287 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
4289 lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset, lookup);
4291 switch (lookup_type)
4293 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
4294 ret = opentype_layout_apply_gpos_single_adjustment(context, lookup, subtable_offset);
4295 break;
4296 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
4297 ret = opentype_layout_apply_gpos_pair_adjustment(context, lookup, subtable_offset);
4298 break;
4299 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
4300 ret = opentype_layout_apply_gpos_cursive_attachment(context, lookup, subtable_offset);
4301 break;
4302 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
4303 ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, lookup, subtable_offset);
4304 break;
4305 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
4306 ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, lookup, subtable_offset);
4307 break;
4308 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
4309 ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, lookup, subtable_offset);
4310 break;
4311 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
4312 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
4313 break;
4314 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
4315 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
4316 break;
4317 case GPOS_LOOKUP_EXTENSION_POSITION:
4318 WARN("Recursive extension lookup.\n");
4319 break;
4320 default:
4321 WARN("Unknown lookup type %u.\n", lookup_type);
4324 if (ret)
4325 break;
4328 return ret;
4331 struct lookups
4333 struct lookup *lookups;
4334 size_t capacity;
4335 size_t count;
4338 static int lookups_sorting_compare(const void *a, const void *b)
4340 const struct lookup *left = (const struct lookup *)a;
4341 const struct lookup *right = (const struct lookup *)b;
4342 return left->index < right->index ? -1 : left->index > right->index ? 1 : 0;
4345 static BOOL opentype_layout_init_lookup(const struct ot_gsubgpos_table *table, unsigned short lookup_index, unsigned int mask,
4346 struct lookup *lookup)
4348 unsigned short subtable_count, lookup_type, flags, mark_filtering_set;
4349 const struct ot_lookup_table *lookup_table;
4350 unsigned int offset;
4352 if (!(offset = table_read_be_word(&table->table, table->lookup_list +
4353 FIELD_OFFSET(struct ot_lookup_list, lookup[lookup_index]))))
4355 return FALSE;
4358 offset += table->lookup_list;
4360 if (!(lookup_table = table_read_ensure(&table->table, offset, sizeof(*lookup_table))))
4361 return FALSE;
4363 if (!(subtable_count = GET_BE_WORD(lookup_table->subtable_count)))
4364 return FALSE;
4366 lookup_type = GET_BE_WORD(lookup_table->lookup_type);
4367 flags = GET_BE_WORD(lookup_table->flags);
4369 if (flags & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
4371 mark_filtering_set = table_read_be_word(&table->table, offset +
4372 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable_count]));
4373 flags |= mark_filtering_set << 16;
4376 lookup->index = lookup_index;
4377 lookup->type = lookup_type;
4378 lookup->flags = flags;
4379 lookup->subtable_count = subtable_count;
4380 lookup->mask = mask;
4381 lookup->offset = offset;
4383 return TRUE;
4386 static void opentype_layout_add_lookups(const struct ot_feature_list *feature_list, UINT16 total_lookup_count,
4387 const struct ot_gsubgpos_table *table, struct shaping_feature *feature, struct lookups *lookups)
4389 UINT16 feature_offset, lookup_count;
4390 unsigned int i;
4392 /* Feature wasn't found */
4393 if (feature->index == 0xffff)
4394 return;
4396 feature_offset = GET_BE_WORD(feature_list->features[feature->index].offset);
4398 lookup_count = table_read_be_word(&table->table, table->feature_list + feature_offset +
4399 FIELD_OFFSET(struct ot_feature, lookup_count));
4400 if (!lookup_count)
4401 return;
4403 if (!dwrite_array_reserve((void **)&lookups->lookups, &lookups->capacity, lookups->count + lookup_count,
4404 sizeof(*lookups->lookups)))
4406 return;
4409 for (i = 0; i < lookup_count; ++i)
4411 UINT16 lookup_index = table_read_be_word(&table->table, table->feature_list + feature_offset +
4412 FIELD_OFFSET(struct ot_feature, lookuplist_index[i]));
4414 if (lookup_index >= total_lookup_count)
4415 continue;
4417 if (opentype_layout_init_lookup(table, lookup_index, feature->mask, &lookups->lookups[lookups->count]))
4418 lookups->count++;
4422 static void opentype_layout_collect_lookups(struct scriptshaping_context *context, unsigned int script_index,
4423 unsigned int language_index, const struct shaping_features *features, const struct ot_gsubgpos_table *table,
4424 struct lookups *lookups)
4426 unsigned int last_num_lookups = 0, stage, script_feature_count = 0;
4427 UINT16 total_feature_count, total_lookup_count;
4428 struct shaping_feature required_feature = { 0 };
4429 const struct ot_feature_list *feature_list;
4430 const struct ot_langsys *langsys = NULL;
4431 struct shaping_feature *feature;
4432 unsigned int i, j, next_bit;
4433 unsigned int global_bit_shift = 1;
4434 unsigned int global_bit_mask = 2;
4435 UINT16 feature_index;
4437 if (!table->table.data)
4438 return;
4440 if (script_index != ~0u)
4442 unsigned int table_offset, langsys_offset;
4444 /* ScriptTable offset. */
4445 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
4446 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
4447 if (!table_offset)
4448 return;
4450 if (language_index == ~0u)
4451 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
4452 else
4453 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
4454 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
4455 FIELD_OFFSET(struct ot_langsys_record, langsys));
4456 langsys_offset += table->script_list + table_offset;
4458 script_feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
4459 if (script_feature_count)
4460 langsys = table_read_ensure(&table->table, langsys_offset,
4461 FIELD_OFFSET(struct ot_langsys, feature_index[script_feature_count]));
4462 if (!langsys)
4463 script_feature_count = 0;
4466 total_feature_count = table_read_be_word(&table->table, table->feature_list);
4467 if (!total_feature_count)
4468 return;
4470 total_lookup_count = table_read_be_word(&table->table, table->lookup_list);
4471 if (!total_lookup_count)
4472 return;
4474 feature_list = table_read_ensure(&table->table, table->feature_list,
4475 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
4476 if (!feature_list)
4477 return;
4479 /* Required feature. */
4480 required_feature.index = langsys ? GET_BE_WORD(langsys->required_feature_index) : 0xffff;
4481 if (required_feature.index < total_feature_count)
4482 required_feature.tag = feature_list->features[required_feature.index].tag;
4483 required_feature.mask = global_bit_mask;
4485 context->global_mask = global_bit_mask;
4486 next_bit = global_bit_shift + 1;
4487 for (i = 0; i < features->count; ++i)
4489 unsigned int bits_needed;
4490 BOOL found = FALSE;
4492 feature = &features->features[i];
4494 feature->index = 0xffff;
4496 if ((feature->flags & FEATURE_GLOBAL) && feature->max_value == 1)
4497 bits_needed = 0;
4498 else
4499 BitScanReverse(&bits_needed, min(feature->max_value, 256));
4501 if (!feature->max_value || next_bit + bits_needed > 8 * sizeof (feature->mask))
4502 continue;
4504 if (required_feature.tag == feature->tag)
4505 required_feature.stage = feature->stage;
4507 for (j = 0; j < script_feature_count; ++j)
4509 feature_index = GET_BE_WORD(langsys->feature_index[j]);
4510 if (feature_index >= total_feature_count)
4511 continue;
4512 if ((found = feature_list->features[feature_index].tag == feature->tag))
4514 feature->index = feature_index;
4515 break;
4519 if (!found && (features->features[i].flags & FEATURE_GLOBAL_SEARCH))
4521 for (j = 0; j < total_feature_count; ++j)
4523 if ((found = (feature_list->features[j].tag == feature->tag)))
4525 feature->index = j;
4526 break;
4531 if (!found)
4532 continue;
4534 if (feature->flags & FEATURE_GLOBAL && feature->max_value == 1)
4536 feature->shift = global_bit_shift;
4537 feature->mask = global_bit_mask;
4539 else
4541 feature->shift = next_bit;
4542 feature->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
4543 next_bit += bits_needed;
4544 context->global_mask |= (feature->default_value << feature->shift) & feature->mask;
4548 for (stage = 0; stage <= features->stage; ++stage)
4550 if (required_feature.index != 0xffff && required_feature.stage == stage)
4551 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &required_feature, lookups);
4553 for (i = 0; i < features->count; ++i)
4555 if (features->features[i].stage == stage)
4556 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &features->features[i], lookups);
4559 /* Sort and merge lookups for current stage. */
4560 if (last_num_lookups < lookups->count)
4562 qsort(lookups->lookups + last_num_lookups, lookups->count - last_num_lookups, sizeof(*lookups->lookups),
4563 lookups_sorting_compare);
4565 j = last_num_lookups;
4566 for (i = j + 1; i < lookups->count; ++i)
4568 if (lookups->lookups[i].index != lookups->lookups[j].index)
4570 lookups->lookups[++j] = lookups->lookups[i];
4572 else
4574 lookups->lookups[j].mask |= lookups->lookups[i].mask;
4577 lookups->count = j + 1;
4580 last_num_lookups = lookups->count;
4584 static int feature_search_compare(const void *a, const void* b)
4586 unsigned int tag = *(unsigned int *)a;
4587 const struct shaping_feature *feature = b;
4589 return tag < feature->tag ? -1 : tag > feature->tag ? 1 : 0;
4592 static unsigned int shaping_features_get_mask(const struct shaping_features *features, unsigned int tag, unsigned int *shift)
4594 struct shaping_feature *feature;
4596 feature = bsearch(&tag, features->features, features->count, sizeof(*features->features), feature_search_compare);
4598 if (!feature || feature->index == 0xffff)
4599 return 0;
4601 if (shift) *shift = feature->shift;
4602 return feature->mask;
4605 static void opentype_layout_get_glyph_range_for_text(struct scriptshaping_context *context, unsigned int start_char,
4606 unsigned int end_char, unsigned int *start_glyph, unsigned int *end_glyph)
4608 *start_glyph = context->u.buffer.clustermap[start_char];
4609 if (end_char >= context->length - 1)
4610 *end_glyph = context->glyph_count - 1;
4611 else
4612 *end_glyph = context->u.buffer.clustermap[end_char + 1] - 1;
4615 static void opentype_layout_set_glyph_masks(struct scriptshaping_context *context, const struct shaping_features *features)
4617 const DWRITE_TYPOGRAPHIC_FEATURES **user_features = context->user_features.features;
4618 unsigned int f, r, g, start_char, mask, shift, value;
4620 for (g = 0; g < context->glyph_count; ++g)
4621 context->glyph_infos[g].mask = context->global_mask;
4623 /* FIXME: set shaper masks */
4625 for (r = 0, start_char = 0; r < context->user_features.range_count; ++r)
4627 unsigned int start_glyph, end_glyph;
4629 if (start_char >= context->length)
4630 break;
4632 opentype_layout_get_glyph_range_for_text(context, start_char, start_char + context->user_features.range_lengths[r],
4633 &start_glyph, &end_glyph);
4634 start_char += context->user_features.range_lengths[r];
4636 if (start_glyph > end_glyph || end_glyph >= context->glyph_count)
4637 continue;
4639 for (f = 0; f < user_features[r]->featureCount; ++f)
4641 mask = shaping_features_get_mask(features, user_features[r]->features[f].nameTag, &shift);
4642 if (!mask)
4643 continue;
4645 value = (user_features[r]->features[f].parameter << shift) & mask;
4647 for (g = start_glyph; g <= end_glyph; ++g)
4648 context->glyph_infos[g].mask = (context->glyph_infos[g].mask & ~mask) | value;
4653 static void opentype_layout_apply_gpos_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
4655 struct lookup lookup = { 0 };
4656 /* Feature mask is intentionally zero, it's not used outside of main loop. */
4657 if (opentype_layout_init_lookup(context->table, lookup_index, 0, &lookup))
4658 opentype_layout_apply_gpos_lookup(context, &lookup);
4661 void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index,
4662 unsigned int language_index, const struct shaping_features *features)
4664 struct lookups lookups = { 0 };
4665 unsigned int i;
4666 BOOL ret;
4668 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
4669 context->u.buffer.apply_context_lookup = opentype_layout_apply_gpos_context_lookup;
4670 opentype_layout_collect_lookups(context, script_index, language_index, features, &context->cache->gpos, &lookups);
4672 for (i = 0; i < context->glyph_count; ++i)
4673 opentype_set_glyph_props(context, i);
4674 opentype_layout_set_glyph_masks(context, features);
4676 for (i = 0; i < lookups.count; ++i)
4678 const struct lookup *lookup = &lookups.lookups[i];
4680 context->cur = 0;
4681 context->lookup_mask = lookup->mask;
4683 while (context->cur < context->glyph_count)
4685 ret = FALSE;
4687 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
4688 lookup_is_glyph_match(context, context->cur, lookup->flags))
4690 ret = opentype_layout_apply_gpos_lookup(context, lookup);
4693 if (!ret)
4694 context->cur++;
4698 heap_free(lookups.lookups);
4701 static void opentype_layout_replace_glyph(struct scriptshaping_context *context, UINT16 glyph)
4703 UINT16 orig_glyph = context->u.subst.glyphs[context->cur];
4704 if (glyph != orig_glyph)
4706 context->u.subst.glyphs[context->cur] = glyph;
4707 opentype_set_subst_glyph_props(context, context->cur);
4711 static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4712 unsigned int subtable_offset)
4714 const struct dwrite_fonttable *table = &context->table->table;
4715 UINT16 format, coverage, orig_glyph, glyph;
4716 unsigned int coverage_index;
4718 orig_glyph = glyph = context->u.subst.glyphs[context->cur];
4720 format = table_read_be_word(table, subtable_offset);
4722 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, coverage));
4724 if (format == 1)
4726 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4727 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4729 glyph = orig_glyph + table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, delta));
4731 else if (format == 2)
4733 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4734 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4736 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count),
4737 coverage_index, &glyph))
4739 return FALSE;
4742 else
4744 WARN("Unknown single substitution format %u.\n", format);
4745 return FALSE;
4748 opentype_layout_replace_glyph(context, glyph);
4749 context->cur++;
4751 return TRUE;
4754 static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *context, unsigned int count)
4756 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
4757 struct shaping_glyph_info *glyph_infos;
4758 unsigned int new_capacity;
4759 UINT16 *glyphs;
4760 BOOL ret;
4762 if (context->u.subst.capacity >= count)
4763 return TRUE;
4765 new_capacity = context->u.subst.capacity * 2;
4767 if ((glyphs = heap_realloc(context->u.subst.glyphs, new_capacity * sizeof(*glyphs))))
4768 context->u.subst.glyphs = glyphs;
4769 if ((glyph_props = heap_realloc(context->u.subst.glyph_props, new_capacity * sizeof(*glyph_props))))
4770 context->u.subst.glyph_props = glyph_props;
4771 if ((glyph_infos = heap_realloc(context->glyph_infos, new_capacity * sizeof(*glyph_infos))))
4772 context->glyph_infos = glyph_infos;
4774 if ((ret = (glyphs && glyph_props && glyph_infos)))
4775 context->u.subst.capacity = new_capacity;
4777 return ret;
4780 static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4781 unsigned int subtable_offset)
4783 const struct dwrite_fonttable *table = &context->table->table;
4784 UINT16 format, coverage, glyph, glyph_count;
4785 unsigned int i, idx, coverage_index;
4786 const UINT16 *glyphs;
4788 idx = context->cur;
4789 glyph = context->u.subst.glyphs[idx];
4791 format = table_read_be_word(table, subtable_offset);
4793 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, coverage));
4795 if (format == 1)
4797 UINT16 seq_offset;
4799 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4800 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4802 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, seq_count),
4803 coverage_index, &seq_offset))
4805 return FALSE;
4808 if (!(glyphs = table_read_array_be_word(table, subtable_offset + seq_offset, ~0u, &glyph_count))) return FALSE;
4810 if (glyph_count == 1)
4812 /* Equivalent of single substitution. */
4813 opentype_layout_replace_glyph(context, GET_BE_WORD(glyphs[0]));
4814 context->cur++;
4816 else if (glyph_count == 0)
4818 context->cur++;
4820 else
4822 unsigned int shift_len, src_idx, dest_idx, mask;
4824 /* Current glyph is also replaced. */
4825 glyph_count--;
4827 if (!(opentype_layout_gsub_ensure_buffer(context, context->glyph_count + glyph_count)))
4828 return FALSE;
4830 shift_len = context->cur + 1 < context->glyph_count ? context->glyph_count - context->cur - 1 : 0;
4832 if (shift_len)
4834 src_idx = context->cur + 1;
4835 dest_idx = src_idx + glyph_count;
4837 memmove(&context->u.subst.glyphs[dest_idx], &context->u.subst.glyphs[src_idx],
4838 shift_len * sizeof(*context->u.subst.glyphs));
4839 memmove(&context->u.subst.glyph_props[dest_idx], &context->u.subst.glyph_props[src_idx],
4840 shift_len * sizeof(*context->u.subst.glyph_props));
4841 memmove(&context->glyph_infos[dest_idx], &context->glyph_infos[src_idx],
4842 shift_len * sizeof(*context->glyph_infos));
4845 mask = context->glyph_infos[context->cur].mask;
4846 for (i = 0, idx = context->cur; i <= glyph_count; ++i)
4848 glyph = GET_BE_WORD(glyphs[i]);
4849 context->u.subst.glyphs[idx + i] = glyph;
4850 if (i)
4852 context->u.subst.glyph_props[idx + i].isClusterStart = 0;
4853 context->u.buffer.glyph_props[idx + i].components = 0;
4854 context->glyph_infos[idx + i].start_text_idx = 0;
4856 opentype_set_subst_glyph_props(context, idx + i);
4857 /* Inherit feature mask from original matched glyph. */
4858 context->glyph_infos[idx + i].mask = mask;
4861 context->cur += glyph_count + 1;
4862 context->glyph_count += glyph_count;
4865 else
4867 WARN("Unknown multiple substitution format %u.\n", format);
4868 return FALSE;
4871 return TRUE;
4874 static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4875 unsigned int subtable_offset)
4877 const struct dwrite_fonttable *table = &context->table->table;
4878 unsigned int idx, coverage_index;
4879 UINT16 format, coverage, glyph;
4881 idx = context->cur;
4882 glyph = context->u.subst.glyphs[idx];
4884 format = table_read_be_word(table, subtable_offset);
4886 if (format == 1)
4888 const struct ot_gsub_altsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
4889 unsigned int shift, alt_index;
4890 UINT16 set_offset;
4892 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, coverage));
4894 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4895 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4897 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, count),
4898 coverage_index, &set_offset))
4899 return FALSE;
4901 /* Argument is 1-based. */
4902 BitScanForward(&shift, context->lookup_mask);
4903 alt_index = (context->lookup_mask & context->glyph_infos[idx].mask) >> shift;
4904 if (!alt_index) return FALSE;
4906 if (!table_read_array_be_word(table, subtable_offset + set_offset, alt_index - 1, &glyph)) return FALSE;
4908 else
4910 WARN("Unexpected alternate substitution format %d.\n", format);
4911 return FALSE;
4914 opentype_layout_replace_glyph(context, glyph);
4915 context->cur++;
4917 return TRUE;
4920 static BOOL opentype_layout_context_match_input(const struct match_context *mc, unsigned int count, const UINT16 *input,
4921 unsigned int *end_offset, unsigned int *match_positions)
4923 struct match_data match_data = { .mc = mc, .subtable_offset = mc->input_offset };
4924 struct scriptshaping_context *context = mc->context;
4925 struct glyph_iterator iter;
4926 unsigned int i;
4928 if (count > GLYPH_CONTEXT_MAX_LENGTH)
4929 return FALSE;
4931 match_positions[0] = context->cur;
4933 glyph_iterator_init(context, mc->lookup->flags, context->cur, count - 1, &iter);
4934 iter.mask = context->lookup_mask;
4935 iter.match_func = mc->match_func;
4936 iter.match_data = &match_data;
4937 iter.glyph_data = input;
4939 for (i = 1; i < count; ++i)
4941 if (!glyph_iterator_next(&iter))
4942 return FALSE;
4944 match_positions[i] = iter.pos;
4947 *end_offset = iter.pos - context->cur + 1;
4949 return TRUE;
4952 static void opentype_layout_unsafe_to_break(struct scriptshaping_context *context, unsigned int idx)
4954 if (context->u.buffer.glyph_props[idx].isClusterStart)
4955 context->u.buffer.text_props[context->glyph_infos[idx].start_text_idx].canBreakShapingAfter = 0;
4958 static void opentype_layout_delete_glyph(struct scriptshaping_context *context, unsigned int idx)
4960 unsigned int shift_len;
4962 shift_len = context->glyph_count - context->cur - 1;
4964 if (shift_len)
4966 memmove(&context->u.buffer.glyphs[idx], &context->u.buffer.glyphs[idx + 1],
4967 shift_len * sizeof(*context->u.buffer.glyphs));
4968 memmove(&context->u.buffer.glyph_props[idx], &context->u.buffer.glyph_props[idx + 1],
4969 shift_len * sizeof(*context->u.buffer.glyph_props));
4970 memmove(&context->glyph_infos[idx], &context->glyph_infos[idx + 1], shift_len * sizeof(*context->glyph_infos));
4973 context->glyph_count--;
4976 static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset,
4977 const struct lookup *lookup)
4979 struct match_context mc = { .context = context, .lookup = lookup, .match_func = opentype_match_glyph_func };
4980 const struct dwrite_fonttable *gsub = &context->table->table;
4981 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
4982 unsigned int i, j, comp_count, match_length = 0;
4983 const struct ot_gsub_lig *lig;
4984 UINT16 lig_glyph;
4986 comp_count = table_read_be_word(gsub, offset + FIELD_OFFSET(struct ot_gsub_lig, comp_count));
4988 if (!comp_count)
4989 return FALSE;
4991 lig = table_read_ensure(gsub, offset, FIELD_OFFSET(struct ot_gsub_lig, components[comp_count-1]));
4992 if (!lig)
4993 return FALSE;
4995 lig_glyph = GET_BE_WORD(lig->lig_glyph);
4997 if (comp_count == 1)
4999 opentype_layout_replace_glyph(context, lig_glyph);
5000 context->cur++;
5001 return TRUE;
5004 if (!opentype_layout_context_match_input(&mc, comp_count, lig->components, &match_length, match_positions))
5005 return FALSE;
5007 opentype_layout_replace_glyph(context, lig_glyph);
5008 context->u.buffer.glyph_props[context->cur].components = comp_count;
5010 /* Positioning against a ligature implies keeping track of ligature component
5011 glyph should be attached to. Update per-glyph property for interleaving glyphs,
5012 0 means attaching to last component, n - attaching to n-th glyph before last. */
5013 for (i = 1; i < comp_count; ++i)
5015 j = match_positions[i - 1] + 1;
5016 while (j < match_positions[i])
5018 context->u.buffer.glyph_props[j++].lig_component = comp_count - i;
5020 opentype_layout_unsafe_to_break(context, i);
5021 context->u.buffer.glyph_props[i].isClusterStart = 0;
5022 context->glyph_infos[i].start_text_idx = 0;
5025 /* Delete ligated glyphs, backwards to preserve index. */
5026 for (i = 1; i < comp_count; ++i)
5028 opentype_layout_delete_glyph(context, match_positions[comp_count - i]);
5031 /* Skip whole matched sequence, accounting for deleted glyphs. */
5032 context->cur += match_length - (comp_count - 1);
5034 return TRUE;
5037 static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5038 unsigned int subtable_offset)
5040 const struct dwrite_fonttable *table = &context->table->table;
5041 UINT16 format, coverage, glyph, lig_set_offset;
5042 unsigned int coverage_index;
5044 glyph = context->u.subst.glyphs[context->cur];
5046 format = table_read_be_word(table, subtable_offset);
5048 if (format == 1)
5050 const struct ot_gsub_ligsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
5051 unsigned int i;
5052 const UINT16 *offsets;
5053 UINT16 lig_count;
5055 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, coverage));
5057 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5058 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5060 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_set_count),
5061 coverage_index, &lig_set_offset))
5062 return FALSE;
5064 if (!(offsets = table_read_array_be_word(table, subtable_offset + lig_set_offset, ~0u, &lig_count)))
5065 return FALSE;
5067 /* First applicable ligature is used. */
5068 for (i = 0; i < lig_count; ++i)
5070 if (opentype_layout_apply_ligature(context, subtable_offset + lig_set_offset + GET_BE_WORD(offsets[i]), lookup))
5071 return TRUE;
5074 else
5075 WARN("Unexpected ligature substitution format %d.\n", format);
5077 return FALSE;
5080 static BOOL opentype_layout_context_match_backtrack(const struct match_context *mc, unsigned int count,
5081 const UINT16 *backtrack, unsigned int *match_start)
5083 struct match_data match_data = { .mc = mc, .subtable_offset = mc->backtrack_offset };
5084 struct scriptshaping_context *context = mc->context;
5085 struct glyph_iterator iter;
5086 unsigned int i;
5088 glyph_iterator_init(context, mc->lookup->flags, context->cur, count, &iter);
5089 iter.match_func = mc->match_func;
5090 iter.match_data = &match_data;
5091 iter.glyph_data = backtrack;
5093 for (i = 0; i < count; ++i)
5095 if (!glyph_iterator_prev(&iter))
5096 return FALSE;
5099 *match_start = iter.pos;
5101 return TRUE;
5104 static BOOL opentype_layout_context_match_lookahead(const struct match_context *mc, unsigned int count,
5105 const UINT16 *lookahead, unsigned int offset, unsigned int *end_index)
5107 struct match_data match_data = { .mc = mc, .subtable_offset = mc->lookahead_offset };
5108 struct scriptshaping_context *context = mc->context;
5109 struct glyph_iterator iter;
5110 unsigned int i;
5112 glyph_iterator_init(context, mc->lookup->flags, context->cur + offset - 1, count, &iter);
5113 iter.match_func = mc->match_func;
5114 iter.match_data = &match_data;
5115 iter.glyph_data = lookahead;
5117 for (i = 0; i < count; ++i)
5119 if (!glyph_iterator_next(&iter))
5120 return FALSE;
5123 *end_index = iter.pos;
5125 return TRUE;
5128 static BOOL opentype_layout_context_apply_lookup(struct scriptshaping_context *context, unsigned int count,
5129 unsigned int *match_positions, unsigned int lookup_count, const UINT16 *lookup_records, unsigned int match_length)
5131 unsigned int i, j;
5132 int end, delta;
5134 if (!context->nesting_level_left)
5135 return TRUE;
5137 end = context->cur + match_length;
5139 for (i = 0; i < lookup_count; ++i)
5141 unsigned int idx = GET_BE_WORD(lookup_records[i]);
5142 unsigned int orig_len, lookup_index, next;
5144 if (idx >= count)
5145 continue;
5147 context->cur = match_positions[idx];
5149 orig_len = context->glyph_count;
5151 lookup_index = GET_BE_WORD(lookup_records[i+1]);
5153 --context->nesting_level_left;
5154 context->u.buffer.apply_context_lookup(context, lookup_index);
5155 ++context->nesting_level_left;
5157 delta = context->glyph_count - orig_len;
5158 if (!delta)
5159 continue;
5161 end += delta;
5162 if (end <= (int)match_positions[idx])
5164 end = match_positions[idx];
5165 break;
5168 next = idx + 1;
5170 if (delta > 0)
5172 if (delta + count > GLYPH_CONTEXT_MAX_LENGTH)
5173 break;
5175 else
5177 delta = max(delta, (int)next - (int)count);
5178 next -= delta;
5181 memmove(match_positions + next + delta, match_positions + next,
5182 (count - next) * sizeof (*match_positions));
5183 next += delta;
5184 count += delta;
5186 for (j = idx + 1; j < next; j++)
5187 match_positions[j] = match_positions[j - 1] + 1;
5189 for (; next < count; next++)
5190 match_positions[next] += delta;
5193 context->cur = end;
5195 return TRUE;
5198 static BOOL opentype_layout_apply_chain_context_match(unsigned int backtrack_count, const UINT16 *backtrack,
5199 unsigned int input_count, const UINT16 *input, unsigned int lookahead_count, const UINT16 *lookahead,
5200 unsigned int lookup_count, const UINT16 *lookup_records, const struct match_context *mc)
5202 unsigned int start_index = 0, match_length = 0, end_index = 0;
5203 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5205 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5206 opentype_layout_context_match_backtrack(mc, backtrack_count, backtrack, &start_index) &&
5207 opentype_layout_context_match_lookahead(mc, lookahead_count, lookahead, input_count, &end_index) &&
5208 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count, lookup_records, match_length);
5211 static BOOL opentype_layout_apply_chain_rule_set(const struct match_context *mc, unsigned int offset)
5213 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5214 const struct dwrite_fonttable *table = &mc->context->table->table;
5215 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5216 const struct ot_gsubgpos_ruleset *ruleset;
5217 unsigned int i, count;
5219 count = table_read_be_word(table, offset);
5220 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5222 for (i = 0; i < count; ++i)
5224 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5226 backtrack_count = table_read_be_word(table, rule_offset);
5227 rule_offset += 2;
5228 backtrack = table_read_ensure(table, rule_offset, backtrack_count * sizeof(*backtrack));
5229 rule_offset += backtrack_count * sizeof(*backtrack);
5231 if (!(input_count = table_read_be_word(table, rule_offset)))
5232 continue;
5234 rule_offset += 2;
5235 input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input));
5236 rule_offset += (input_count - 1) * sizeof(*input);
5238 lookahead_count = table_read_be_word(table, rule_offset);
5239 rule_offset += 2;
5240 lookahead = table_read_ensure(table, rule_offset, lookahead_count * sizeof(*lookahead));
5241 rule_offset += lookahead_count * sizeof(*lookahead);
5243 lookup_count = table_read_be_word(table, rule_offset);
5244 rule_offset += 2;
5245 lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records));
5247 /* First applicable rule is used. */
5248 if (opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input, lookahead_count,
5249 lookahead, lookup_count, lookup_records, mc))
5251 return TRUE;
5255 return FALSE;
5258 static BOOL opentype_layout_apply_context_match(unsigned int input_count, const UINT16 *input, unsigned int lookup_count,
5259 const UINT16 *lookup_records, const struct match_context *mc)
5261 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5262 unsigned int match_length = 0;
5264 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5265 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count,
5266 lookup_records, match_length);
5269 static BOOL opentype_layout_apply_rule_set(const struct match_context *mc, unsigned int offset)
5271 unsigned int input_count, lookup_count;
5272 const struct dwrite_fonttable *table = &mc->context->table->table;
5273 const UINT16 *input, *lookup_records;
5274 const struct ot_gsubgpos_ruleset *ruleset;
5275 unsigned int i, count;
5277 count = table_read_be_word(table, offset);
5278 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5280 for (i = 0; i < count; ++i)
5282 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5284 if (!(input_count = table_read_be_word(table, rule_offset)))
5285 continue;
5286 rule_offset += 2;
5288 if (!(lookup_count = table_read_be_word(table, rule_offset)))
5289 continue;
5290 rule_offset += 2;
5292 if (!(input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input))))
5293 continue;
5294 rule_offset += (input_count - 1) * sizeof(*input);
5296 if (!(lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records))))
5297 continue;
5299 /* First applicable rule is used. */
5300 if (opentype_layout_apply_context_match(input_count, input, lookup_count, lookup_records, mc))
5301 return TRUE;
5304 return FALSE;
5307 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
5308 unsigned int subtable_offset)
5310 struct match_context mc = { .context = context, .lookup = lookup };
5311 const struct dwrite_fonttable *table = &context->table->table;
5312 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5313 UINT16 glyph, format, coverage;
5314 BOOL ret = FALSE;
5316 glyph = context->u.subst.glyphs[context->cur];
5318 format = table_read_be_word(table, subtable_offset);
5320 if (format == 1)
5322 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5324 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5325 if (coverage_index == GLYPH_NOT_COVERED)
5326 return FALSE;
5328 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5329 if (coverage_index >= count)
5330 return FALSE;
5332 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5333 rulesets[coverage_index]));
5334 offset += subtable_offset;
5336 mc.match_func = opentype_match_glyph_func;
5338 ret = opentype_layout_apply_rule_set(&mc, offset);
5340 else if (format == 2)
5342 unsigned int input_classdef, rule_set_idx;
5344 offset = subtable_offset + 2 /* format */;
5346 coverage = table_read_be_word(table, offset);
5347 offset += 2;
5349 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5350 if (coverage_index == GLYPH_NOT_COVERED)
5351 return FALSE;
5353 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5354 offset += 2;
5356 count = table_read_be_word(table, offset);
5357 offset+= 2;
5359 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5360 if (rule_set_idx >= count)
5361 return FALSE;
5363 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5364 offset += subtable_offset;
5366 mc.input_offset = input_classdef;
5367 mc.match_func = opentype_match_class_func;
5369 ret = opentype_layout_apply_rule_set(&mc, offset);
5371 else if (format == 3)
5373 unsigned int input_count, lookup_count;
5374 const UINT16 *input, *lookup_records;
5376 offset = subtable_offset + 2 /* format */;
5378 input_count = table_read_be_word(table, offset);
5379 offset += 2;
5381 if (!input_count)
5382 return FALSE;
5384 lookup_count = table_read_be_word(table, offset);
5385 offset += 2;
5387 if (!(input = table_read_ensure(table, offset, sizeof(*input) * input_count)))
5388 return FALSE;
5389 offset += sizeof(*input) * input_count;
5391 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5392 if (coverage_index == GLYPH_NOT_COVERED)
5393 return FALSE;
5395 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5397 mc.input_offset = subtable_offset;
5398 mc.match_func = opentype_match_coverage_func;
5400 ret = opentype_layout_apply_context_match(input_count, input + 1, lookup_count, lookup_records, &mc);
5402 else
5403 WARN("Unknown contextual substitution format %u.\n", format);
5405 return ret;
5408 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
5409 unsigned int subtable_offset)
5411 struct match_context mc = { .context = context, .lookup = lookup };
5412 const struct dwrite_fonttable *table = &context->table->table;
5413 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5414 UINT16 glyph, format, coverage;
5415 BOOL ret = FALSE;
5417 glyph = context->u.subst.glyphs[context->cur];
5419 format = table_read_be_word(table, subtable_offset);
5421 if (format == 1)
5423 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5425 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5426 if (coverage_index == GLYPH_NOT_COVERED)
5427 return FALSE;
5429 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5430 if (coverage_index >= count)
5431 return FALSE;
5433 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5434 rulesets[coverage_index]));
5435 offset += subtable_offset;
5437 mc.match_func = opentype_match_glyph_func;
5439 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5441 else if (format == 2)
5443 unsigned int backtrack_classdef, input_classdef, lookahead_classdef, rule_set_idx;
5445 offset = subtable_offset + 2 /* format */;
5447 coverage = table_read_be_word(table, offset);
5448 offset += 2;
5450 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5451 if (coverage_index == GLYPH_NOT_COVERED)
5452 return FALSE;
5454 backtrack_classdef = table_read_be_word(table, offset) + subtable_offset;
5455 offset += 2;
5457 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5458 offset += 2;
5460 lookahead_classdef = table_read_be_word(table, offset) + subtable_offset;
5461 offset += 2;
5463 count = table_read_be_word(table, offset);
5464 offset+= 2;
5466 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5467 if (rule_set_idx >= count)
5468 return FALSE;
5470 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5471 offset += subtable_offset;
5473 mc.backtrack_offset = backtrack_classdef;
5474 mc.input_offset = input_classdef;
5475 mc.lookahead_offset = lookahead_classdef;
5476 mc.match_func = opentype_match_class_func;
5478 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5480 else if (format == 3)
5482 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5483 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5485 offset = subtable_offset + 2 /* format */;
5487 backtrack_count = table_read_be_word(table, offset);
5488 offset += 2;
5489 backtrack = table_read_ensure(table, offset, backtrack_count * sizeof(*backtrack));
5490 offset += backtrack_count * sizeof(*backtrack);
5492 input_count = table_read_be_word(table, offset);
5493 offset += 2;
5494 input = table_read_ensure(table, offset, input_count * sizeof(*input));
5495 offset += input_count * sizeof(*input);
5497 lookahead_count = table_read_be_word(table, offset);
5498 offset += 2;
5499 lookahead = table_read_ensure(table, offset, lookahead_count * sizeof(*lookahead));
5500 offset += lookahead_count * sizeof(*lookahead);
5502 lookup_count = table_read_be_word(table, offset);
5503 offset += 2;
5504 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5506 if (input)
5507 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5509 if (coverage_index == GLYPH_NOT_COVERED)
5510 return FALSE;
5512 mc.backtrack_offset = subtable_offset;
5513 mc.input_offset = subtable_offset;
5514 mc.lookahead_offset = subtable_offset;
5515 mc.match_func = opentype_match_coverage_func;
5517 ret = opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input + 1, lookahead_count,
5518 lookahead, lookup_count, lookup_records, &mc);
5520 else
5521 WARN("Unknown chaining contextual substitution format %u.\n", format);
5523 return ret;
5526 static BOOL opentype_layout_apply_gsub_reverse_chain_context_substitution(struct scriptshaping_context *context,
5527 const struct lookup *lookup, unsigned int subtable_offset)
5529 const struct dwrite_fonttable *table = &context->table->table;
5530 unsigned int offset = subtable_offset;
5531 UINT16 glyph, format;
5533 if (context->nesting_level_left != SHAPE_MAX_NESTING_LEVEL)
5534 return FALSE;
5536 glyph = context->u.subst.glyphs[context->cur];
5538 format = table_read_be_word(table, offset);
5539 offset += 2;
5541 if (format == 1)
5543 struct match_context mc = { .context = context, .lookup = lookup };
5544 unsigned int start_index = 0, end_index = 0, backtrack_count, lookahead_count;
5545 unsigned int coverage, coverage_index;
5546 const UINT16 *backtrack, *lookahead;
5548 coverage = table_read_be_word(table, offset);
5549 offset += 2;
5551 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5552 if (coverage_index == GLYPH_NOT_COVERED)
5553 return FALSE;
5555 backtrack_count = table_read_be_word(table, offset);
5556 offset += 2;
5558 backtrack = table_read_ensure(table, offset, sizeof(*backtrack) * backtrack_count);
5559 offset += sizeof(*backtrack) * backtrack_count;
5561 lookahead_count = table_read_be_word(table, offset);
5562 offset += 2;
5564 lookahead = table_read_ensure(table, offset, sizeof(*lookahead) * lookahead_count);
5565 offset += sizeof(*lookahead) * lookahead_count;
5567 mc.match_func = opentype_match_coverage_func;
5568 mc.backtrack_offset = subtable_offset;
5569 mc.lookahead_offset = subtable_offset;
5571 if (opentype_layout_context_match_backtrack(&mc, backtrack_count, backtrack, &start_index) &&
5572 opentype_layout_context_match_lookahead(&mc, lookahead_count, lookahead, 1, &end_index))
5574 unsigned int glyph_count = table_read_be_word(table, offset);
5575 if (coverage_index >= glyph_count)
5576 return FALSE;
5577 offset += 2;
5579 glyph = table_read_be_word(table, offset + coverage_index * sizeof(glyph));
5580 opentype_layout_replace_glyph(context, glyph);
5582 return TRUE;
5585 else
5586 WARN("Unknown reverse chaining contextual substitution format %u.\n", format);
5588 return FALSE;
5591 static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
5593 unsigned int i, lookup_type;
5594 BOOL ret = FALSE;
5596 for (i = 0; i < lookup->subtable_count; ++i)
5598 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
5600 lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset, lookup);
5602 switch (lookup_type)
5604 case GSUB_LOOKUP_SINGLE_SUBST:
5605 ret = opentype_layout_apply_gsub_single_substitution(context, lookup, subtable_offset);
5606 break;
5607 case GSUB_LOOKUP_MULTIPLE_SUBST:
5608 ret = opentype_layout_apply_gsub_mult_substitution(context, lookup, subtable_offset);
5609 break;
5610 case GSUB_LOOKUP_ALTERNATE_SUBST:
5611 ret = opentype_layout_apply_gsub_alt_substitution(context, lookup, subtable_offset);
5612 break;
5613 case GSUB_LOOKUP_LIGATURE_SUBST:
5614 ret = opentype_layout_apply_gsub_lig_substitution(context, lookup, subtable_offset);
5615 break;
5616 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
5617 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
5618 break;
5619 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
5620 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
5621 break;
5622 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
5623 ret = opentype_layout_apply_gsub_reverse_chain_context_substitution(context, lookup, subtable_offset);
5624 break;
5625 case GSUB_LOOKUP_EXTENSION_SUBST:
5626 WARN("Invalid lookup type for extension substitution %#x.\n", lookup_type);
5627 break;
5628 default:
5629 WARN("Unknown lookup type %u.\n", lookup_type);
5632 if (ret)
5633 break;
5636 return ret;
5639 static unsigned int unicode_get_mirrored_char(unsigned int codepoint)
5641 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
5642 WCHAR mirror;
5643 /* TODO: check if mirroring for higher planes makes sense at all */
5644 if (codepoint > 0xffff) return codepoint;
5645 mirror = get_table_entry(wine_mirror_map, codepoint);
5646 return mirror ? mirror : codepoint;
5650 * 034F # Mn COMBINING GRAPHEME JOINER
5651 * 061C # Cf ARABIC LETTER MARK
5652 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5653 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5654 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5655 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5657 static unsigned int opentype_is_default_ignorable(unsigned int codepoint)
5659 return codepoint == 0x34f || codepoint == 0x61c || codepoint == 0xfeff ||
5660 (codepoint >= 0x180b && codepoint <= 0x180e) || (codepoint >= 0x200b && codepoint <= 0x200f);
5663 static unsigned int opentype_is_diacritic(unsigned int codepoint)
5665 WCHAR ch = codepoint;
5666 WORD type = 0;
5667 /* Ignore higher planes for now. */
5668 if (codepoint > 0xffff) return 0;
5669 GetStringTypeW(CT_CTYPE3, &ch, 1, &type);
5670 return !!(type & C3_DIACRITIC);
5673 static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, const struct shaping_features *features)
5675 unsigned int rtlm_mask = shaping_features_get_mask(features, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), NULL);
5676 const struct shaping_font_ops *font = context->cache->font;
5677 unsigned int i, g, c, codepoint, cluster_start_idx = 0;
5678 UINT16 *clustermap = context->u.subst.clustermap;
5679 const WCHAR *text = context->text;
5680 BOOL bmp;
5682 memset(context->u.subst.glyph_props, 0, context->u.subst.max_glyph_count * sizeof(*context->u.subst.glyph_props));
5683 memset(context->u.buffer.text_props, 0, context->length * sizeof(*context->u.buffer.text_props));
5685 for (i = 0; i < context->length; ++i)
5687 g = context->glyph_count;
5689 if ((bmp = !(IS_HIGH_SURROGATE(text[i]) && (i < context->length - 1) && IS_LOW_SURROGATE(text[i + 1]))))
5691 codepoint = text[i];
5693 else
5695 codepoint = 0x10000 + ((text[i] - 0xd800) << 10) + (text[i + 1] - 0xdc00);
5698 if (context->is_rtl)
5700 c = unicode_get_mirrored_char(codepoint);
5701 if (c != codepoint && font->has_glyph(context->cache->context, c))
5702 codepoint = c;
5703 else
5704 context->glyph_infos[i].mask |= rtlm_mask;
5707 /* TODO: should this check for glyph availability? */
5708 if (*context->u.subst.digits && codepoint >= '0' && codepoint <= '9')
5709 codepoint = context->u.subst.digits[codepoint - '0'];
5711 context->u.buffer.glyphs[g] = font->get_glyph(context->cache->context, codepoint);
5712 context->u.buffer.glyph_props[g].justification = SCRIPT_JUSTIFY_CHARACTER;
5713 opentype_set_subst_glyph_props(context, g);
5715 /* Group diacritics with preceding base. Glyph class is ignored here. */
5716 if (!g || !opentype_is_diacritic(codepoint))
5718 context->u.buffer.glyph_props[g].isClusterStart = 1;
5719 context->glyph_infos[g].start_text_idx = i;
5720 cluster_start_idx = g;
5723 if (opentype_is_default_ignorable(codepoint))
5724 context->u.buffer.glyph_props[g].isZeroWidthSpace = 1;
5725 context->u.buffer.glyph_props[g].components = 1;
5726 context->glyph_count++;
5728 /* Set initial cluster map here, it's used for setting user features masks. */
5729 clustermap[i] = cluster_start_idx;
5730 if (bmp)
5731 context->u.buffer.text_props[i].canBreakShapingAfter = 1;
5732 else
5734 clustermap[i + 1] = cluster_start_idx;
5735 context->u.buffer.text_props[i + 1].canBreakShapingAfter = 1;
5736 ++i;
5741 static BOOL opentype_is_gsub_lookup_reversed(const struct scriptshaping_context *context, const struct lookup *lookup)
5743 unsigned int subtable_offset, lookup_type = lookup->type;
5745 if (lookup->type == GSUB_LOOKUP_EXTENSION_SUBST)
5747 subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, 0);
5748 /* Assumes format 1. */
5749 lookup_type = table_read_be_word(&context->table->table, subtable_offset + 2);
5751 return lookup_type == GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST;
5754 static void opentype_layout_apply_gsub_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
5756 struct lookup lookup = { 0 };
5757 /* Feature mask is intentionally zero, it's not used outside of main loop. */
5758 if (opentype_layout_init_lookup(context->table, lookup_index, 0, &lookup))
5759 opentype_layout_apply_gsub_lookup(context, &lookup);
5762 void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int script_index,
5763 unsigned int language_index, const struct shaping_features *features)
5765 struct lookups lookups = { 0 };
5766 unsigned int i, j, start_idx;
5767 BOOL ret;
5769 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
5770 context->u.buffer.apply_context_lookup = opentype_layout_apply_gsub_context_lookup;
5771 opentype_layout_collect_lookups(context, script_index, language_index, features, context->table, &lookups);
5773 opentype_get_nominal_glyphs(context, features);
5774 opentype_layout_set_glyph_masks(context, features);
5776 for (i = 0; i < lookups.count; ++i)
5778 const struct lookup *lookup = &lookups.lookups[i];
5780 context->lookup_mask = lookup->mask;
5782 if (!opentype_is_gsub_lookup_reversed(context, lookup))
5784 context->cur = 0;
5785 while (context->cur < context->glyph_count)
5787 ret = FALSE;
5789 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
5790 lookup_is_glyph_match(context, context->cur, lookup->flags))
5792 ret = opentype_layout_apply_gsub_lookup(context, lookup);
5795 if (!ret)
5796 context->cur++;
5799 else
5801 context->cur = context->glyph_count - 1;
5803 for (;;)
5805 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
5806 lookup_is_glyph_match(context, context->cur, lookup->flags))
5808 opentype_layout_apply_gsub_lookup(context, lookup);
5811 if (context->cur == 0) break;
5812 --context->cur;
5817 /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding
5818 text span to start_idx. */
5819 start_idx = 0;
5820 for (i = 1; i < context->glyph_count; ++i)
5822 if (context->u.buffer.glyph_props[i].isClusterStart)
5824 unsigned int start_text, end_text;
5826 start_text = context->glyph_infos[start_idx].start_text_idx;
5827 end_text = context->glyph_infos[i].start_text_idx;
5829 for (j = start_text; j < end_text; ++j)
5830 context->u.buffer.clustermap[j] = start_idx;
5832 start_idx = i;
5836 /* Fill the tail. */
5837 for (j = context->glyph_infos[start_idx].start_text_idx; j < context->length; ++j)
5838 context->u.buffer.clustermap[j] = start_idx;
5840 heap_free(lookups.lookups);
5843 static BOOL opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5844 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
5846 const struct dwrite_fonttable *table = &context->table->table;
5847 const UINT16 *offsets;
5848 unsigned int count;
5850 if (format == 1 || format == 2)
5852 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5853 return TRUE;
5855 else if (format == 3)
5857 count = table_read_be_word(table, subtable_offset + 2);
5858 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6, count * sizeof(*offsets))))
5859 return FALSE;
5861 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
5862 return TRUE;
5865 return FALSE;
5868 static BOOL opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5869 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
5871 const struct dwrite_fonttable *table = &context->table->table;
5872 unsigned int count, backtrack_count;
5873 const UINT16 *offsets;
5875 if (format == 1 || format == 2)
5877 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5878 return TRUE;
5880 else if (format == 3)
5882 backtrack_count = table_read_be_word(table, subtable_offset + 2);
5884 count = table_read_be_word(table, subtable_offset + 4 + backtrack_count * sizeof(*offsets));
5886 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6 + backtrack_count * sizeof(*offsets),
5887 count * sizeof(*offsets))))
5888 return FALSE;
5890 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
5891 return TRUE;
5894 return FALSE;
5897 static BOOL opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5898 const struct lookup *lookup)
5900 const struct dwrite_fonttable *gsub = &context->table->table;
5901 static const unsigned short gsub_formats[] =
5903 0, /* Unused */
5904 1, /* SingleSubst */
5905 1, /* MultipleSubst */
5906 1, /* AlternateSubst */
5907 1, /* LigatureSubst */
5908 3, /* ContextSubst */
5909 3, /* ChainContextSubst */
5910 0, /* Extension, unused */
5911 1, /* ReverseChainSubst */
5913 unsigned int i, coverage, lookup_type, format;
5915 for (i = 0; i < lookup->subtable_count; ++i)
5917 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
5919 lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset, lookup);
5921 format = table_read_be_word(gsub, subtable_offset);
5923 if (!format || format > ARRAY_SIZE(gsub_formats) || format > gsub_formats[lookup_type])
5924 break;
5926 coverage = table_read_be_word(gsub, subtable_offset + 2);
5928 switch (lookup_type)
5930 case GSUB_LOOKUP_SINGLE_SUBST:
5931 case GSUB_LOOKUP_MULTIPLE_SUBST:
5932 case GSUB_LOOKUP_ALTERNATE_SUBST:
5933 case GSUB_LOOKUP_LIGATURE_SUBST:
5934 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
5936 if (opentype_layout_is_glyph_covered(gsub, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5937 return TRUE;
5939 break;
5941 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
5943 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
5944 return TRUE;
5946 break;
5948 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
5950 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
5951 return TRUE;
5953 break;
5955 default:
5956 WARN("Unknown lookup type %u.\n", lookup_type);
5960 return FALSE;
5963 static BOOL opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5964 const struct lookup *lookup)
5966 const struct dwrite_fonttable *gpos = &context->table->table;
5967 static const unsigned short gpos_formats[] =
5969 0, /* Unused */
5970 2, /* SinglePos */
5971 2, /* PairPos */
5972 1, /* CursivePos */
5973 1, /* MarkBasePos */
5974 1, /* MarkLigPos */
5975 1, /* MarkMarkPos */
5976 3, /* ContextPos */
5977 3, /* ChainContextPos */
5978 0, /* Extension, unused */
5980 unsigned int i, coverage, lookup_type, format;
5982 for (i = 0; i < lookup->subtable_count; ++i)
5984 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
5986 lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset, lookup);
5988 format = table_read_be_word(gpos, subtable_offset);
5990 if (!format || format > ARRAY_SIZE(gpos_formats) || format > gpos_formats[lookup_type])
5991 break;
5993 coverage = table_read_be_word(gpos, subtable_offset + 2);
5995 switch (lookup_type)
5997 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
5998 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
5999 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
6000 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
6001 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
6002 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
6004 if (opentype_layout_is_glyph_covered(gpos, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6005 return TRUE;
6007 break;
6009 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
6011 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6012 return TRUE;
6014 break;
6016 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
6018 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6019 return TRUE;
6021 break;
6023 default:
6024 WARN("Unknown lookup type %u.\n", lookup_type);
6028 return FALSE;
6031 typedef BOOL (*p_lookup_is_glyph_covered_func)(struct scriptshaping_context *context, UINT16 glyph, const struct lookup *lookup);
6033 BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index,
6034 unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count,
6035 const UINT16 *glyphs, UINT8 *feature_applies)
6037 p_lookup_is_glyph_covered_func func_is_covered;
6038 struct shaping_features features = { 0 };
6039 struct lookups lookups = { 0 };
6040 BOOL ret = FALSE, is_covered;
6041 unsigned int i, j, applies;
6043 features.features = feature;
6044 features.count = 1;
6046 for (i = 0; i < context->glyph_count; ++i)
6047 opentype_set_glyph_props(context, i);
6049 opentype_layout_collect_lookups(context, script_index, language_index, &features, context->table, &lookups);
6051 func_is_covered = context->table == &context->cache->gsub ? opentype_layout_gsub_lookup_is_glyph_covered :
6052 opentype_layout_gpos_lookup_is_glyph_covered;
6054 for (i = 0; i < lookups.count; ++i)
6056 struct lookup *lookup = &lookups.lookups[i];
6058 applies = 0;
6059 for (j = 0; j < context->glyph_count; ++j)
6061 if (lookup_is_glyph_match(context, j, lookup->flags))
6063 if ((is_covered = func_is_covered(context, glyphs[i], lookup)))
6064 ++applies;
6065 feature_applies[j] |= is_covered;
6069 if ((ret = (applies == context->glyph_count)))
6070 break;
6073 heap_free(lookups.lookups);
6075 return ret;
6078 BOOL opentype_has_vertical_variants(struct dwrite_fontface *fontface)
6080 unsigned int i, j, count = 0, lookup_type, subtable_offset;
6081 struct shaping_features features = { 0 };
6082 struct shaping_feature vert_feature = { 0 };
6083 struct scriptshaping_context context = { 0 };
6084 struct lookups lookups = { 0 };
6085 UINT16 format;
6087 context.cache = fontface_get_shaping_cache(fontface);
6088 context.table = &context.cache->gsub;
6090 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6091 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6092 vert_feature.max_value = 1;
6093 vert_feature.default_value = 1;
6095 features.features = &vert_feature;
6096 features.count = features.capacity = 1;
6098 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6100 for (i = 0; i < lookups.count && !count; ++i)
6102 const struct dwrite_fonttable *table = &context.table->table;
6103 const struct lookup *lookup = &lookups.lookups[i];
6105 for (j = 0; j < lookup->subtable_count && !count; ++j)
6107 subtable_offset = opentype_layout_get_gsubgpos_subtable(&context, lookup->offset, j);
6108 lookup_type = opentype_layout_adjust_extension_subtable(&context, &subtable_offset, lookup);
6110 if (lookup_type != GSUB_LOOKUP_SINGLE_SUBST)
6111 continue;
6113 format = table_read_be_word(table, subtable_offset);
6115 if (format == 1)
6117 count = 1;
6119 else if (format == 2)
6121 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count));
6123 else
6124 WARN("Unrecognized single substitution format %u.\n", format);
6128 heap_free(lookups.lookups);
6130 return !!count;
6133 HRESULT opentype_get_vertical_glyph_variants(struct dwrite_fontface *fontface, unsigned int glyph_count,
6134 const UINT16 *nominal_glyphs, UINT16 *glyphs)
6136 struct shaping_features features = { 0 };
6137 struct shaping_feature vert_feature = { 0 };
6138 struct scriptshaping_context context = { 0 };
6139 struct lookups lookups = { 0 };
6140 unsigned int i;
6142 memcpy(glyphs, nominal_glyphs, glyph_count * sizeof(*glyphs));
6144 if (!(fontface->flags & FONTFACE_HAS_VERTICAL_VARIANTS))
6145 return S_OK;
6147 context.cache = fontface_get_shaping_cache(fontface);
6148 context.u.subst.glyphs = glyphs;
6149 context.u.subst.glyph_props = heap_calloc(glyph_count, sizeof(*context.u.subst.glyph_props));
6150 context.u.subst.max_glyph_count = glyph_count;
6151 context.u.subst.capacity = glyph_count;
6152 context.glyph_infos = heap_alloc_zero(sizeof(*context.glyph_infos) * glyph_count);
6153 context.table = &context.cache->gsub;
6155 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6156 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6157 vert_feature.max_value = 1;
6158 vert_feature.default_value = 1;
6160 features.features = &vert_feature;
6161 features.count = features.capacity = 1;
6163 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6164 opentype_layout_set_glyph_masks(&context, &features);
6166 for (i = 0; i < lookups.count; ++i)
6168 const struct lookup *lookup = &lookups.lookups[i];
6170 /* FIXME: should probably handle extension subtables. */
6171 if (lookup->type != GSUB_LOOKUP_SINGLE_SUBST)
6172 continue;
6174 context.cur = 0;
6175 while (context.cur < context.glyph_count)
6177 BOOL ret = FALSE;
6179 if (lookup_is_glyph_match(&context, context.cur, lookup->flags))
6180 ret = opentype_layout_apply_gsub_lookup(&context, lookup);
6182 if (!ret)
6183 context.cur++;
6187 heap_free(context.u.subst.glyph_props);
6188 heap_free(context.glyph_infos);
6189 heap_free(lookups.lookups);
6191 return S_OK;