include: Make sure __int64 is correctly defined on PPC64.
[wine.git] / dlls / dwrite / opentype.c
blob0e5b8c75f622d44792ed81950e31529cd037f600
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 struct name_record
325 WORD platformID;
326 WORD encodingID;
327 WORD languageID;
328 WORD nameID;
329 WORD length;
330 WORD offset;
333 struct name_header
335 WORD format;
336 WORD count;
337 WORD stringOffset;
338 struct name_record records[1];
341 struct vdmx_header
343 WORD version;
344 WORD num_recs;
345 WORD num_ratios;
348 struct vdmx_ratio
350 BYTE bCharSet;
351 BYTE xRatio;
352 BYTE yStartRatio;
353 BYTE yEndRatio;
356 struct vdmx_vtable
358 WORD yPelHeight;
359 SHORT yMax;
360 SHORT yMin;
363 struct vdmx_group
365 WORD recs;
366 BYTE startsz;
367 BYTE endsz;
368 struct vdmx_vtable entries[1];
371 struct ot_feature_record
373 DWORD tag;
374 WORD offset;
377 struct ot_feature_list
379 WORD feature_count;
380 struct ot_feature_record features[1];
383 struct ot_langsys
385 WORD lookup_order; /* Reserved */
386 WORD required_feature_index;
387 WORD feature_count;
388 WORD feature_index[1];
391 struct ot_langsys_record
393 CHAR tag[4];
394 WORD langsys;
397 struct ot_script
399 WORD default_langsys;
400 WORD langsys_count;
401 struct ot_langsys_record langsys[1];
402 } OT_Script;
404 struct ot_script_record
406 CHAR tag[4];
407 WORD script;
410 struct ot_script_list
412 WORD script_count;
413 struct ot_script_record scripts[1];
416 enum ot_gdef_class
418 GDEF_CLASS_UNCLASSIFIED = 0,
419 GDEF_CLASS_BASE = 1,
420 GDEF_CLASS_LIGATURE = 2,
421 GDEF_CLASS_MARK = 3,
422 GDEF_CLASS_COMPONENT = 4,
423 GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
426 struct gdef_header
428 DWORD version;
429 UINT16 classdef;
430 UINT16 attach_list;
431 UINT16 ligcaret_list;
432 UINT16 markattach_classdef;
433 UINT16 markglyphsetdef;
436 struct ot_gdef_classdef_format1
438 WORD format;
439 WORD start_glyph;
440 WORD glyph_count;
441 WORD classes[1];
444 struct ot_gdef_class_range
446 WORD start_glyph;
447 WORD end_glyph;
448 WORD glyph_class;
451 struct ot_gdef_classdef_format2
453 WORD format;
454 WORD range_count;
455 struct ot_gdef_class_range ranges[1];
458 struct gpos_gsub_header
460 DWORD version;
461 WORD script_list;
462 WORD feature_list;
463 WORD lookup_list;
466 enum gsub_gpos_lookup_flags
468 LOOKUP_FLAG_RTL = 0x1, /* Only used for GPOS cursive attachments. */
470 LOOKUP_FLAG_IGNORE_BASE = 0x2,
471 LOOKUP_FLAG_IGNORE_LIGATURES = 0x4,
472 LOOKUP_FLAG_IGNORE_MARKS = 0x8,
473 LOOKUP_FLAG_IGNORE_MASK = 0xe,
475 LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x10,
476 LOOKUP_FLAG_MARK_ATTACHMENT_TYPE = 0xff00,
479 enum glyph_prop_flags
481 GLYPH_PROP_BASE = LOOKUP_FLAG_IGNORE_BASE,
482 GLYPH_PROP_LIGATURE = LOOKUP_FLAG_IGNORE_LIGATURES,
483 GLYPH_PROP_MARK = LOOKUP_FLAG_IGNORE_MARKS,
484 GLYPH_PROP_ZWNJ = 0x10,
485 GLYPH_PROP_ZWJ = 0x20,
486 GLYPH_PROP_IGNORABLE = 0x40,
487 GLYPH_PROP_HIDDEN = 0x80,
490 enum gpos_lookup_type
492 GPOS_LOOKUP_SINGLE_ADJUSTMENT = 1,
493 GPOS_LOOKUP_PAIR_ADJUSTMENT = 2,
494 GPOS_LOOKUP_CURSIVE_ATTACHMENT = 3,
495 GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT = 4,
496 GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT = 5,
497 GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT = 6,
498 GPOS_LOOKUP_CONTEXTUAL_POSITION = 7,
499 GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION = 8,
500 GPOS_LOOKUP_EXTENSION_POSITION = 9,
503 enum gsub_lookup_type
505 GSUB_LOOKUP_SINGLE_SUBST = 1,
506 GSUB_LOOKUP_MULTIPLE_SUBST = 2,
507 GSUB_LOOKUP_ALTERNATE_SUBST = 3,
508 GSUB_LOOKUP_LIGATURE_SUBST = 4,
509 GSUB_LOOKUP_CONTEXTUAL_SUBST = 5,
510 GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST = 6,
511 GSUB_LOOKUP_EXTENSION_SUBST = 7,
512 GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST = 8,
515 enum gpos_value_format
517 GPOS_VALUE_X_PLACEMENT = 0x1,
518 GPOS_VALUE_Y_PLACEMENT = 0x2,
519 GPOS_VALUE_X_ADVANCE = 0x4,
520 GPOS_VALUE_Y_ADVANCE = 0x8,
521 GPOS_VALUE_X_PLACEMENT_DEVICE = 0x10,
522 GPOS_VALUE_Y_PLACEMENT_DEVICE = 0x20,
523 GPOS_VALUE_X_ADVANCE_DEVICE = 0x40,
524 GPOS_VALUE_Y_ADVANCE_DEVICE = 0x80,
527 enum OPENTYPE_PLATFORM_ID
529 OPENTYPE_PLATFORM_UNICODE = 0,
530 OPENTYPE_PLATFORM_MAC,
531 OPENTYPE_PLATFORM_ISO,
532 OPENTYPE_PLATFORM_WIN,
533 OPENTYPE_PLATFORM_CUSTOM
536 struct ot_gsubgpos_extension_format1
538 UINT16 format;
539 UINT16 lookup_type;
540 DWORD extension_offset;
543 struct ot_gsub_singlesubst_format1
545 UINT16 format;
546 UINT16 coverage;
547 short delta;
550 struct ot_gsub_singlesubst_format2
552 UINT16 format;
553 UINT16 coverage;
554 UINT16 count;
555 UINT16 substitutes[1];
558 struct ot_gsub_multsubst_format1
560 UINT16 format;
561 UINT16 coverage;
562 UINT16 seq_count;
563 UINT16 seq[1];
566 struct ot_gsub_altsubst_format1
568 UINT16 format;
569 UINT16 coverage;
570 UINT16 count;
571 UINT16 sets[1];
574 struct ot_gsub_ligsubst_format1
576 UINT16 format;
577 UINT16 coverage;
578 UINT16 lig_set_count;
579 UINT16 lig_sets[1];
582 struct ot_gsub_ligset
584 UINT16 count;
585 UINT16 offsets[1];
588 struct ot_gsub_lig
590 UINT16 lig_glyph;
591 UINT16 comp_count;
592 UINT16 components[1];
595 struct ot_gsubgpos_context_format1
597 UINT16 format;
598 UINT16 coverage;
599 UINT16 ruleset_count;
600 UINT16 rulesets[1];
603 struct ot_gsubgpos_ruleset
605 UINT16 count;
606 UINT16 offsets[1];
609 struct ot_feature
611 WORD feature_params;
612 WORD lookup_count;
613 WORD lookuplist_index[1];
616 struct ot_lookup_list
618 WORD lookup_count;
619 WORD lookup[1];
622 struct ot_lookup_table
624 WORD lookup_type;
625 WORD flags;
626 WORD subtable_count;
627 WORD subtable[1];
630 #define GLYPH_NOT_COVERED (~0u)
632 struct ot_coverage_format1
634 WORD format;
635 WORD glyph_count;
636 WORD glyphs[1];
639 struct ot_coverage_range
641 WORD start_glyph;
642 WORD end_glyph;
643 WORD startcoverage_index;
646 struct ot_coverage_format2
648 WORD format;
649 WORD range_count;
650 struct ot_coverage_range ranges[1];
653 struct ot_gpos_device_table
655 WORD start_size;
656 WORD end_size;
657 WORD format;
658 WORD values[1];
661 struct ot_gpos_singlepos_format1
663 WORD format;
664 WORD coverage;
665 WORD value_format;
666 WORD value[1];
669 struct ot_gpos_singlepos_format2
671 WORD format;
672 WORD coverage;
673 WORD value_format;
674 WORD value_count;
675 WORD values[1];
678 struct ot_gpos_pairvalue
680 WORD second_glyph;
681 BYTE data[1];
684 struct ot_gpos_pairset
686 WORD pairvalue_count;
687 struct ot_gpos_pairvalue pairvalues[1];
690 struct ot_gpos_pairpos_format1
692 WORD format;
693 WORD coverage;
694 WORD value_format1;
695 WORD value_format2;
696 WORD pairset_count;
697 WORD pairsets[1];
700 struct ot_gpos_pairpos_format2
702 WORD format;
703 WORD coverage;
704 WORD value_format1;
705 WORD value_format2;
706 WORD class_def1;
707 WORD class_def2;
708 WORD class1_count;
709 WORD class2_count;
710 WORD values[1];
713 struct ot_gpos_anchor_format1
715 WORD format;
716 short x_coord;
717 short y_coord;
720 struct ot_gpos_anchor_format2
722 WORD format;
723 short x_coord;
724 short y_coord;
725 WORD anchor_point;
728 struct ot_gpos_anchor_format3
730 WORD format;
731 short x_coord;
732 short y_coord;
733 WORD x_dev_offset;
734 WORD y_dev_offset;
737 struct ot_gpos_cursive_format1
739 WORD format;
740 WORD coverage;
741 WORD count;
742 WORD anchors[1];
745 struct ot_gpos_mark_record
747 WORD mark_class;
748 WORD mark_anchor;
751 struct ot_gpos_mark_array
753 WORD count;
754 struct ot_gpos_mark_record records[1];
757 struct ot_gpos_base_array
759 WORD count;
760 WORD offsets[1];
763 struct ot_gpos_mark_to_base_format1
765 WORD format;
766 WORD mark_coverage;
767 WORD base_coverage;
768 WORD mark_class_count;
769 WORD mark_array;
770 WORD base_array;
773 struct ot_gpos_mark_to_lig_format1
775 WORD format;
776 WORD mark_coverage;
777 WORD lig_coverage;
778 WORD mark_class_count;
779 WORD mark_array;
780 WORD lig_array;
783 struct ot_gpos_mark_to_mark_format1
785 WORD format;
786 WORD mark1_coverage;
787 WORD mark2_coverage;
788 WORD mark_class_count;
789 WORD mark1_array;
790 WORD mark2_array;
793 typedef struct {
794 WORD SubstFormat;
795 WORD Coverage;
796 WORD DeltaGlyphID;
797 } GSUB_SingleSubstFormat1;
799 typedef struct {
800 WORD SubstFormat;
801 WORD Coverage;
802 WORD GlyphCount;
803 WORD Substitute[1];
804 } GSUB_SingleSubstFormat2;
806 typedef struct {
807 WORD SubstFormat;
808 WORD ExtensionLookupType;
809 DWORD ExtensionOffset;
810 } GSUB_ExtensionPosFormat1;
812 #include "poppack.h"
814 enum TT_NAME_WINDOWS_ENCODING_ID
816 TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
817 TT_NAME_WINDOWS_ENCODING_UNICODE_BMP,
818 TT_NAME_WINDOWS_ENCODING_SJIS,
819 TT_NAME_WINDOWS_ENCODING_PRC,
820 TT_NAME_WINDOWS_ENCODING_BIG5,
821 TT_NAME_WINDOWS_ENCODING_WANSUNG,
822 TT_NAME_WINDOWS_ENCODING_JOHAB,
823 TT_NAME_WINDOWS_ENCODING_RESERVED1,
824 TT_NAME_WINDOWS_ENCODING_RESERVED2,
825 TT_NAME_WINDOWS_ENCODING_RESERVED3,
826 TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
829 enum TT_NAME_MAC_ENCODING_ID
831 TT_NAME_MAC_ENCODING_ROMAN = 0,
832 TT_NAME_MAC_ENCODING_JAPANESE,
833 TT_NAME_MAC_ENCODING_TRAD_CHINESE,
834 TT_NAME_MAC_ENCODING_KOREAN,
835 TT_NAME_MAC_ENCODING_ARABIC,
836 TT_NAME_MAC_ENCODING_HEBREW,
837 TT_NAME_MAC_ENCODING_GREEK,
838 TT_NAME_MAC_ENCODING_RUSSIAN,
839 TT_NAME_MAC_ENCODING_RSYMBOL,
840 TT_NAME_MAC_ENCODING_DEVANAGARI,
841 TT_NAME_MAC_ENCODING_GURMUKHI,
842 TT_NAME_MAC_ENCODING_GUJARATI,
843 TT_NAME_MAC_ENCODING_ORIYA,
844 TT_NAME_MAC_ENCODING_BENGALI,
845 TT_NAME_MAC_ENCODING_TAMIL,
846 TT_NAME_MAC_ENCODING_TELUGU,
847 TT_NAME_MAC_ENCODING_KANNADA,
848 TT_NAME_MAC_ENCODING_MALAYALAM,
849 TT_NAME_MAC_ENCODING_SINHALESE,
850 TT_NAME_MAC_ENCODING_BURMESE,
851 TT_NAME_MAC_ENCODING_KHMER,
852 TT_NAME_MAC_ENCODING_THAI,
853 TT_NAME_MAC_ENCODING_LAOTIAN,
854 TT_NAME_MAC_ENCODING_GEORGIAN,
855 TT_NAME_MAC_ENCODING_ARMENIAN,
856 TT_NAME_MAC_ENCODING_SIMPL_CHINESE,
857 TT_NAME_MAC_ENCODING_TIBETAN,
858 TT_NAME_MAC_ENCODING_MONGOLIAN,
859 TT_NAME_MAC_ENCODING_GEEZ,
860 TT_NAME_MAC_ENCODING_SLAVIC,
861 TT_NAME_MAC_ENCODING_VIETNAMESE,
862 TT_NAME_MAC_ENCODING_SINDHI,
863 TT_NAME_MAC_ENCODING_UNINTERPRETED
866 enum TT_NAME_MAC_LANGUAGE_ID
868 TT_NAME_MAC_LANGID_ENGLISH = 0,
869 TT_NAME_MAC_LANGID_FRENCH,
870 TT_NAME_MAC_LANGID_GERMAN,
871 TT_NAME_MAC_LANGID_ITALIAN,
872 TT_NAME_MAC_LANGID_DUTCH,
873 TT_NAME_MAC_LANGID_SWEDISH,
874 TT_NAME_MAC_LANGID_SPANISH,
875 TT_NAME_MAC_LANGID_DANISH,
876 TT_NAME_MAC_LANGID_PORTUGUESE,
877 TT_NAME_MAC_LANGID_NORWEGIAN,
878 TT_NAME_MAC_LANGID_HEBREW,
879 TT_NAME_MAC_LANGID_JAPANESE,
880 TT_NAME_MAC_LANGID_ARABIC,
881 TT_NAME_MAC_LANGID_FINNISH,
882 TT_NAME_MAC_LANGID_GREEK,
883 TT_NAME_MAC_LANGID_ICELANDIC,
884 TT_NAME_MAC_LANGID_MALTESE,
885 TT_NAME_MAC_LANGID_TURKISH,
886 TT_NAME_MAC_LANGID_CROATIAN,
887 TT_NAME_MAC_LANGID_TRAD_CHINESE,
888 TT_NAME_MAC_LANGID_URDU,
889 TT_NAME_MAC_LANGID_HINDI,
890 TT_NAME_MAC_LANGID_THAI,
891 TT_NAME_MAC_LANGID_KOREAN,
892 TT_NAME_MAC_LANGID_LITHUANIAN,
893 TT_NAME_MAC_LANGID_POLISH,
894 TT_NAME_MAC_LANGID_HUNGARIAN,
895 TT_NAME_MAC_LANGID_ESTONIAN,
896 TT_NAME_MAC_LANGID_LATVIAN,
897 TT_NAME_MAC_LANGID_SAMI,
898 TT_NAME_MAC_LANGID_FAROESE,
899 TT_NAME_MAC_LANGID_FARSI,
900 TT_NAME_MAC_LANGID_RUSSIAN,
901 TT_NAME_MAC_LANGID_SIMPL_CHINESE,
902 TT_NAME_MAC_LANGID_FLEMISH,
903 TT_NAME_MAC_LANGID_GAELIC,
904 TT_NAME_MAC_LANGID_ALBANIAN,
905 TT_NAME_MAC_LANGID_ROMANIAN,
906 TT_NAME_MAC_LANGID_CZECH,
907 TT_NAME_MAC_LANGID_SLOVAK,
908 TT_NAME_MAC_LANGID_SLOVENIAN,
909 TT_NAME_MAC_LANGID_YIDDISH,
910 TT_NAME_MAC_LANGID_SERBIAN,
911 TT_NAME_MAC_LANGID_MACEDONIAN,
912 TT_NAME_MAC_LANGID_BULGARIAN,
913 TT_NAME_MAC_LANGID_UKRAINIAN,
914 TT_NAME_MAC_LANGID_BYELORUSSIAN,
915 TT_NAME_MAC_LANGID_UZBEK,
916 TT_NAME_MAC_LANGID_KAZAKH,
917 TT_NAME_MAC_LANGID_AZERB_CYR,
918 TT_NAME_MAC_LANGID_AZERB_ARABIC,
919 TT_NAME_MAC_LANGID_ARMENIAN,
920 TT_NAME_MAC_LANGID_GEORGIAN,
921 TT_NAME_MAC_LANGID_MOLDAVIAN,
922 TT_NAME_MAC_LANGID_KIRGHIZ,
923 TT_NAME_MAC_LANGID_TAJIKI,
924 TT_NAME_MAC_LANGID_TURKMEN,
925 TT_NAME_MAC_LANGID_MONGOLIAN,
926 TT_NAME_MAC_LANGID_MONGOLIAN_CYR,
927 TT_NAME_MAC_LANGID_PASHTO,
928 TT_NAME_MAC_LANGID_KURDISH,
929 TT_NAME_MAC_LANGID_KASHMIRI,
930 TT_NAME_MAC_LANGID_SINDHI,
931 TT_NAME_MAC_LANGID_TIBETAN,
932 TT_NAME_MAC_LANGID_NEPALI,
933 TT_NAME_MAC_LANGID_SANSKRIT,
934 TT_NAME_MAC_LANGID_MARATHI,
935 TT_NAME_MAC_LANGID_BENGALI,
936 TT_NAME_MAC_LANGID_ASSAMESE,
937 TT_NAME_MAC_LANGID_GUJARATI,
938 TT_NAME_MAC_LANGID_PUNJABI,
939 TT_NAME_MAC_LANGID_ORIYA,
940 TT_NAME_MAC_LANGID_MALAYALAM,
941 TT_NAME_MAC_LANGID_KANNADA,
942 TT_NAME_MAC_LANGID_TAMIL,
943 TT_NAME_MAC_LANGID_TELUGU,
944 TT_NAME_MAC_LANGID_SINHALESE,
945 TT_NAME_MAC_LANGID_BURMESE,
946 TT_NAME_MAC_LANGID_KHMER,
947 TT_NAME_MAC_LANGID_LAO,
948 TT_NAME_MAC_LANGID_VIETNAMESE,
949 TT_NAME_MAC_LANGID_INDONESIAN,
950 TT_NAME_MAC_LANGID_TAGALOG,
951 TT_NAME_MAC_LANGID_MALAY_ROMAN,
952 TT_NAME_MAC_LANGID_MALAY_ARABIC,
953 TT_NAME_MAC_LANGID_AMHARIC,
954 TT_NAME_MAC_LANGID_TIGRINYA,
955 TT_NAME_MAC_LANGID_GALLA,
956 TT_NAME_MAC_LANGID_SOMALI,
957 TT_NAME_MAC_LANGID_SWAHILI,
958 TT_NAME_MAC_LANGID_KINYARWANDA,
959 TT_NAME_MAC_LANGID_RUNDI,
960 TT_NAME_MAC_LANGID_NYANJA,
961 TT_NAME_MAC_LANGID_MALAGASY,
962 TT_NAME_MAC_LANGID_ESPERANTO,
963 TT_NAME_MAC_LANGID_WELSH = 128,
964 TT_NAME_MAC_LANGID_BASQUE,
965 TT_NAME_MAC_LANGID_CATALAN,
966 TT_NAME_MAC_LANGID_LATIN,
967 TT_NAME_MAC_LANGID_QUECHUA,
968 TT_NAME_MAC_LANGID_GUARANI,
969 TT_NAME_MAC_LANGID_AYMARA,
970 TT_NAME_MAC_LANGID_TATAR,
971 TT_NAME_MAC_LANGID_UIGHUR,
972 TT_NAME_MAC_LANGID_DZONGKHA,
973 TT_NAME_MAC_LANGID_JAVANESE,
974 TT_NAME_MAC_LANGID_SUNDANESE,
975 TT_NAME_MAC_LANGID_GALICIAN,
976 TT_NAME_MAC_LANGID_AFRIKAANS,
977 TT_NAME_MAC_LANGID_BRETON,
978 TT_NAME_MAC_LANGID_INUKTITUT,
979 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC,
980 TT_NAME_MAC_LANGID_MANX_GAELIC,
981 TT_NAME_MAC_LANGID_IRISH_GAELIC,
982 TT_NAME_MAC_LANGID_TONGAN,
983 TT_NAME_MAC_LANGID_GREEK_POLYTONIC,
984 TT_NAME_MAC_LANGID_GREENLANDIC,
985 TT_NAME_MAC_LANGID_AZER_ROMAN
988 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
989 static const char name_mac_langid_to_locale[][10] = {
990 "en-US",
991 "fr-FR",
992 "de-DE",
993 "it-IT",
994 "nl-NL",
995 "sv-SE",
996 "es-ES",
997 "da-DA",
998 "pt-PT",
999 "no-NO",
1000 "he-IL",
1001 "ja-JP",
1002 "ar-AR",
1003 "fi-FI",
1004 "el-GR",
1005 "is-IS",
1006 "mt-MT",
1007 "tr-TR",
1008 "hr-HR",
1009 "zh-HK",
1010 "ur-PK",
1011 "hi-IN",
1012 "th-TH",
1013 "ko-KR",
1014 "lt-LT",
1015 "pl-PL",
1016 "hu-HU",
1017 "et-EE",
1018 "lv-LV",
1019 "se-NO",
1020 "fo-FO",
1021 "fa-IR",
1022 "ru-RU",
1023 "zh-CN",
1024 "nl-BE",
1025 "gd-GB",
1026 "sq-AL",
1027 "ro-RO",
1028 "cs-CZ",
1029 "sk-SK",
1030 "sl-SI",
1032 "sr-Latn",
1033 "mk-MK",
1034 "bg-BG",
1035 "uk-UA",
1036 "be-BY",
1037 "uz-Latn",
1038 "kk-KZ",
1039 "az-Cyrl-AZ",
1040 "az-AZ",
1041 "hy-AM",
1042 "ka-GE",
1045 "tg-TJ",
1046 "tk-TM",
1047 "mn-Mong",
1048 "mn-MN",
1049 "ps-AF",
1050 "ku-Arab",
1052 "sd-Arab",
1053 "bo-CN",
1054 "ne-NP",
1055 "sa-IN",
1056 "mr-IN",
1057 "bn-IN",
1058 "as-IN",
1059 "gu-IN",
1060 "pa-Arab",
1061 "or-IN",
1062 "ml-IN",
1063 "kn-IN",
1064 "ta-LK",
1065 "te-IN",
1066 "si-LK",
1068 "km-KH",
1069 "lo-LA",
1070 "vi-VN",
1071 "id-ID",
1073 "ms-MY",
1074 "ms-Arab",
1075 "am-ET",
1076 "ti-ET",
1079 "sw-KE",
1080 "rw-RW",
1118 "cy-GB",
1119 "eu-ES",
1120 "ca-ES",
1125 "tt-RU",
1126 "ug-CN",
1130 "gl-ES",
1131 "af-ZA",
1132 "br-FR",
1133 "iu-Latn-CA",
1134 "gd-GB",
1136 "ga-IE",
1139 "kl-GL",
1140 "az-Latn"
1143 enum OPENTYPE_STRING_ID
1145 OPENTYPE_STRING_COPYRIGHT_NOTICE = 0,
1146 OPENTYPE_STRING_FAMILY_NAME,
1147 OPENTYPE_STRING_SUBFAMILY_NAME,
1148 OPENTYPE_STRING_UNIQUE_IDENTIFIER,
1149 OPENTYPE_STRING_FULL_FONTNAME,
1150 OPENTYPE_STRING_VERSION_STRING,
1151 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1152 OPENTYPE_STRING_TRADEMARK,
1153 OPENTYPE_STRING_MANUFACTURER,
1154 OPENTYPE_STRING_DESIGNER,
1155 OPENTYPE_STRING_DESCRIPTION,
1156 OPENTYPE_STRING_VENDOR_URL,
1157 OPENTYPE_STRING_DESIGNER_URL,
1158 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1159 OPENTYPE_STRING_LICENSE_INFO_URL,
1160 OPENTYPE_STRING_RESERVED_ID15,
1161 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1162 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1163 OPENTYPE_STRING_COMPATIBLE_FULLNAME,
1164 OPENTYPE_STRING_SAMPLE_TEXT,
1165 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1166 OPENTYPE_STRING_WWS_FAMILY_NAME,
1167 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
1170 static const UINT16 dwriteid_to_opentypeid[DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME + 1] =
1172 (UINT16)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
1173 OPENTYPE_STRING_COPYRIGHT_NOTICE,
1174 OPENTYPE_STRING_VERSION_STRING,
1175 OPENTYPE_STRING_TRADEMARK,
1176 OPENTYPE_STRING_MANUFACTURER,
1177 OPENTYPE_STRING_DESIGNER,
1178 OPENTYPE_STRING_DESIGNER_URL,
1179 OPENTYPE_STRING_DESCRIPTION,
1180 OPENTYPE_STRING_VENDOR_URL,
1181 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1182 OPENTYPE_STRING_LICENSE_INFO_URL,
1183 OPENTYPE_STRING_FAMILY_NAME,
1184 OPENTYPE_STRING_SUBFAMILY_NAME,
1185 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1186 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1187 OPENTYPE_STRING_SAMPLE_TEXT,
1188 OPENTYPE_STRING_FULL_FONTNAME,
1189 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1190 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1191 OPENTYPE_STRING_WWS_FAMILY_NAME,
1194 /* CPAL table */
1195 struct cpal_header_0
1197 USHORT version;
1198 USHORT num_palette_entries;
1199 USHORT num_palettes;
1200 USHORT num_color_records;
1201 ULONG offset_first_color_record;
1202 USHORT color_record_indices[1];
1205 struct cpal_color_record
1207 BYTE blue;
1208 BYTE green;
1209 BYTE red;
1210 BYTE alpha;
1213 /* COLR table */
1214 struct colr_header
1216 USHORT version;
1217 USHORT num_baseglyph_records;
1218 ULONG offset_baseglyph_records;
1219 ULONG offset_layer_records;
1220 USHORT num_layer_records;
1223 struct colr_baseglyph_record
1225 USHORT glyph;
1226 USHORT first_layer_index;
1227 USHORT num_layers;
1230 struct colr_layer_record
1232 USHORT glyph;
1233 USHORT palette_index;
1236 struct meta_data_map
1238 DWORD tag;
1239 DWORD offset;
1240 DWORD length;
1243 struct meta_header
1245 DWORD version;
1246 DWORD flags;
1247 DWORD reserved;
1248 DWORD data_maps_count;
1249 struct meta_data_map maps[1];
1252 static const void *table_read_ensure(const struct dwrite_fonttable *table, unsigned int offset, unsigned int size)
1254 if (size > table->size || offset > table->size - size)
1255 return NULL;
1257 return table->data + offset;
1260 static WORD table_read_be_word(const struct dwrite_fonttable *table, unsigned int offset)
1262 const WORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1263 return ptr ? GET_BE_WORD(*ptr) : 0;
1266 static DWORD table_read_be_dword(const struct dwrite_fonttable *table, unsigned int offset)
1268 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1269 return ptr ? GET_BE_DWORD(*ptr) : 0;
1272 static DWORD table_read_dword(const struct dwrite_fonttable *table, unsigned int offset)
1274 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1275 return ptr ? *ptr : 0;
1278 BOOL is_face_type_supported(DWRITE_FONT_FACE_TYPE type)
1280 return (type == DWRITE_FONT_FACE_TYPE_CFF) ||
1281 (type == DWRITE_FONT_FACE_TYPE_TRUETYPE) ||
1282 (type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) ||
1283 (type == DWRITE_FONT_FACE_TYPE_RAW_CFF);
1286 typedef HRESULT (*dwrite_fontfile_analyzer)(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1287 DWRITE_FONT_FACE_TYPE *face_type);
1289 static HRESULT opentype_ttc_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1290 DWRITE_FONT_FACE_TYPE *face_type)
1292 static const DWORD ttctag = MS_TTCF_TAG;
1293 const TTC_Header_V1 *header;
1294 void *context;
1295 HRESULT hr;
1297 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(header), &context);
1298 if (FAILED(hr))
1299 return hr;
1301 if (!memcmp(header->TTCTag, &ttctag, sizeof(ttctag))) {
1302 *font_count = GET_BE_DWORD(header->numFonts);
1303 *file_type = DWRITE_FONT_FILE_TYPE_OPENTYPE_COLLECTION;
1304 *face_type = DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION;
1307 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1309 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1312 static HRESULT opentype_ttf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1313 DWRITE_FONT_FACE_TYPE *face_type)
1315 const DWORD *header;
1316 void *context;
1317 HRESULT hr;
1319 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1320 if (FAILED(hr))
1321 return hr;
1323 if (GET_BE_DWORD(*header) == 0x10000) {
1324 *font_count = 1;
1325 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
1326 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
1329 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1331 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1334 static HRESULT opentype_otf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1335 DWRITE_FONT_FACE_TYPE *face_type)
1337 const DWORD *header;
1338 void *context;
1339 HRESULT hr;
1341 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1342 if (FAILED(hr))
1343 return hr;
1345 if (GET_BE_DWORD(*header) == MS_OTTO_TAG) {
1346 *font_count = 1;
1347 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
1348 *face_type = DWRITE_FONT_FACE_TYPE_CFF;
1351 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1353 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1356 static HRESULT opentype_type1_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1357 DWRITE_FONT_FACE_TYPE *face_type)
1359 #include "pshpack1.h"
1360 /* Specified in Adobe TechNote #5178 */
1361 struct pfm_header {
1362 WORD dfVersion;
1363 DWORD dfSize;
1364 char data0[95];
1365 DWORD dfDevice;
1366 char data1[12];
1368 #include "poppack.h"
1369 struct type1_header {
1370 WORD tag;
1371 char data[14];
1373 const struct type1_header *header;
1374 void *context;
1375 HRESULT hr;
1377 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1378 if (FAILED(hr))
1379 return hr;
1381 /* tag is followed by plain text section */
1382 if (header->tag == 0x8001 &&
1383 (!memcmp(header->data, "%!PS-AdobeFont", 14) ||
1384 !memcmp(header->data, "%!FontType", 10))) {
1385 *font_count = 1;
1386 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFB;
1387 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1390 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1392 /* let's see if it's a .pfm metrics file */
1393 if (*file_type == DWRITE_FONT_FILE_TYPE_UNKNOWN) {
1394 const struct pfm_header *pfm_header;
1395 UINT64 filesize;
1396 DWORD offset;
1397 BOOL header_checked;
1399 hr = IDWriteFontFileStream_GetFileSize(stream, &filesize);
1400 if (FAILED(hr))
1401 return hr;
1403 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&pfm_header, 0, sizeof(*pfm_header), &context);
1404 if (FAILED(hr))
1405 return hr;
1407 offset = pfm_header->dfDevice;
1408 header_checked = pfm_header->dfVersion == 0x100 && pfm_header->dfSize == filesize;
1409 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1411 /* as a last test check static string in PostScript information section */
1412 if (header_checked) {
1413 static const char postscript[] = "PostScript";
1414 char *devtype_name;
1416 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&devtype_name, offset, sizeof(postscript), &context);
1417 if (FAILED(hr))
1418 return hr;
1420 if (!memcmp(devtype_name, postscript, sizeof(postscript))) {
1421 *font_count = 1;
1422 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFM;
1423 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1426 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1430 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1433 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, BOOL *supported, DWRITE_FONT_FILE_TYPE *file_type,
1434 DWRITE_FONT_FACE_TYPE *face_type, UINT32 *face_count)
1436 static dwrite_fontfile_analyzer fontfile_analyzers[] = {
1437 opentype_ttf_analyzer,
1438 opentype_otf_analyzer,
1439 opentype_ttc_analyzer,
1440 opentype_type1_analyzer,
1441 NULL
1443 dwrite_fontfile_analyzer *analyzer = fontfile_analyzers;
1444 DWRITE_FONT_FACE_TYPE face;
1445 HRESULT hr;
1447 if (!face_type)
1448 face_type = &face;
1450 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
1451 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
1452 *face_count = 0;
1454 while (*analyzer) {
1455 hr = (*analyzer)(stream, face_count, file_type, face_type);
1456 if (FAILED(hr))
1457 return hr;
1459 if (hr == S_OK)
1460 break;
1462 analyzer++;
1465 *supported = is_face_type_supported(*face_type);
1466 return S_OK;
1469 HRESULT opentype_try_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag, const void **table_data,
1470 void **table_context, UINT32 *table_size, BOOL *found)
1472 void *table_directory_context, *sfnt_context;
1473 TT_TableRecord *table_record = NULL;
1474 TTC_SFNT_V1 *font_header = NULL;
1475 UINT32 table_offset = 0;
1476 UINT16 table_count;
1477 HRESULT hr;
1479 if (found) *found = FALSE;
1480 if (table_size) *table_size = 0;
1482 *table_data = NULL;
1483 *table_context = NULL;
1485 if (stream_desc->face_type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) {
1486 const TTC_Header_V1 *ttc_header;
1487 void * ttc_context;
1488 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
1489 if (SUCCEEDED(hr)) {
1490 if (stream_desc->face_index >= GET_BE_DWORD(ttc_header->numFonts))
1491 hr = E_INVALIDARG;
1492 else {
1493 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[stream_desc->face_index]);
1494 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
1496 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, ttc_context);
1499 else
1500 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
1502 if (FAILED(hr))
1503 return hr;
1505 table_count = GET_BE_WORD(font_header->numTables);
1506 table_offset += sizeof(*font_header);
1508 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, sfnt_context);
1510 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void **)&table_record, table_offset,
1511 table_count * sizeof(*table_record), &table_directory_context);
1512 if (hr == S_OK) {
1513 UINT16 i;
1515 for (i = 0; i < table_count; i++) {
1516 if (table_record->tag == tag) {
1517 UINT32 offset = GET_BE_DWORD(table_record->offset);
1518 UINT32 length = GET_BE_DWORD(table_record->length);
1520 if (found)
1521 *found = TRUE;
1522 if (table_size)
1523 *table_size = length;
1524 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, table_data, offset,
1525 length, table_context);
1526 break;
1528 table_record++;
1531 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, table_directory_context);
1534 return hr;
1537 static HRESULT opentype_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag,
1538 struct dwrite_fonttable *table)
1540 return opentype_try_get_font_table(stream_desc, tag, (const void **)&table->data, &table->context, &table->size, &table->exists);
1543 /**********
1544 * CMAP
1545 **********/
1547 static UINT16 opentype_cmap_format0_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1549 const UINT8 *glyphs = cmap->data;
1550 return (ch < 0xff) ? glyphs[ch] : 0;
1553 static unsigned int opentype_cmap_format0_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1554 DWRITE_UNICODE_RANGE *ranges)
1556 if (count > 0)
1558 ranges->first = 0;
1559 ranges->last = 255;
1562 return 1;
1565 struct cmap_format4_compare_context
1567 const struct dwrite_cmap *cmap;
1568 unsigned int ch;
1571 static int cmap_format4_compare_range(const void *a, const void *b)
1573 const struct cmap_format4_compare_context *key = a;
1574 const UINT16 *end = b;
1575 unsigned int idx;
1577 if (key->ch > GET_BE_WORD(*end))
1578 return 1;
1580 idx = end - key->cmap->u.format4.ends;
1581 if (key->ch < GET_BE_WORD(key->cmap->u.format4.starts[idx]))
1582 return -1;
1584 return 0;
1587 static UINT16 opentype_cmap_format4_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1589 struct cmap_format4_compare_context key = { .cmap = cmap, .ch = ch };
1590 unsigned int glyph, idx, range_offset;
1591 const UINT16 *end_found;
1593 /* Look up range. */
1594 end_found = bsearch(&key, cmap->u.format4.ends, cmap->u.format4.seg_count, sizeof(*cmap->u.format4.ends),
1595 cmap_format4_compare_range);
1596 if (!end_found)
1597 return 0;
1599 idx = end_found - cmap->u.format4.ends;
1601 range_offset = GET_BE_WORD(cmap->u.format4.id_range_offset[idx]);
1603 if (!range_offset)
1605 glyph = ch + GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1607 else
1609 unsigned int index = range_offset / 2 + (ch - GET_BE_WORD(cmap->u.format4.starts[idx])) + idx - cmap->u.format4.seg_count;
1610 if (index >= cmap->u.format4.glyph_id_array_len)
1611 return 0;
1612 glyph = GET_BE_WORD(cmap->u.format4.glyph_id_array[index]);
1613 if (!glyph)
1614 return 0;
1615 glyph += GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1618 return glyph & 0xffff;
1621 static unsigned int opentype_cmap_format4_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1622 DWRITE_UNICODE_RANGE *ranges)
1624 unsigned int i;
1626 count = min(count, cmap->u.format4.seg_count);
1628 for (i = 0; i < count; ++i)
1630 ranges[i].first = GET_BE_WORD(cmap->u.format4.starts[i]);
1631 ranges[i].last = GET_BE_WORD(cmap->u.format4.ends[i]);
1634 return cmap->u.format4.seg_count;
1637 static UINT16 opentype_cmap_format6_10_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1639 const UINT16 *glyphs = cmap->data;
1640 if (ch < cmap->u.format6_10.first || ch > cmap->u.format6_10.last) return 0;
1641 return glyphs[ch - cmap->u.format6_10.first];
1644 static unsigned int opentype_cmap_format6_10_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1645 DWRITE_UNICODE_RANGE *ranges)
1647 if (count > 0)
1649 ranges->first = cmap->u.format6_10.first;
1650 ranges->last = cmap->u.format6_10.last;
1653 return 1;
1656 static int cmap_format12_13_compare_group(const void *a, const void *b)
1658 const unsigned int *ch = a;
1659 const UINT32 *group = b;
1661 if (*ch > GET_BE_DWORD(group[1]))
1662 return 1;
1664 if (*ch < GET_BE_DWORD(group[0]))
1665 return -1;
1667 return 0;
1670 static UINT16 opentype_cmap_format12_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1672 const UINT32 *groups = cmap->data;
1673 const UINT32 *group_found;
1675 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1676 cmap_format12_13_compare_group)))
1677 return 0;
1679 return GET_BE_DWORD(group_found[0]) <= GET_BE_DWORD(group_found[1]) ?
1680 GET_BE_DWORD(group_found[2]) + (ch - GET_BE_DWORD(group_found[0])) : 0;
1683 static unsigned int opentype_cmap_format12_13_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1684 DWRITE_UNICODE_RANGE *ranges)
1686 unsigned int i, group_count = cmap->u.format12_13.group_count;
1687 const UINT32 *groups = cmap->data;
1689 count = min(count, group_count);
1691 for (i = 0; i < count; ++i)
1693 ranges[i].first = GET_BE_DWORD(groups[3 * i]);
1694 ranges[i].last = GET_BE_DWORD(groups[3 * i + 1]);
1697 return group_count;
1700 static UINT16 opentype_cmap_format13_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1702 const UINT32 *groups = cmap->data;
1703 const UINT32 *group_found;
1705 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1706 cmap_format12_13_compare_group)))
1707 return 0;
1709 return GET_BE_DWORD(group_found[2]);
1712 static UINT16 opentype_cmap_dummy_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1714 return 0;
1717 static unsigned int opentype_cmap_dummy_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1718 DWRITE_UNICODE_RANGE *ranges)
1720 return 0;
1723 UINT16 opentype_cmap_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1725 UINT16 glyph;
1727 if (!cmap->get_glyph) return 0;
1728 glyph = cmap->get_glyph(cmap, ch);
1729 if (!glyph && cmap->symbol && ch <= 0xff)
1730 glyph = cmap->get_glyph(cmap, ch + 0xf000);
1731 return glyph;
1734 static int cmap_header_compare(const void *a, const void *b)
1736 const UINT16 *key = a;
1737 const UINT16 *record = b;
1739 /* Platform. */
1740 if (key[0] < GET_BE_WORD(record[0])) return -1;
1741 if (key[0] > GET_BE_WORD(record[0])) return 1;
1742 /* Encoding. */
1743 if (key[1] < GET_BE_WORD(record[1])) return -1;
1744 if (key[1] > GET_BE_WORD(record[1])) return 1;
1746 return 0;
1749 void dwrite_cmap_init(struct dwrite_cmap *cmap, IDWriteFontFile *file, unsigned int face_index,
1750 DWRITE_FONT_FACE_TYPE face_type)
1752 static const UINT16 encodings[][2] =
1754 { 3, 0 }, /* MS Symbol encoding is preferred. */
1755 { 3, 10 },
1756 { 0, 6 },
1757 { 0, 4 },
1758 { 3, 1 },
1759 { 0, 3 },
1760 { 0, 2 },
1761 { 0, 1 },
1762 { 0, 0 },
1764 const struct cmap_encoding_record *records, *found_record = NULL;
1765 unsigned int length, offset, format, count, f, i, num_records;
1766 struct file_stream_desc stream_desc;
1767 struct dwrite_fonttable table;
1768 const UINT16 *pair = NULL;
1769 HRESULT hr;
1771 if (cmap->data) return;
1773 /* For fontface stream is already available and preset. */
1774 if (!cmap->stream && FAILED(hr = get_filestream_from_file(file, &cmap->stream)))
1776 WARN("Failed to get file stream, hr %#x.\n", hr);
1777 goto failed;
1780 stream_desc.stream = cmap->stream;
1781 stream_desc.face_type = face_type;
1782 stream_desc.face_index = face_index;
1784 opentype_get_font_table(&stream_desc, MS_CMAP_TAG, &table);
1785 if (!table.exists)
1786 goto failed;
1787 cmap->table_context = table.context;
1789 num_records = table_read_be_word(&table, 2);
1790 records = table_read_ensure(&table, 4, sizeof(*records) * num_records);
1792 for (i = 0; i < ARRAY_SIZE(encodings); ++i)
1794 pair = encodings[i];
1795 if ((found_record = bsearch(pair, records, num_records, sizeof(*records), cmap_header_compare)))
1796 break;
1799 if (!found_record)
1801 WARN("No suitable cmap table were found.\n");
1802 goto failed;
1805 /* Symbol encoding. */
1806 cmap->symbol = pair[0] == 3 && pair[1] == 0;
1807 offset = GET_BE_DWORD(found_record->offset);
1809 format = table_read_be_word(&table, offset);
1811 switch (format)
1813 case 0:
1814 cmap->data = table_read_ensure(&table, offset + 6, 256);
1815 cmap->get_glyph = opentype_cmap_format0_get_glyph;
1816 cmap->get_ranges = opentype_cmap_format0_get_ranges;
1817 break;
1818 case 4:
1819 length = table_read_be_word(&table, offset + 2);
1820 cmap->u.format4.seg_count = count = table_read_be_word(&table, offset + 6) / 2;
1821 cmap->u.format4.ends = table_read_ensure(&table, offset + 14, count * 2);
1822 cmap->u.format4.starts = cmap->u.format4.ends + count + 1;
1823 cmap->u.format4.id_delta = cmap->u.format4.starts + count;
1824 cmap->u.format4.id_range_offset = cmap->u.format4.id_delta + count;
1825 cmap->u.format4.glyph_id_array = cmap->data = cmap->u.format4.id_range_offset + count;
1826 cmap->u.format4.glyph_id_array_len = (length - 16 - 8 * count) / 2;
1827 cmap->get_glyph = opentype_cmap_format4_get_glyph;
1828 cmap->get_ranges = opentype_cmap_format4_get_ranges;
1829 break;
1830 case 6:
1831 case 10:
1832 /* Format 10 uses 4 byte fields. */
1833 f = format == 6 ? 1 : 2;
1834 cmap->u.format6_10.first = table_read_be_word(&table, offset + f * 6);
1835 count = table_read_be_word(&table, offset + f * 8);
1836 cmap->u.format6_10.last = cmap->u.format6_10.first + count;
1837 cmap->data = table_read_ensure(&table, offset + f * 10, count * 2);
1838 cmap->get_glyph = opentype_cmap_format6_10_get_glyph;
1839 cmap->get_ranges = opentype_cmap_format6_10_get_ranges;
1840 break;
1841 case 12:
1842 case 13:
1843 cmap->u.format12_13.group_count = count = table_read_be_dword(&table, offset + 12);
1844 cmap->data = table_read_ensure(&table, offset + 16, count * 3 * 4);
1845 cmap->get_glyph = format == 12 ? opentype_cmap_format12_get_glyph : opentype_cmap_format13_get_glyph;
1846 cmap->get_ranges = opentype_cmap_format12_13_get_ranges;
1847 break;
1848 default:
1849 WARN("Unhandled subtable format %u.\n", format);
1852 failed:
1854 if (!cmap->data)
1856 /* Dummy implementation, returns 0 unconditionally. */
1857 cmap->data = cmap;
1858 cmap->get_glyph = opentype_cmap_dummy_get_glyph;
1859 cmap->get_ranges = opentype_cmap_dummy_get_ranges;
1863 void dwrite_cmap_release(struct dwrite_cmap *cmap)
1865 if (cmap->stream)
1867 IDWriteFontFileStream_ReleaseFileFragment(cmap->stream, cmap->table_context);
1868 IDWriteFontFileStream_Release(cmap->stream);
1870 cmap->data = NULL;
1871 cmap->stream = NULL;
1874 HRESULT opentype_cmap_get_unicode_ranges(const struct dwrite_cmap *cmap, unsigned int max_count, DWRITE_UNICODE_RANGE *ranges,
1875 unsigned int *count)
1877 if (!cmap->data)
1878 return E_FAIL;
1880 *count = cmap->get_ranges(cmap, max_count, ranges);
1882 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
1885 void opentype_get_font_typo_metrics(struct file_stream_desc *stream_desc, unsigned int *ascent, unsigned int *descent)
1887 struct dwrite_fonttable os2;
1889 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1891 *ascent = *descent = 0;
1893 if (os2.size >= FIELD_OFFSET(struct tt_os2, sTypoLineGap))
1895 SHORT value = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoDescender));
1896 *ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoAscender));
1897 *descent = value < 0 ? -value : 0;
1900 if (os2.data)
1901 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
1904 void opentype_get_font_metrics(struct file_stream_desc *stream_desc, DWRITE_FONT_METRICS1 *metrics, DWRITE_CARET_METRICS *caret)
1906 struct dwrite_fonttable os2, head, post, hhea;
1908 memset(metrics, 0, sizeof(*metrics));
1910 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1911 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
1912 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
1913 opentype_get_font_table(stream_desc, MS_HHEA_TAG, &hhea);
1915 if (head.data)
1917 metrics->designUnitsPerEm = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, unitsPerEm));
1918 metrics->glyphBoxLeft = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, xMin));
1919 metrics->glyphBoxTop = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, yMax));
1920 metrics->glyphBoxRight = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, xMax));
1921 metrics->glyphBoxBottom = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, yMin));
1924 if (caret)
1926 if (hhea.data)
1928 caret->slopeRise = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretSlopeRise));
1929 caret->slopeRun = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretSlopeRun));
1930 caret->offset = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretOffset));
1932 else
1933 memset(caret, 0, sizeof(*caret));
1936 if (os2.data)
1938 USHORT version = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, version));
1940 metrics->ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWinAscent));
1941 /* Some fonts have usWinDescent value stored as signed short, which could be wrongly
1942 interpreted as large unsigned value. */
1943 metrics->descent = abs((SHORT)table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWinDescent)));
1945 /* Line gap is estimated using two sets of ascender/descender values and 'hhea' line gap. */
1946 if (hhea.data)
1948 SHORT descender = (SHORT)table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, descender));
1949 INT32 linegap;
1951 linegap = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, ascender)) + abs(descender) +
1952 table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, linegap)) - metrics->ascent - metrics->descent;
1953 metrics->lineGap = linegap > 0 ? linegap : 0;
1956 metrics->strikethroughPosition = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, yStrikeoutPosition));
1957 metrics->strikethroughThickness = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, yStrikeoutSize));
1958 metrics->subscriptPositionX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptXOffset));
1959 /* Y offset is stored as positive offset below baseline */
1960 metrics->subscriptPositionY = -table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptYOffset));
1961 metrics->subscriptSizeX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptXSize));
1962 metrics->subscriptSizeY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptYSize));
1963 metrics->superscriptPositionX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptXOffset));
1964 metrics->superscriptPositionY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptYOffset));
1965 metrics->superscriptSizeX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptXSize));
1966 metrics->superscriptSizeY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptYSize));
1968 /* version 2 fields */
1969 if (version >= 2)
1971 metrics->capHeight = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sCapHeight));
1972 metrics->xHeight = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sxHeight));
1975 if (table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) & OS2_FSSELECTION_USE_TYPO_METRICS)
1977 SHORT descent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoDescender));
1978 metrics->ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoAscender));
1979 metrics->descent = descent < 0 ? -descent : 0;
1980 metrics->lineGap = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoLineGap));
1981 metrics->hasTypographicMetrics = TRUE;
1984 else
1986 metrics->strikethroughPosition = metrics->designUnitsPerEm / 3;
1987 if (hhea.data)
1989 metrics->ascent = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, ascender));
1990 metrics->descent = abs((SHORT)table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, descender)));
1994 if (post.data)
1996 metrics->underlinePosition = table_read_be_word(&post, FIELD_OFFSET(struct tt_post, underlinePosition));
1997 metrics->underlineThickness = table_read_be_word(&post, FIELD_OFFSET(struct tt_post, underlineThickness));
2000 if (metrics->underlineThickness == 0)
2001 metrics->underlineThickness = metrics->designUnitsPerEm / 14;
2002 if (metrics->strikethroughThickness == 0)
2003 metrics->strikethroughThickness = metrics->underlineThickness;
2005 /* estimate missing metrics */
2006 if (metrics->xHeight == 0)
2007 metrics->xHeight = metrics->designUnitsPerEm / 2;
2008 if (metrics->capHeight == 0)
2009 metrics->capHeight = metrics->designUnitsPerEm * 7 / 10;
2011 if (os2.data)
2012 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2013 if (head.data)
2014 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2015 if (post.data)
2016 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2017 if (hhea.data)
2018 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, hhea.context);
2021 void opentype_get_font_properties(struct file_stream_desc *stream_desc, struct dwrite_font_props *props)
2023 struct dwrite_fonttable os2, head, colr, cpal;
2024 BOOL is_symbol, is_monospaced;
2026 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2027 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
2029 /* default stretch, weight and style to normal */
2030 props->stretch = DWRITE_FONT_STRETCH_NORMAL;
2031 props->weight = DWRITE_FONT_WEIGHT_NORMAL;
2032 props->style = DWRITE_FONT_STYLE_NORMAL;
2033 memset(&props->panose, 0, sizeof(props->panose));
2034 memset(&props->fontsig, 0, sizeof(props->fontsig));
2035 memset(&props->lf, 0, sizeof(props->lf));
2036 props->flags = 0;
2038 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
2039 if (os2.data)
2041 USHORT version = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, version));
2042 USHORT fsSelection = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection));
2043 USHORT usWeightClass = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWeightClass));
2044 USHORT usWidthClass = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWidthClass));
2045 const void *panose;
2047 if (usWidthClass > DWRITE_FONT_STRETCH_UNDEFINED && usWidthClass <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
2048 props->stretch = usWidthClass;
2050 if (usWeightClass >= 1 && usWeightClass <= 9)
2051 usWeightClass *= 100;
2053 if (usWeightClass > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
2054 props->weight = DWRITE_FONT_WEIGHT_ULTRA_BLACK;
2055 else if (usWeightClass > 0)
2056 props->weight = usWeightClass;
2058 if (version >= 4 && (fsSelection & OS2_FSSELECTION_OBLIQUE))
2059 props->style = DWRITE_FONT_STYLE_OBLIQUE;
2060 else if (fsSelection & OS2_FSSELECTION_ITALIC)
2061 props->style = DWRITE_FONT_STYLE_ITALIC;
2062 props->lf.lfItalic = !!(fsSelection & OS2_FSSELECTION_ITALIC);
2064 if ((panose = table_read_ensure(&os2, FIELD_OFFSET(struct tt_os2, panose), sizeof(props->panose))))
2065 memcpy(&props->panose, panose, sizeof(props->panose));
2067 /* FONTSIGNATURE */
2068 props->fontsig.fsUsb[0] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange1));
2069 props->fontsig.fsUsb[1] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange2));
2070 props->fontsig.fsUsb[2] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange3));
2071 props->fontsig.fsUsb[3] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange4));
2073 if (version)
2075 props->fontsig.fsCsb[0] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulCodePageRange1));
2076 props->fontsig.fsCsb[1] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulCodePageRange2));
2079 else if (head.data)
2081 USHORT macStyle = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, macStyle));
2083 if (macStyle & TT_HEAD_MACSTYLE_CONDENSED)
2084 props->stretch = DWRITE_FONT_STRETCH_CONDENSED;
2085 else if (macStyle & TT_HEAD_MACSTYLE_EXTENDED)
2086 props->stretch = DWRITE_FONT_STRETCH_EXPANDED;
2088 if (macStyle & TT_HEAD_MACSTYLE_BOLD)
2089 props->weight = DWRITE_FONT_WEIGHT_BOLD;
2091 if (macStyle & TT_HEAD_MACSTYLE_ITALIC) {
2092 props->style = DWRITE_FONT_STYLE_ITALIC;
2093 props->lf.lfItalic = 1;
2097 props->lf.lfWeight = props->weight;
2099 /* FONT_IS_SYMBOL */
2100 if (!(is_symbol = props->panose.familyKind == DWRITE_PANOSE_FAMILY_SYMBOL))
2102 struct dwrite_fonttable cmap;
2103 int i, offset, num_tables;
2105 opentype_get_font_table(stream_desc, MS_CMAP_TAG, &cmap);
2107 if (cmap.data)
2109 num_tables = table_read_be_word(&cmap, FIELD_OFFSET(struct cmap_header, num_tables));
2110 offset = FIELD_OFFSET(struct cmap_header, tables);
2112 for (i = 0; !is_symbol && i < num_tables; ++i)
2114 WORD platform, encoding;
2116 platform = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2117 FIELD_OFFSET(struct cmap_encoding_record, platformID));
2118 encoding = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2119 FIELD_OFFSET(struct cmap_encoding_record, encodingID));
2121 is_symbol = platform == OPENTYPE_CMAP_TABLE_PLATFORM_WIN &&
2122 encoding == OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL;
2125 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cmap.context);
2128 if (is_symbol)
2129 props->flags |= FONT_IS_SYMBOL;
2131 /* FONT_IS_MONOSPACED */
2132 if (!(is_monospaced = props->panose.text.proportion == DWRITE_PANOSE_PROPORTION_MONOSPACED))
2134 struct dwrite_fonttable post;
2136 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
2138 if (post.data)
2140 is_monospaced = !!table_read_dword(&post, FIELD_OFFSET(struct tt_post, fixed_pitch));
2142 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2145 if (is_monospaced)
2146 props->flags |= FONT_IS_MONOSPACED;
2148 /* FONT_IS_COLORED */
2149 opentype_get_font_table(stream_desc, MS_COLR_TAG, &colr);
2150 if (colr.data)
2152 opentype_get_font_table(stream_desc, MS_CPAL_TAG, &cpal);
2153 if (cpal.data)
2155 props->flags |= FONT_IS_COLORED;
2156 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cpal.context);
2159 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, colr.context);
2162 TRACE("stretch %d, weight %d, style %d\n", props->stretch, props->weight, props->style);
2164 if (os2.data)
2165 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2166 if (head.data)
2167 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2170 static UINT get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform, USHORT encoding)
2172 UINT codepage = 0;
2174 switch (platform) {
2175 case OPENTYPE_PLATFORM_UNICODE:
2176 break;
2177 case OPENTYPE_PLATFORM_MAC:
2178 switch (encoding)
2180 case TT_NAME_MAC_ENCODING_ROMAN:
2181 codepage = 10000;
2182 break;
2183 case TT_NAME_MAC_ENCODING_JAPANESE:
2184 codepage = 10001;
2185 break;
2186 case TT_NAME_MAC_ENCODING_TRAD_CHINESE:
2187 codepage = 10002;
2188 break;
2189 case TT_NAME_MAC_ENCODING_KOREAN:
2190 codepage = 10003;
2191 break;
2192 case TT_NAME_MAC_ENCODING_ARABIC:
2193 codepage = 10004;
2194 break;
2195 case TT_NAME_MAC_ENCODING_HEBREW:
2196 codepage = 10005;
2197 break;
2198 case TT_NAME_MAC_ENCODING_GREEK:
2199 codepage = 10006;
2200 break;
2201 case TT_NAME_MAC_ENCODING_RUSSIAN:
2202 codepage = 10007;
2203 break;
2204 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE:
2205 codepage = 10008;
2206 break;
2207 case TT_NAME_MAC_ENCODING_THAI:
2208 codepage = 10021;
2209 break;
2210 default:
2211 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2212 break;
2214 break;
2215 case OPENTYPE_PLATFORM_WIN:
2216 switch (encoding)
2218 case TT_NAME_WINDOWS_ENCODING_SYMBOL:
2219 case TT_NAME_WINDOWS_ENCODING_UNICODE_BMP:
2220 case TT_NAME_WINDOWS_ENCODING_UNICODE_FULL:
2221 break;
2222 case TT_NAME_WINDOWS_ENCODING_SJIS:
2223 codepage = 932;
2224 break;
2225 case TT_NAME_WINDOWS_ENCODING_PRC:
2226 codepage = 936;
2227 break;
2228 case TT_NAME_WINDOWS_ENCODING_BIG5:
2229 codepage = 950;
2230 break;
2231 case TT_NAME_WINDOWS_ENCODING_WANSUNG:
2232 codepage = 20949;
2233 break;
2234 case TT_NAME_WINDOWS_ENCODING_JOHAB:
2235 codepage = 1361;
2236 break;
2237 default:
2238 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2239 break;
2241 break;
2242 default:
2243 FIXME("unknown platform %d\n", platform);
2246 return codepage;
2249 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform, USHORT lang_id, WCHAR *locale, USHORT locale_len)
2251 static const WCHAR enusW[] = {'e','n','-','U','S',0};
2253 switch (platform) {
2254 case OPENTYPE_PLATFORM_MAC:
2256 const char *locale_name = NULL;
2258 if (lang_id > TT_NAME_MAC_LANGID_AZER_ROMAN)
2259 WARN("invalid mac lang id %d\n", lang_id);
2260 else if (!name_mac_langid_to_locale[lang_id][0])
2261 FIXME("failed to map mac lang id %d to locale name\n", lang_id);
2262 else
2263 locale_name = name_mac_langid_to_locale[lang_id];
2265 if (locale_name)
2266 MultiByteToWideChar(CP_ACP, 0, name_mac_langid_to_locale[lang_id], -1, locale, locale_len);
2267 else
2268 strcpyW(locale, enusW);
2269 break;
2271 case OPENTYPE_PLATFORM_WIN:
2272 if (!LCIDToLocaleName(MAKELCID(lang_id, SORT_DEFAULT), locale, locale_len, 0)) {
2273 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id, SORT_DEFAULT));
2274 strcpyW(locale, enusW);
2276 break;
2277 case OPENTYPE_PLATFORM_UNICODE:
2278 strcpyW(locale, enusW);
2279 break;
2280 default:
2281 FIXME("unknown platform %d\n", platform);
2285 static BOOL opentype_decode_namerecord(const struct dwrite_fonttable *table, unsigned int idx,
2286 IDWriteLocalizedStrings *strings)
2288 USHORT lang_id, length, offset, encoding, platform;
2289 const struct name_header *header = (const struct name_header *)table->data;
2290 const struct name_record *record;
2291 unsigned int i, string_offset;
2292 BOOL ret = FALSE;
2293 const void *name;
2295 string_offset = table_read_be_word(table, FIELD_OFFSET(struct name_header, stringOffset));
2297 record = &header->records[idx];
2299 platform = GET_BE_WORD(record->platformID);
2300 lang_id = GET_BE_WORD(record->languageID);
2301 length = GET_BE_WORD(record->length);
2302 offset = GET_BE_WORD(record->offset);
2303 encoding = GET_BE_WORD(record->encodingID);
2305 if (!(name = table_read_ensure(table, string_offset + offset, length)))
2306 return FALSE;
2308 if (lang_id < 0x8000)
2310 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
2311 WCHAR *name_string;
2312 UINT codepage;
2314 codepage = get_name_record_codepage(platform, encoding);
2315 get_name_record_locale(platform, lang_id, locale, ARRAY_SIZE(locale));
2317 if (codepage)
2319 DWORD len = MultiByteToWideChar(codepage, 0, name, length, NULL, 0);
2320 name_string = heap_alloc(sizeof(WCHAR) * (len+1));
2321 MultiByteToWideChar(codepage, 0, name, length, name_string, len);
2322 name_string[len] = 0;
2324 else
2326 length /= sizeof(WCHAR);
2327 name_string = heap_strdupnW(name, length);
2328 for (i = 0; i < length; i++)
2329 name_string[i] = GET_BE_WORD(name_string[i]);
2332 TRACE("string %s for locale %s found\n", debugstr_w(name_string), debugstr_w(locale));
2333 add_localizedstring(strings, locale, name_string);
2334 heap_free(name_string);
2335 ret = TRUE;
2337 else
2338 FIXME("handle NAME format 1\n");
2340 return ret;
2343 static HRESULT opentype_get_font_strings_from_id(const struct dwrite_fonttable *table, enum OPENTYPE_STRING_ID id,
2344 IDWriteLocalizedStrings **strings)
2346 int i, count, candidate_mac, candidate_unicode;
2347 const struct name_record *records;
2348 WORD format;
2349 BOOL exists;
2350 HRESULT hr;
2352 if (!table->data)
2353 return E_FAIL;
2355 if (FAILED(hr = create_localizedstrings(strings)))
2356 return hr;
2358 format = table_read_be_word(table, FIELD_OFFSET(struct name_header, format));
2360 if (format != 0 && format != 1)
2361 FIXME("unsupported NAME format %d\n", format);
2363 count = table_read_be_word(table, FIELD_OFFSET(struct name_header, count));
2365 if (!(records = table_read_ensure(table, FIELD_OFFSET(struct name_header, records),
2366 count * sizeof(struct name_record))))
2368 count = 0;
2371 exists = FALSE;
2372 candidate_unicode = candidate_mac = -1;
2374 for (i = 0; i < count; i++)
2376 unsigned short platform;
2378 if (GET_BE_WORD(records[i].nameID) != id)
2379 continue;
2381 platform = GET_BE_WORD(records[i].platformID);
2382 switch (platform)
2384 /* Skip Unicode or Mac entries for now, fonts tend to duplicate those
2385 strings as WIN platform entries. If font does not have WIN entry for
2386 this id, we will use Mac or Unicode platform entry while assuming
2387 en-US locale. */
2388 case OPENTYPE_PLATFORM_UNICODE:
2389 if (candidate_unicode == -1)
2390 candidate_unicode = i;
2391 break;
2392 case OPENTYPE_PLATFORM_MAC:
2393 if (candidate_mac == -1)
2394 candidate_mac = i;
2395 break;
2396 case OPENTYPE_PLATFORM_WIN:
2397 if (opentype_decode_namerecord(table, i, *strings))
2398 exists = TRUE;
2399 break;
2400 default:
2401 FIXME("platform %i not supported\n", platform);
2402 break;
2406 if (!exists)
2408 if (candidate_mac != -1)
2409 exists = opentype_decode_namerecord(table, candidate_mac, *strings);
2410 if (!exists && candidate_unicode != -1)
2411 exists = opentype_decode_namerecord(table, candidate_unicode, *strings);
2413 if (!exists)
2415 IDWriteLocalizedStrings_Release(*strings);
2416 *strings = NULL;
2420 if (*strings)
2421 sort_localizedstrings(*strings);
2423 return exists ? S_OK : E_FAIL;
2426 static WCHAR *meta_get_lng_name(WCHAR *str, WCHAR **ctx)
2428 static const WCHAR delimW[] = {',',' ',0};
2429 WCHAR *ret;
2431 if (!str) str = *ctx;
2432 while (*str && strchrW(delimW, *str)) str++;
2433 if (!*str) return NULL;
2434 ret = str++;
2435 while (*str && !strchrW(delimW, *str)) str++;
2436 if (*str) *str++ = 0;
2437 *ctx = str;
2439 return ret;
2442 static HRESULT opentype_get_font_strings_from_meta(const struct file_stream_desc *stream_desc,
2443 DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **ret)
2445 static const WCHAR emptyW[] = { 0 };
2446 const struct meta_data_map *maps;
2447 IDWriteLocalizedStrings *strings;
2448 struct dwrite_fonttable meta;
2449 DWORD version, i, count, tag;
2450 HRESULT hr;
2452 *ret = NULL;
2454 switch (id)
2456 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2457 tag = MS_DLNG_TAG;
2458 break;
2459 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2460 tag = MS_SLNG_TAG;
2461 break;
2462 default:
2463 WARN("Unexpected id %d.\n", id);
2464 return S_OK;
2467 if (FAILED(hr = create_localizedstrings(&strings)))
2468 return hr;
2470 opentype_get_font_table(stream_desc, MS_META_TAG, &meta);
2472 if (meta.data)
2474 version = table_read_be_dword(&meta, 0);
2475 if (version != 1)
2477 WARN("Unexpected meta table version %d.\n", version);
2478 goto end;
2481 count = table_read_be_dword(&meta, FIELD_OFFSET(struct meta_header, data_maps_count));
2482 if (!(maps = table_read_ensure(&meta, FIELD_OFFSET(struct meta_header, maps),
2483 count * sizeof(struct meta_data_map))))
2484 goto end;
2486 for (i = 0; i < count; ++i)
2488 const char *data;
2490 if (maps[i].tag == tag && maps[i].length)
2492 DWORD length = GET_BE_DWORD(maps[i].length), j;
2494 if ((data = table_read_ensure(&meta, GET_BE_DWORD(maps[i].offset), length)))
2496 WCHAR *ptrW = heap_alloc((length + 1) * sizeof(WCHAR)), *ctx, *token;
2498 if (!ptrW)
2500 hr = E_OUTOFMEMORY;
2501 goto end;
2504 /* Data is stored in comma separated list, ASCII range only. */
2505 for (j = 0; j < length; ++j)
2506 ptrW[j] = data[j];
2507 ptrW[length] = 0;
2509 token = meta_get_lng_name(ptrW, &ctx);
2511 while (token)
2513 add_localizedstring(strings, emptyW, token);
2514 token = meta_get_lng_name(NULL, &ctx);
2517 heap_free(ptrW);
2521 end:
2522 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, meta.context);
2525 if (IDWriteLocalizedStrings_GetCount(strings))
2526 *ret = strings;
2527 else
2528 IDWriteLocalizedStrings_Release(strings);
2530 return hr;
2533 HRESULT opentype_get_font_info_strings(const struct file_stream_desc *stream_desc, DWRITE_INFORMATIONAL_STRING_ID id,
2534 IDWriteLocalizedStrings **strings)
2536 struct dwrite_fonttable name;
2538 switch (id)
2540 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2541 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2542 opentype_get_font_strings_from_meta(stream_desc, id, strings);
2543 break;
2544 default:
2545 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2546 opentype_get_font_strings_from_id(&name, dwriteid_to_opentypeid[id], strings);
2547 if (name.context)
2548 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2551 return S_OK;
2554 /* FamilyName locating order is WWS Family Name -> Preferred Family Name -> Family Name. If font claims to
2555 have 'Preferred Family Name' in WWS format, then WWS name is not used. */
2556 HRESULT opentype_get_font_familyname(struct file_stream_desc *stream_desc, IDWriteLocalizedStrings **names)
2558 struct dwrite_fonttable os2, name;
2559 UINT16 fsselection;
2560 HRESULT hr;
2562 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2563 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2565 *names = NULL;
2567 /* If Preferred Family doesn't conform to WWS model try WWS name. */
2568 fsselection = os2.data ? table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) : 0;
2569 if (os2.data && !(fsselection & OS2_FSSELECTION_WWS))
2570 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_WWS_FAMILY_NAME, names);
2571 else
2572 hr = E_FAIL;
2574 if (FAILED(hr))
2575 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME, names);
2576 if (FAILED(hr))
2577 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_FAMILY_NAME, names);
2579 if (os2.context)
2580 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2581 if (name.context)
2582 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2584 return hr;
2587 /* FaceName locating order is WWS Face Name -> Preferred Face Name -> Face Name. If font claims to
2588 have 'Preferred Face Name' in WWS format, then WWS name is not used. */
2589 HRESULT opentype_get_font_facename(struct file_stream_desc *stream_desc, WCHAR *lfname, IDWriteLocalizedStrings **names)
2591 struct dwrite_fonttable os2, name;
2592 IDWriteLocalizedStrings *lfnames;
2593 UINT16 fsselection;
2594 HRESULT hr;
2596 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2597 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2599 *names = NULL;
2601 /* if Preferred Family doesn't conform to WWS model try WWS name */
2602 fsselection = os2.data ? table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) : 0;
2603 if (os2.data && !(fsselection & OS2_FSSELECTION_WWS))
2604 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_WWS_SUBFAMILY_NAME, names);
2605 else
2606 hr = E_FAIL;
2608 if (FAILED(hr))
2609 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME, names);
2610 if (FAILED(hr))
2611 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_SUBFAMILY_NAME, names);
2613 /* User locale is preferred, with fallback to en-us. */
2614 *lfname = 0;
2615 if (SUCCEEDED(opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_FAMILY_NAME, &lfnames)))
2617 static const WCHAR enusW[] = {'e','n','-','u','s',0};
2618 WCHAR localeW[LOCALE_NAME_MAX_LENGTH];
2619 UINT32 index;
2620 BOOL exists;
2622 exists = FALSE;
2623 if (GetSystemDefaultLocaleName(localeW, ARRAY_SIZE(localeW)))
2624 IDWriteLocalizedStrings_FindLocaleName(lfnames, localeW, &index, &exists);
2626 if (!exists)
2627 IDWriteLocalizedStrings_FindLocaleName(lfnames, enusW, &index, &exists);
2629 if (exists) {
2630 UINT32 length = 0;
2631 WCHAR *nameW;
2633 IDWriteLocalizedStrings_GetStringLength(lfnames, index, &length);
2634 nameW = heap_alloc((length + 1) * sizeof(WCHAR));
2635 if (nameW) {
2636 *nameW = 0;
2637 IDWriteLocalizedStrings_GetString(lfnames, index, nameW, length + 1);
2638 lstrcpynW(lfname, nameW, LF_FACESIZE);
2639 heap_free(nameW);
2643 IDWriteLocalizedStrings_Release(lfnames);
2646 if (os2.context)
2647 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2648 if (name.context)
2649 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2651 return hr;
2654 static const struct ot_langsys *opentype_get_langsys(const struct ot_gsubgpos_table *table, unsigned int script_index,
2655 unsigned int language_index, unsigned int *feature_count)
2657 unsigned int table_offset, langsys_offset;
2658 const struct ot_langsys *langsys = NULL;
2660 *feature_count = 0;
2662 if (!table->table.data || script_index == ~0u)
2663 return NULL;
2665 /* ScriptTable offset. */
2666 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
2667 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
2668 if (!table_offset)
2669 return NULL;
2671 if (language_index == ~0u)
2672 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
2673 else
2674 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
2675 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
2676 FIELD_OFFSET(struct ot_langsys_record, langsys));
2677 langsys_offset += table->script_list + table_offset;
2679 *feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
2680 if (*feature_count)
2681 langsys = table_read_ensure(&table->table, langsys_offset, FIELD_OFFSET(struct ot_langsys, feature_index[*feature_count]));
2682 if (!langsys)
2683 *feature_count = 0;
2685 return langsys;
2688 void opentype_get_typographic_features(struct ot_gsubgpos_table *table, unsigned int script_index,
2689 unsigned int language_index, struct tag_array *t)
2691 unsigned int i, total_feature_count, script_feature_count;
2692 const struct ot_feature_list *feature_list;
2693 const struct ot_langsys *langsys = NULL;
2695 langsys = opentype_get_langsys(table, script_index, language_index, &script_feature_count);
2697 total_feature_count = table_read_be_word(&table->table, table->feature_list);
2698 if (!total_feature_count)
2699 return;
2701 feature_list = table_read_ensure(&table->table, table->feature_list,
2702 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
2703 if (!feature_list)
2704 return;
2706 for (i = 0; i < script_feature_count; ++i)
2708 unsigned int feature_index = GET_BE_WORD(langsys->feature_index[i]);
2709 if (feature_index >= total_feature_count)
2710 continue;
2712 if (!dwrite_array_reserve((void **)&t->tags, &t->capacity, t->count + 1, sizeof(*t->tags)))
2713 return;
2715 t->tags[t->count++] = feature_list->features[feature_index].tag;
2719 static unsigned int find_vdmx_group(const struct vdmx_header *hdr)
2721 WORD num_ratios, i;
2722 const struct vdmx_ratio *ratios = (struct vdmx_ratio *)(hdr + 1);
2723 BYTE dev_x_ratio = 1, dev_y_ratio = 1;
2724 unsigned int group_offset = 0;
2726 num_ratios = GET_BE_WORD(hdr->num_ratios);
2728 for (i = 0; i < num_ratios; i++) {
2730 if (!ratios[i].bCharSet) continue;
2732 if ((ratios[i].xRatio == 0 && ratios[i].yStartRatio == 0 &&
2733 ratios[i].yEndRatio == 0) ||
2734 (ratios[i].xRatio == dev_x_ratio && ratios[i].yStartRatio <= dev_y_ratio &&
2735 ratios[i].yEndRatio >= dev_y_ratio))
2737 group_offset = GET_BE_WORD(*((WORD *)(ratios + num_ratios) + i));
2738 break;
2742 return group_offset;
2745 BOOL opentype_get_vdmx_size(const struct dwrite_fonttable *vdmx, INT emsize, UINT16 *ascent, UINT16 *descent)
2747 unsigned int num_ratios, num_recs, group_offset, i;
2748 const struct vdmx_header *header;
2749 const struct vdmx_group *group;
2751 if (!vdmx->exists)
2752 return FALSE;
2754 num_ratios = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_ratios));
2755 num_recs = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_recs));
2757 header = table_read_ensure(vdmx, 0, sizeof(*header) + num_ratios * sizeof(struct vdmx_ratio) +
2758 num_recs * sizeof(*group));
2760 if (!header)
2761 return FALSE;
2763 group_offset = find_vdmx_group(header);
2764 if (!group_offset)
2765 return FALSE;
2767 num_recs = table_read_be_word(vdmx, group_offset);
2768 group = table_read_ensure(vdmx, group_offset, FIELD_OFFSET(struct vdmx_group, entries[num_recs]));
2770 if (!group)
2771 return FALSE;
2773 if (emsize < group->startsz || emsize >= group->endsz)
2774 return FALSE;
2776 for (i = 0; i < num_recs; ++i)
2778 WORD ppem = GET_BE_WORD(group->entries[i].yPelHeight);
2779 if (ppem > emsize) {
2780 FIXME("interpolate %d\n", emsize);
2781 return FALSE;
2784 if (ppem == emsize) {
2785 *ascent = (SHORT)GET_BE_WORD(group->entries[i].yMax);
2786 *descent = -(SHORT)GET_BE_WORD(group->entries[i].yMin);
2787 return TRUE;
2791 return FALSE;
2794 unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable *gasp, float emsize)
2796 unsigned int version, num_ranges, i;
2797 const struct gasp_header *table;
2798 WORD flags = 0;
2800 if (!gasp->exists)
2801 return 0;
2803 num_ranges = table_read_be_word(gasp, FIELD_OFFSET(struct gasp_header, num_ranges));
2805 table = table_read_ensure(gasp, 0, FIELD_OFFSET(struct gasp_header, ranges[num_ranges]));
2806 if (!table)
2807 return 0;
2809 version = GET_BE_WORD(table->version);
2810 if (version > 1)
2812 ERR("Unsupported gasp table format version %u.\n", version);
2813 goto done;
2816 for (i = 0; i < num_ranges; ++i)
2818 flags = GET_BE_WORD(table->ranges[i].flags);
2819 if (emsize <= GET_BE_WORD(table->ranges[i].max_ppem)) break;
2822 done:
2823 return flags;
2826 unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable *cpal)
2828 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palettes));
2831 unsigned int opentype_get_cpal_paletteentrycount(const struct dwrite_fonttable *cpal)
2833 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palette_entries));
2836 HRESULT opentype_get_cpal_entries(const struct dwrite_fonttable *cpal, unsigned int palette,
2837 unsigned int first_entry_index, unsigned int entry_count, DWRITE_COLOR_F *entries)
2839 unsigned int num_palettes, num_palette_entries, i;
2840 const struct cpal_color_record *records;
2841 const struct cpal_header_0 *header;
2843 header = table_read_ensure(cpal, 0, sizeof(*header));
2845 if (!cpal->exists || !header)
2846 return DWRITE_E_NOCOLOR;
2848 num_palettes = GET_BE_WORD(header->num_palettes);
2849 if (palette >= num_palettes)
2850 return DWRITE_E_NOCOLOR;
2852 header = table_read_ensure(cpal, 0, FIELD_OFFSET(struct cpal_header_0, color_record_indices[palette]));
2853 if (!header)
2854 return DWRITE_E_NOCOLOR;
2856 num_palette_entries = GET_BE_WORD(header->num_palette_entries);
2857 if (first_entry_index + entry_count > num_palette_entries)
2858 return E_INVALIDARG;
2860 records = table_read_ensure(cpal, GET_BE_DWORD(header->offset_first_color_record),
2861 sizeof(*records) * GET_BE_WORD(header->num_color_records));
2862 if (!records)
2863 return DWRITE_E_NOCOLOR;
2865 first_entry_index += GET_BE_WORD(header->color_record_indices[palette]);
2867 for (i = 0; i < entry_count; i++) {
2868 entries[i].u1.r = records[first_entry_index + i].red / 255.0f;
2869 entries[i].u2.g = records[first_entry_index + i].green / 255.0f;
2870 entries[i].u3.b = records[first_entry_index + i].blue / 255.0f;
2871 entries[i].u4.a = records[first_entry_index + i].alpha / 255.0f;
2874 return S_OK;
2877 static int colr_compare_gid(const void *g, const void *r)
2879 const struct colr_baseglyph_record *record = r;
2880 UINT16 glyph = *(UINT16*)g, GID = GET_BE_WORD(record->glyph);
2881 int ret = 0;
2883 if (glyph > GID)
2884 ret = 1;
2885 else if (glyph < GID)
2886 ret = -1;
2888 return ret;
2891 HRESULT opentype_get_colr_glyph(const struct dwrite_fonttable *colr, UINT16 glyph, struct dwrite_colorglyph *ret)
2893 unsigned int num_baseglyph_records, offset_baseglyph_records;
2894 const struct colr_baseglyph_record *record;
2895 const struct colr_layer_record *layer;
2896 const struct colr_header *header;
2898 memset(ret, 0, sizeof(*ret));
2899 ret->glyph = glyph;
2900 ret->palette_index = 0xffff;
2902 header = table_read_ensure(colr, 0, sizeof(*header));
2903 if (!header)
2904 return S_FALSE;
2906 num_baseglyph_records = GET_BE_WORD(header->num_baseglyph_records);
2907 offset_baseglyph_records = GET_BE_DWORD(header->offset_baseglyph_records);
2908 if (!table_read_ensure(colr, offset_baseglyph_records, num_baseglyph_records * sizeof(*record)))
2910 return S_FALSE;
2913 record = bsearch(&glyph, colr->data + offset_baseglyph_records, num_baseglyph_records,
2914 sizeof(*record), colr_compare_gid);
2915 if (!record)
2916 return S_FALSE;
2918 ret->first_layer = GET_BE_WORD(record->first_layer_index);
2919 ret->num_layers = GET_BE_WORD(record->num_layers);
2921 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
2922 (ret->first_layer + ret->layer) * sizeof(*layer))))
2924 layer += ret->first_layer + ret->layer;
2925 ret->glyph = GET_BE_WORD(layer->glyph);
2926 ret->palette_index = GET_BE_WORD(layer->palette_index);
2929 return S_OK;
2932 void opentype_colr_next_glyph(const struct dwrite_fonttable *colr, struct dwrite_colorglyph *glyph)
2934 const struct colr_layer_record *layer;
2935 const struct colr_header *header;
2937 /* iterated all the way through */
2938 if (glyph->layer == glyph->num_layers)
2939 return;
2941 if (!(header = table_read_ensure(colr, 0, sizeof(*header))))
2942 return;
2944 glyph->layer++;
2946 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
2947 (glyph->first_layer + glyph->layer) * sizeof(*layer))))
2949 layer += glyph->first_layer + glyph->layer;
2950 glyph->glyph = GET_BE_WORD(layer->glyph);
2951 glyph->palette_index = GET_BE_WORD(layer->palette_index);
2955 static BOOL opentype_has_font_table(IDWriteFontFace5 *fontface, UINT32 tag)
2957 BOOL exists = FALSE;
2958 const void *data;
2959 void *context;
2960 UINT32 size;
2961 HRESULT hr;
2963 hr = IDWriteFontFace5_TryGetFontTable(fontface, tag, &data, &size, &context, &exists);
2964 if (FAILED(hr))
2965 return FALSE;
2967 if (exists)
2968 IDWriteFontFace5_ReleaseFontTable(fontface, context);
2970 return exists;
2973 static unsigned int opentype_get_sbix_formats(IDWriteFontFace5 *fontface)
2975 unsigned int num_strikes, num_glyphs, i, j, ret = 0;
2976 const struct sbix_header *sbix_header;
2977 struct dwrite_fonttable table;
2979 memset(&table, 0, sizeof(table));
2980 table.exists = TRUE;
2982 if (!get_fontface_table(fontface, MS_MAXP_TAG, &table))
2983 return 0;
2985 num_glyphs = table_read_be_word(&table, FIELD_OFFSET(struct maxp, num_glyphs));
2987 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
2989 memset(&table, 0, sizeof(table));
2990 table.exists = TRUE;
2992 if (!get_fontface_table(fontface, MS_SBIX_TAG, &table))
2993 return 0;
2995 num_strikes = table_read_be_dword(&table, FIELD_OFFSET(struct sbix_header, num_strikes));
2996 sbix_header = table_read_ensure(&table, 0, FIELD_OFFSET(struct sbix_header, strike_offset[num_strikes]));
2998 if (sbix_header)
3000 for (i = 0; i < num_strikes; ++i)
3002 unsigned int strike_offset = GET_BE_DWORD(sbix_header->strike_offset[i]);
3003 const struct sbix_strike *strike = table_read_ensure(&table, strike_offset,
3004 FIELD_OFFSET(struct sbix_strike, glyphdata_offsets[num_glyphs + 1]));
3006 if (!strike)
3007 continue;
3009 for (j = 0; j < num_glyphs; j++)
3011 unsigned int offset = GET_BE_DWORD(strike->glyphdata_offsets[j]);
3012 unsigned int next_offset = GET_BE_DWORD(strike->glyphdata_offsets[j + 1]);
3013 const struct sbix_glyph_data *glyph_data;
3015 if (offset == next_offset)
3016 continue;
3018 glyph_data = table_read_ensure(&table, strike_offset + offset, sizeof(*glyph_data));
3019 if (!glyph_data)
3020 continue;
3022 switch (glyph_data->graphic_type)
3024 case MS_PNG__TAG:
3025 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3026 break;
3027 case MS_JPG__TAG:
3028 ret |= DWRITE_GLYPH_IMAGE_FORMATS_JPEG;
3029 break;
3030 case MS_TIFF_TAG:
3031 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TIFF;
3032 break;
3033 default:
3034 FIXME("unexpected bitmap format %s\n", debugstr_tag(GET_BE_DWORD(glyph_data->graphic_type)));
3040 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
3042 return ret;
3045 static unsigned int opentype_get_cblc_formats(IDWriteFontFace5 *fontface)
3047 const unsigned int format_mask = DWRITE_GLYPH_IMAGE_FORMATS_PNG |
3048 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3049 const struct cblc_bitmapsize_table *sizes;
3050 struct dwrite_fonttable cblc = { 0 };
3051 unsigned int num_sizes, i, ret = 0;
3052 const struct cblc_header *header;
3054 cblc.exists = TRUE;
3055 if (!get_fontface_table(fontface, MS_CBLC_TAG, &cblc))
3056 return 0;
3058 num_sizes = table_read_be_dword(&cblc, FIELD_OFFSET(struct cblc_header, num_sizes));
3059 sizes = table_read_ensure(&cblc, sizeof(*header), num_sizes * sizeof(*sizes));
3061 if (sizes)
3063 for (i = 0; i < num_sizes; ++i)
3065 BYTE bpp = sizes[i].bit_depth;
3067 if ((ret & format_mask) == format_mask)
3068 break;
3070 if (bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8)
3071 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3072 else if (bpp == 32)
3073 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3077 IDWriteFontFace5_ReleaseFontTable(fontface, cblc.context);
3079 return ret;
3082 UINT32 opentype_get_glyph_image_formats(IDWriteFontFace5 *fontface)
3084 UINT32 ret = DWRITE_GLYPH_IMAGE_FORMATS_NONE;
3086 if (opentype_has_font_table(fontface, MS_GLYF_TAG))
3087 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
3089 if (opentype_has_font_table(fontface, MS_CFF__TAG) ||
3090 opentype_has_font_table(fontface, MS_CFF2_TAG))
3091 ret |= DWRITE_GLYPH_IMAGE_FORMATS_CFF;
3093 if (opentype_has_font_table(fontface, MS_COLR_TAG))
3094 ret |= DWRITE_GLYPH_IMAGE_FORMATS_COLR;
3096 if (opentype_has_font_table(fontface, MS_SVG__TAG))
3097 ret |= DWRITE_GLYPH_IMAGE_FORMATS_SVG;
3099 if (opentype_has_font_table(fontface, MS_SBIX_TAG))
3100 ret |= opentype_get_sbix_formats(fontface);
3102 if (opentype_has_font_table(fontface, MS_CBLC_TAG))
3103 ret |= opentype_get_cblc_formats(fontface);
3105 return ret;
3108 DWRITE_CONTAINER_TYPE opentype_analyze_container_type(void const *data, UINT32 data_size)
3110 DWORD signature;
3112 if (data_size < sizeof(DWORD))
3113 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3115 /* Both WOFF and WOFF2 start with 4 bytes signature. */
3116 signature = *(DWORD *)data;
3118 switch (signature)
3120 case MS_WOFF_TAG:
3121 return DWRITE_CONTAINER_TYPE_WOFF;
3122 case MS_WOF2_TAG:
3123 return DWRITE_CONTAINER_TYPE_WOFF2;
3124 default:
3125 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3129 void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache)
3131 cache->font->grab_font_table(cache->context, MS_GSUB_TAG, &cache->gsub.table.data, &cache->gsub.table.size,
3132 &cache->gsub.table.context);
3134 if (cache->gsub.table.data)
3136 cache->gsub.script_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, script_list));
3137 cache->gsub.feature_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3138 cache->gsub.lookup_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3141 cache->font->grab_font_table(cache->context, MS_GPOS_TAG, &cache->gpos.table.data, &cache->gpos.table.size,
3142 &cache->gpos.table.context);
3144 if (cache->gpos.table.data)
3146 cache->gpos.script_list = table_read_be_word(&cache->gpos.table,
3147 FIELD_OFFSET(struct gpos_gsub_header, script_list));
3148 cache->gpos.feature_list = table_read_be_word(&cache->gpos.table,
3149 FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3150 cache->gpos.lookup_list = table_read_be_word(&cache->gpos.table,
3151 FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3154 cache->font->grab_font_table(cache->context, MS_GDEF_TAG, &cache->gdef.table.data, &cache->gdef.table.size,
3155 &cache->gdef.table.context);
3157 if (cache->gdef.table.data)
3159 unsigned int version = table_read_be_dword(&cache->gdef.table, 0);
3161 cache->gdef.classdef = table_read_be_word(&cache->gdef.table, FIELD_OFFSET(struct gdef_header, classdef));
3162 cache->gdef.markattachclassdef = table_read_be_word(&cache->gdef.table,
3163 FIELD_OFFSET(struct gdef_header, markattach_classdef));
3164 if (version >= 0x00010002)
3165 cache->gdef.markglyphsetdef = table_read_be_word(&cache->gdef.table,
3166 FIELD_OFFSET(struct gdef_header, markglyphsetdef));
3170 unsigned int opentype_layout_find_script(const struct scriptshaping_cache *cache, unsigned int kind, DWORD script,
3171 unsigned int *script_index)
3173 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3174 UINT16 script_count;
3175 unsigned int i;
3177 *script_index = ~0u;
3179 script_count = table_read_be_word(&table->table, table->script_list);
3180 if (!script_count)
3181 return 0;
3183 for (i = 0; i < script_count; i++)
3185 unsigned int tag = table_read_dword(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3186 i * sizeof(struct ot_script_record));
3187 if (!tag)
3188 continue;
3190 if (tag == script)
3192 *script_index = i;
3193 return script;
3197 return 0;
3200 unsigned int opentype_layout_find_language(const struct scriptshaping_cache *cache, unsigned int kind, DWORD language,
3201 unsigned int script_index, unsigned int *language_index)
3203 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3204 UINT16 table_offset, lang_count;
3205 unsigned int i;
3207 *language_index = ~0u;
3209 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3210 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
3211 if (!table_offset)
3212 return 0;
3214 lang_count = table_read_be_word(&table->table, table->script_list + table_offset +
3215 FIELD_OFFSET(struct ot_script, langsys_count));
3216 for (i = 0; i < lang_count; i++)
3218 unsigned int tag = table_read_dword(&table->table, table->script_list + table_offset +
3219 FIELD_OFFSET(struct ot_script, langsys) + i * sizeof(struct ot_langsys_record));
3221 if (tag == language)
3223 *language_index = i;
3224 return language;
3228 /* Try 'defaultLangSys' if it's set. */
3229 if (table_read_be_word(&table->table, table->script_list + table_offset))
3230 return ~0u;
3232 return 0;
3235 static int gdef_class_compare_format2(const void *g, const void *r)
3237 const struct ot_gdef_class_range *range = r;
3238 UINT16 glyph = *(UINT16 *)g;
3240 if (glyph < GET_BE_WORD(range->start_glyph))
3241 return -1;
3242 else if (glyph > GET_BE_WORD(range->end_glyph))
3243 return 1;
3244 else
3245 return 0;
3248 static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable *table,
3249 unsigned int offset, UINT16 glyph)
3251 WORD format = table_read_be_word(table, offset), count;
3252 unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED;
3254 if (format == 1)
3256 const struct ot_gdef_classdef_format1 *format1;
3258 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
3259 format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
3260 if (format1)
3262 WORD start_glyph = GET_BE_WORD(format1->start_glyph);
3263 if (glyph >= start_glyph && (glyph - start_glyph) < count)
3265 glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
3266 if (glyph_class > GDEF_CLASS_MAX)
3267 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3271 else if (format == 2)
3273 const struct ot_gdef_classdef_format2 *format2;
3275 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
3276 format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
3277 if (format2)
3279 const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
3280 sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
3281 glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
3282 GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
3283 if (glyph_class > GDEF_CLASS_MAX)
3284 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3287 else
3288 WARN("Unknown GDEF format %u.\n", format);
3290 return glyph_class;
3293 static unsigned int opentype_set_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3295 struct scriptshaping_cache *cache = context->cache;
3296 unsigned int glyph_class = 0, props;
3298 if (cache->gdef.classdef)
3300 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.classdef,
3301 context->u.buffer.glyphs[idx]);
3304 switch (glyph_class)
3306 case GDEF_CLASS_BASE:
3307 props = GLYPH_PROP_BASE;
3308 break;
3309 case GDEF_CLASS_LIGATURE:
3310 props = GLYPH_PROP_LIGATURE;
3311 break;
3312 case GDEF_CLASS_MARK:
3313 props = GLYPH_PROP_MARK;
3314 if (cache->gdef.markattachclassdef)
3316 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.markattachclassdef,
3317 context->u.buffer.glyphs[idx]);
3318 props |= glyph_class << 8;
3320 break;
3321 default:
3322 props = 0;
3325 context->glyph_infos[idx].props = props;
3327 return props;
3330 static void opentype_set_subst_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3332 unsigned int glyph_props = opentype_set_glyph_props(context, idx) & LOOKUP_FLAG_IGNORE_MASK;
3333 context->u.subst.glyph_props[idx].isDiacritic = !!(glyph_props == GLYPH_PROP_MARK);
3334 context->u.subst.glyph_props[idx].isZeroWidthSpace = !!(glyph_props == GLYPH_PROP_MARK);
3337 struct coverage_compare_format1_context
3339 UINT16 glyph;
3340 const UINT16 *table_base;
3341 unsigned int *coverage_index;
3344 static int coverage_compare_format1(const void *left, const void *right)
3346 const struct coverage_compare_format1_context *context = left;
3347 UINT16 glyph = GET_BE_WORD(*(UINT16 *)right);
3348 int ret;
3350 ret = context->glyph - glyph;
3351 if (!ret)
3352 *context->coverage_index = (UINT16 *)right - context->table_base;
3354 return ret;
3357 static int coverage_compare_format2(const void *g, const void *r)
3359 const struct ot_coverage_range *range = r;
3360 UINT16 glyph = *(UINT16 *)g;
3362 if (glyph < GET_BE_WORD(range->start_glyph))
3363 return -1;
3364 else if (glyph > GET_BE_WORD(range->end_glyph))
3365 return 1;
3366 else
3367 return 0;
3370 static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable *table, unsigned int coverage,
3371 UINT16 glyph)
3373 WORD format = table_read_be_word(table, coverage), count;
3375 count = table_read_be_word(table, coverage + 2);
3377 if (format == 1)
3379 const struct ot_coverage_format1 *format1 = table_read_ensure(table, coverage,
3380 FIELD_OFFSET(struct ot_coverage_format1, glyphs[count]));
3381 struct coverage_compare_format1_context context;
3382 unsigned int coverage_index = GLYPH_NOT_COVERED;
3384 if (format1)
3386 context.glyph = glyph;
3387 context.table_base = format1->glyphs;
3388 context.coverage_index = &coverage_index;
3390 bsearch(&context, format1->glyphs, count, sizeof(glyph), coverage_compare_format1);
3393 return coverage_index;
3395 else if (format == 2)
3397 const struct ot_coverage_format2 *format2 = table_read_ensure(table, coverage,
3398 FIELD_OFFSET(struct ot_coverage_format2, ranges[count]));
3399 if (format2)
3401 const struct ot_coverage_range *range = bsearch(&glyph, format2->ranges, count,
3402 sizeof(struct ot_coverage_range), coverage_compare_format2);
3403 return range && glyph <= GET_BE_WORD(range->end_glyph) ?
3404 GET_BE_WORD(range->startcoverage_index) + glyph - GET_BE_WORD(range->start_glyph) :
3405 GLYPH_NOT_COVERED;
3408 else
3409 WARN("Unknown coverage format %u.\n", format);
3411 return -1;
3414 static inline unsigned int dwrite_popcount(unsigned int x)
3416 #ifdef HAVE___BUILTIN_POPCOUNT
3417 return __builtin_popcount(x);
3418 #else
3419 x -= x >> 1 & 0x55555555;
3420 x = (x & 0x33333333) + (x >> 2 & 0x33333333);
3421 return ((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
3422 #endif
3425 static float opentype_scale_gpos_be_value(WORD value, float emsize, UINT16 upem)
3427 return (short)GET_BE_WORD(value) * emsize / upem;
3430 static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context *context, unsigned int offset)
3432 const struct dwrite_fonttable *table = &context->table->table;
3433 unsigned int start_size, end_size, format, value_word;
3434 unsigned int index, ppem, mask;
3435 int value;
3437 if (!offset)
3438 return 0;
3440 start_size = table_read_be_word(table, offset);
3441 end_size = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, end_size));
3443 ppem = context->emsize;
3444 if (ppem < start_size || ppem > end_size)
3445 return 0;
3447 format = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, format));
3449 if (format < 1 || format > 3)
3450 return 0;
3452 index = ppem - start_size;
3454 value_word = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, values[index >> (4 - format)]));
3455 mask = 0xffff >> (16 - (1 << format));
3457 value = (value_word >> ((index % (4 - format)) * (1 << format))) & mask;
3459 if ((unsigned int)value >= ((mask + 1) >> 1))
3460 value -= mask + 1;
3462 return value;
3465 static void opentype_layout_apply_gpos_value(struct scriptshaping_context *context, unsigned int table_offset,
3466 WORD value_format, const WORD *values, unsigned int glyph)
3468 const struct scriptshaping_cache *cache = context->cache;
3469 DWRITE_GLYPH_OFFSET *offset = &context->offsets[glyph];
3470 float *advance = &context->advances[glyph];
3472 if (!value_format)
3473 return;
3475 if (value_format & GPOS_VALUE_X_PLACEMENT)
3477 offset->advanceOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3478 values++;
3480 if (value_format & GPOS_VALUE_Y_PLACEMENT)
3482 offset->ascenderOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3483 values++;
3485 if (value_format & GPOS_VALUE_X_ADVANCE)
3487 *advance += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3488 values++;
3490 if (value_format & GPOS_VALUE_Y_ADVANCE)
3492 values++;
3494 if (value_format & GPOS_VALUE_X_PLACEMENT_DEVICE)
3496 offset->advanceOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3497 values++;
3499 if (value_format & GPOS_VALUE_Y_PLACEMENT_DEVICE)
3501 offset->ascenderOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3502 values++;
3504 if (value_format & GPOS_VALUE_X_ADVANCE_DEVICE)
3506 *advance += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3507 values++;
3509 if (value_format & GPOS_VALUE_Y_ADVANCE_DEVICE)
3511 values++;
3515 struct lookup
3517 unsigned short index;
3518 unsigned short type;
3519 unsigned short flags;
3520 unsigned short subtable_count;
3522 unsigned int mask;
3523 unsigned int offset;
3524 unsigned int auto_zwnj : 1;
3525 unsigned int auto_zwj : 1;
3528 static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context *context,
3529 const struct lookup *lookup, unsigned int subtable, unsigned int *lookup_type)
3531 unsigned int subtable_offset = table_read_be_word(&context->table->table, lookup->offset +
3532 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
3533 const struct ot_gsubgpos_extension_format1 *format1;
3535 subtable_offset += lookup->offset;
3537 if ((context->table == &context->cache->gsub && lookup->type != GSUB_LOOKUP_EXTENSION_SUBST) ||
3538 (context->table == &context->cache->gpos && lookup->type != GPOS_LOOKUP_EXTENSION_POSITION))
3540 *lookup_type = lookup->type;
3541 return subtable_offset;
3544 *lookup_type = 0;
3546 if (!(format1 = table_read_ensure(&context->table->table, subtable_offset, sizeof(*format1))))
3547 return 0;
3549 if (GET_BE_WORD(format1->format) != 1)
3551 WARN("Unexpected extension table format %#x.\n", format1->format);
3552 return 0;
3555 *lookup_type = GET_BE_WORD(format1->lookup_type);
3556 return subtable_offset + GET_BE_DWORD(format1->extension_offset);
3559 struct ot_lookup
3561 unsigned int offset;
3562 unsigned int subtable_count;
3563 unsigned int flags;
3566 enum iterator_match
3568 /* First two to fit matching callback result. */
3569 ITER_NO = 0,
3570 ITER_YES = 1,
3571 ITER_MAYBE,
3574 struct match_context;
3575 struct match_data
3577 const struct match_context *mc;
3578 unsigned int subtable_offset;
3581 typedef BOOL (*p_match_func)(UINT16 glyph, UINT16 glyph_data, const struct match_data *match_data);
3583 struct match_context
3585 struct scriptshaping_context *context;
3586 unsigned int backtrack_offset;
3587 unsigned int input_offset;
3588 unsigned int lookahead_offset;
3589 p_match_func match_func;
3590 const struct lookup *lookup;
3593 struct glyph_iterator
3595 struct scriptshaping_context *context;
3596 unsigned int flags;
3597 unsigned int pos;
3598 unsigned int len;
3599 unsigned int mask;
3600 p_match_func match_func;
3601 const UINT16 *glyph_data;
3602 const struct match_data *match_data;
3603 unsigned int ignore_zwnj;
3604 unsigned int ignore_zwj;
3607 static void glyph_iterator_init(struct scriptshaping_context *context, unsigned int flags, unsigned int pos,
3608 unsigned int len, struct glyph_iterator *iter)
3610 iter->context = context;
3611 iter->flags = flags;
3612 iter->pos = pos;
3613 iter->len = len;
3614 iter->mask = ~0u;
3615 iter->match_func = NULL;
3616 iter->match_data = NULL;
3617 iter->glyph_data = NULL;
3618 /* Context matching iterators will get these fixed up. */
3619 iter->ignore_zwnj = context->table == &context->cache->gpos;
3620 iter->ignore_zwj = context->auto_zwj;
3623 struct ot_gdef_mark_glyph_sets
3625 UINT16 format;
3626 UINT16 count;
3627 DWORD offsets[1];
3630 static BOOL opentype_match_glyph_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3632 return glyph == glyph_data;
3635 static BOOL opentype_match_class_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3637 const struct match_context *mc = data->mc;
3638 UINT16 glyph_class = opentype_layout_get_glyph_class(&mc->context->table->table, data->subtable_offset, glyph);
3639 return glyph_class == glyph_data;
3642 static BOOL opentype_match_coverage_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3644 const struct match_context *mc = data->mc;
3645 return opentype_layout_is_glyph_covered(&mc->context->table->table, data->subtable_offset + glyph_data, glyph)
3646 != GLYPH_NOT_COVERED;
3649 static BOOL opentype_layout_mark_set_covers(const struct scriptshaping_cache *cache, unsigned int set_index,
3650 UINT16 glyph)
3652 unsigned int format, offset = cache->gdef.markglyphsetdef, coverage_offset, set_count;
3654 if (!offset)
3655 return FALSE;
3657 format = table_read_be_word(&cache->gdef.table, offset);
3659 if (format == 1)
3661 set_count = table_read_be_word(&cache->gdef.table, offset + 2);
3662 if (!set_count || set_index >= set_count)
3663 return FALSE;
3665 coverage_offset = table_read_be_dword(&cache->gdef.table, offset + 2 + set_index * sizeof(coverage_offset));
3666 return opentype_layout_is_glyph_covered(&cache->gdef.table, offset + coverage_offset, glyph) != GLYPH_NOT_COVERED;
3668 else
3669 WARN("Unexpected MarkGlyphSets format %#x.\n", format);
3671 return FALSE;
3674 static BOOL lookup_is_glyph_match(const struct scriptshaping_context *context, unsigned int idx, unsigned int match_props)
3676 unsigned int glyph_props = context->glyph_infos[idx].props;
3677 UINT16 glyph = context->u.buffer.glyphs[idx];
3679 if (glyph_props & match_props & LOOKUP_FLAG_IGNORE_MASK)
3680 return FALSE;
3682 if (!(glyph_props & GLYPH_PROP_MARK))
3683 return TRUE;
3685 if (match_props & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
3686 return opentype_layout_mark_set_covers(context->cache, match_props >> 16, glyph);
3688 if (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE)
3689 return (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE) == (glyph_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE);
3691 return TRUE;
3694 static enum iterator_match glyph_iterator_may_skip(const struct glyph_iterator *iter)
3696 unsigned int glyph_props = iter->context->glyph_infos[iter->pos].props & (GLYPH_PROP_IGNORABLE | GLYPH_PROP_HIDDEN);
3698 if (!lookup_is_glyph_match(iter->context, iter->pos, iter->flags))
3699 return ITER_YES;
3701 if (glyph_props == GLYPH_PROP_IGNORABLE && !iter->context->u.buffer.glyph_props[iter->pos].components &&
3702 (iter->ignore_zwnj || !(iter->context->glyph_infos[iter->pos].props & GLYPH_PROP_ZWNJ)) &&
3703 (iter->ignore_zwj || !(iter->context->glyph_infos[iter->pos].props & GLYPH_PROP_ZWJ)))
3705 return ITER_MAYBE;
3708 return ITER_NO;
3711 static enum iterator_match glyph_iterator_may_match(const struct glyph_iterator *iter)
3713 if (!(iter->mask & iter->context->glyph_infos[iter->pos].mask))
3714 return ITER_NO;
3716 /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that
3717 in all matching functions. */
3718 if (iter->match_func)
3719 return !!iter->match_func(iter->context->u.buffer.glyphs[iter->pos], GET_BE_WORD(*iter->glyph_data), iter->match_data);
3721 return ITER_MAYBE;
3724 static BOOL glyph_iterator_next(struct glyph_iterator *iter)
3726 enum iterator_match skip, match;
3728 while (iter->pos + iter->len < iter->context->glyph_count)
3730 ++iter->pos;
3732 skip = glyph_iterator_may_skip(iter);
3733 if (skip == ITER_YES)
3734 continue;
3736 match = glyph_iterator_may_match(iter);
3737 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3739 --iter->len;
3740 if (iter->glyph_data)
3741 ++iter->glyph_data;
3742 return TRUE;
3745 if (skip == ITER_NO)
3746 return FALSE;
3749 return FALSE;
3752 static BOOL glyph_iterator_prev(struct glyph_iterator *iter)
3754 enum iterator_match skip, match;
3756 while (iter->pos > iter->len - 1)
3758 --iter->pos;
3760 skip = glyph_iterator_may_skip(iter);
3761 if (skip == ITER_YES)
3762 continue;
3764 match = glyph_iterator_may_match(iter);
3765 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3767 --iter->len;
3768 if (iter->glyph_data)
3769 ++iter->glyph_data;
3770 return TRUE;
3773 if (skip == ITER_NO)
3774 return FALSE;
3777 return FALSE;
3780 static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context,
3781 const struct lookup *lookup, unsigned int subtable_offset)
3783 const struct dwrite_fonttable *table = &context->table->table;
3784 UINT16 format, value_format, value_len, coverage, glyph;
3786 unsigned int coverage_index;
3788 format = table_read_be_word(table, subtable_offset);
3790 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage));
3791 value_format = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format));
3792 value_len = dwrite_popcount(value_format);
3794 glyph = context->u.pos.glyphs[context->cur];
3796 if (format == 1)
3798 const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(table, subtable_offset,
3799 FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len]));
3801 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3802 if (coverage_index == GLYPH_NOT_COVERED)
3803 return FALSE;
3805 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, context->cur);
3807 else if (format == 2)
3809 WORD value_count = table_read_be_word(table, subtable_offset +
3810 FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count));
3811 const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(table, subtable_offset,
3812 FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD));
3814 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3815 if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count)
3816 return FALSE;
3818 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, &format2->values[coverage_index * value_len],
3819 context->cur);
3821 else
3823 WARN("Unknown single adjustment format %u.\n", format);
3824 return FALSE;
3827 context->cur++;
3829 return TRUE;
3832 static int gpos_pair_adjustment_compare_format1(const void *g, const void *r)
3834 const struct ot_gpos_pairvalue *pairvalue = r;
3835 UINT16 second_glyph = GET_BE_WORD(pairvalue->second_glyph);
3836 return *(UINT16 *)g - second_glyph;
3839 static BOOL opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context *context,
3840 const struct lookup *lookup, unsigned int subtable_offset)
3842 const struct dwrite_fonttable *table = &context->table->table;
3843 unsigned int first_glyph, second_glyph;
3844 struct glyph_iterator iter_pair;
3845 WORD format, coverage;
3847 WORD value_format1, value_format2, value_len1, value_len2;
3848 unsigned int coverage_index;
3850 glyph_iterator_init(context, lookup->flags, context->cur, 1, &iter_pair);
3851 if (!glyph_iterator_next(&iter_pair))
3852 return FALSE;
3854 if (context->is_rtl)
3856 first_glyph = iter_pair.pos;
3857 second_glyph = context->cur;
3859 else
3861 first_glyph = context->cur;
3862 second_glyph = iter_pair.pos;
3865 format = table_read_be_word(table, subtable_offset);
3867 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1, coverage));
3868 if (!coverage)
3869 return FALSE;
3871 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, context->u.pos.glyphs[first_glyph]);
3872 if (coverage_index == GLYPH_NOT_COVERED)
3873 return FALSE;
3875 if (format == 1)
3877 const struct ot_gpos_pairpos_format1 *format1;
3878 WORD pairset_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1,
3879 pairset_count));
3880 unsigned int pairvalue_len, pairset_offset;
3881 const struct ot_gpos_pairset *pairset;
3882 const WORD *pairvalue;
3883 WORD pairvalue_count;
3885 if (!pairset_count || coverage_index >= pairset_count)
3886 return FALSE;
3888 format1 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairsets[pairset_count]));
3889 if (!format1)
3890 return FALSE;
3892 /* Ordered paired values. */
3893 pairvalue_count = table_read_be_word(table, subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]));
3894 if (!pairvalue_count)
3895 return FALSE;
3897 /* Structure length is variable, but does not change across the subtable. */
3898 value_format1 = GET_BE_WORD(format1->value_format1) & 0xff;
3899 value_format2 = GET_BE_WORD(format1->value_format2) & 0xff;
3901 value_len1 = dwrite_popcount(value_format1);
3902 value_len2 = dwrite_popcount(value_format2);
3903 pairvalue_len = FIELD_OFFSET(struct ot_gpos_pairvalue, data) + value_len1 * sizeof(WORD) +
3904 value_len2 * sizeof(WORD);
3906 pairset_offset = subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]);
3907 pairset = table_read_ensure(table, subtable_offset + pairset_offset, pairvalue_len * pairvalue_count);
3908 if (!pairset)
3909 return FALSE;
3911 pairvalue = bsearch(&context->u.pos.glyphs[second_glyph], pairset->pairvalues, pairvalue_count,
3912 pairvalue_len, gpos_pair_adjustment_compare_format1);
3913 if (!pairvalue)
3914 return FALSE;
3916 pairvalue += 1; /* Skip SecondGlyph. */
3917 opentype_layout_apply_gpos_value(context, pairset_offset, value_format1, pairvalue, first_glyph);
3918 opentype_layout_apply_gpos_value(context, pairset_offset, value_format2, pairvalue + value_len1,
3919 second_glyph);
3921 context->cur = iter_pair.pos;
3922 if (value_len2)
3923 context->cur++;
3925 else if (format == 2)
3927 const struct ot_gpos_pairpos_format2 *format2;
3928 WORD class1_count, class2_count;
3929 unsigned int class1, class2;
3930 const WCHAR *values;
3932 value_format1 = table_read_be_word(table, subtable_offset +
3933 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format1)) & 0xff;
3934 value_format2 = table_read_be_word(table, subtable_offset +
3935 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format2)) & 0xff;
3937 class1_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class1_count));
3938 class2_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class2_count));
3940 value_len1 = dwrite_popcount(value_format1);
3941 value_len2 = dwrite_popcount(value_format2);
3943 format2 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format2,
3944 values[class1_count * class2_count * (value_len1 + value_len2)]));
3945 if (!format2)
3946 return FALSE;
3948 class1 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def1),
3949 context->u.pos.glyphs[first_glyph]);
3950 class2 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def2),
3951 context->u.pos.glyphs[second_glyph]);
3953 if (!(class1 < class1_count && class2 < class2_count))
3954 return FALSE;
3956 values = &format2->values[(class1 * class2_count + class2) * (value_len1 + value_len2)];
3957 opentype_layout_apply_gpos_value(context, subtable_offset, value_format1, values, first_glyph);
3958 opentype_layout_apply_gpos_value(context, subtable_offset, value_format2, values + value_len1,
3959 second_glyph);
3961 context->cur = iter_pair.pos;
3962 if (value_len2)
3963 context->cur++;
3965 else
3967 WARN("Unknown pair adjustment format %u.\n", format);
3968 return FALSE;
3971 return TRUE;
3974 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context *context, unsigned int anchor_offset,
3975 unsigned int glyph_index, float *x, float *y)
3977 const struct scriptshaping_cache *cache = context->cache;
3978 const struct dwrite_fonttable *table = &context->table->table;
3980 WORD format = table_read_be_word(table, anchor_offset);
3982 *x = *y = 0.0f;
3984 if (format == 1)
3986 const struct ot_gpos_anchor_format1 *format1 = table_read_ensure(table, anchor_offset, sizeof(*format1));
3988 if (format1)
3990 *x = opentype_scale_gpos_be_value(format1->x_coord, context->emsize, cache->upem);
3991 *y = opentype_scale_gpos_be_value(format1->y_coord, context->emsize, cache->upem);
3994 else if (format == 2)
3996 const struct ot_gpos_anchor_format2 *format2 = table_read_ensure(table, anchor_offset, sizeof(*format2));
3998 if (format2)
4000 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
4001 FIXME("Use outline anchor point for glyph %u.\n", context->u.pos.glyphs[glyph_index]);
4003 *x = opentype_scale_gpos_be_value(format2->x_coord, context->emsize, cache->upem);
4004 *y = opentype_scale_gpos_be_value(format2->y_coord, context->emsize, cache->upem);
4007 else if (format == 3)
4009 const struct ot_gpos_anchor_format3 *format3 = table_read_ensure(table, anchor_offset, sizeof(*format3));
4011 if (format3)
4013 *x = opentype_scale_gpos_be_value(format3->x_coord, context->emsize, cache->upem);
4014 *y = opentype_scale_gpos_be_value(format3->y_coord, context->emsize, cache->upem);
4016 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
4018 if (format3->x_dev_offset)
4019 *x += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->x_dev_offset));
4020 if (format3->y_dev_offset)
4021 *y += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->y_dev_offset));
4025 else
4026 WARN("Unknown anchor format %u.\n", format);
4029 static BOOL opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context *context,
4030 const struct lookup *lookup, unsigned int subtable_offset)
4032 const struct dwrite_fonttable *table = &context->table->table;
4033 UINT16 format, glyph;
4035 format = table_read_be_word(table, subtable_offset);
4036 glyph = context->u.pos.glyphs[context->cur];
4038 if (format == 1)
4040 WORD coverage_offset = table_read_be_word(table, subtable_offset +
4041 FIELD_OFFSET(struct ot_gpos_cursive_format1, coverage));
4042 unsigned int glyph_index, entry_count, entry_anchor, exit_anchor;
4043 float entry_x, entry_y, exit_x, exit_y, delta;
4044 struct glyph_iterator prev_iter;
4046 if (!coverage_offset)
4047 return FALSE;
4049 entry_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_cursive_format1, count));
4051 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset, glyph);
4052 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4053 return FALSE;
4055 entry_anchor = table_read_be_word(table, subtable_offset +
4056 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2]));
4057 if (!entry_anchor)
4058 return FALSE;
4060 glyph_iterator_init(context, lookup->flags, context->cur, 1, &prev_iter);
4061 if (!glyph_iterator_prev(&prev_iter))
4062 return FALSE;
4064 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset,
4065 context->u.pos.glyphs[prev_iter.pos]);
4066 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4067 return FALSE;
4069 exit_anchor = table_read_be_word(table, subtable_offset +
4070 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2 + 1]));
4071 if (!exit_anchor)
4072 return FALSE;
4074 opentype_layout_gpos_get_anchor(context, subtable_offset + exit_anchor, prev_iter.pos, &exit_x, &exit_y);
4075 opentype_layout_gpos_get_anchor(context, subtable_offset + entry_anchor, context->cur, &entry_x, &entry_y);
4077 if (context->is_rtl)
4079 delta = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4080 context->advances[prev_iter.pos] -= delta;
4081 context->advances[context->cur] = entry_x + context->offsets[context->cur].advanceOffset;
4082 context->offsets[prev_iter.pos].advanceOffset -= delta;
4084 else
4086 delta = entry_x + context->offsets[context->cur].advanceOffset;
4087 context->advances[prev_iter.pos] = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4088 context->advances[context->cur] -= delta;
4089 context->offsets[context->cur].advanceOffset -= delta;
4092 if (lookup->flags & LOOKUP_FLAG_RTL)
4093 context->offsets[prev_iter.pos].ascenderOffset = entry_y - exit_y;
4094 else
4095 context->offsets[context->cur].ascenderOffset = exit_y - entry_y;
4097 context->cur++;
4099 else
4101 WARN("Unknown cursive attachment format %u.\n", format);
4102 return FALSE;
4105 return TRUE;
4108 static BOOL opentype_layout_apply_mark_array(struct scriptshaping_context *context, unsigned int subtable_offset,
4109 unsigned int mark_array, unsigned int mark_index, unsigned int glyph_index, unsigned int anchors_matrix,
4110 unsigned int class_count, unsigned int glyph_pos)
4112 const struct dwrite_fonttable *table = &context->table->table;
4113 unsigned int mark_class, mark_count, glyph_count;
4114 const struct ot_gpos_mark_record *record;
4115 float mark_x, mark_y, base_x, base_y;
4116 const UINT16 *anchors;
4118 mark_count = table_read_be_word(table, subtable_offset + mark_array);
4119 if (mark_index >= mark_count) return FALSE;
4121 if (!(record = table_read_ensure(table, subtable_offset + mark_array +
4122 FIELD_OFFSET(struct ot_gpos_mark_array, records[mark_index]), sizeof(*record))))
4124 return FALSE;
4127 mark_class = GET_BE_WORD(record->mark_class);
4128 if (mark_class >= class_count) return FALSE;
4130 glyph_count = table_read_be_word(table, subtable_offset + anchors_matrix);
4131 if (glyph_index >= glyph_count) return FALSE;
4133 /* Anchors data is stored as two dimensional array [glyph_count][class_count], starting with row count field. */
4134 anchors = table_read_ensure(table, subtable_offset + anchors_matrix + 2, glyph_count * class_count * sizeof(*anchors));
4135 if (!anchors) return FALSE;
4137 opentype_layout_gpos_get_anchor(context, subtable_offset + mark_array + GET_BE_WORD(record->mark_anchor),
4138 context->cur, &mark_x, &mark_y);
4139 opentype_layout_gpos_get_anchor(context, subtable_offset + anchors_matrix +
4140 GET_BE_WORD(anchors[glyph_index * class_count + mark_class]), glyph_pos, &base_x, &base_y);
4142 if (context->is_rtl)
4143 context->offsets[context->cur].advanceOffset = mark_x - base_x;
4144 else
4145 context->offsets[context->cur].advanceOffset = -context->advances[glyph_pos] + base_x - mark_x;
4147 context->offsets[context->cur].ascenderOffset = base_y - mark_y;
4148 context->cur++;
4150 return TRUE;
4153 static BOOL opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context *context,
4154 const struct lookup *lookup, unsigned int subtable_offset)
4156 const struct dwrite_fonttable *table = &context->table->table;
4157 WORD format;
4159 format = table_read_be_word(table, subtable_offset);
4161 if (format == 1)
4163 const struct ot_gpos_mark_to_base_format1 *format1;
4164 unsigned int base_index, mark_index;
4165 struct glyph_iterator base_iter;
4167 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4169 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4170 context->u.pos.glyphs[context->cur]);
4171 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4173 /* Look back for first base glyph. */
4174 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &base_iter);
4175 if (!glyph_iterator_prev(&base_iter))
4176 return FALSE;
4178 base_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->base_coverage),
4179 context->u.pos.glyphs[base_iter.pos]);
4180 if (base_index == GLYPH_NOT_COVERED) return FALSE;
4182 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4183 base_index, GET_BE_WORD(format1->base_array), GET_BE_WORD(format1->mark_class_count), base_iter.pos);
4185 else
4187 WARN("Unknown mark-to-base format %u.\n", format);
4188 return FALSE;
4191 return TRUE;
4194 static const UINT16 * table_read_array_be_word(const struct dwrite_fonttable *table, unsigned int offset,
4195 unsigned int index, UINT16 *data)
4197 unsigned int count = table_read_be_word(table, offset);
4198 const UINT16 *array;
4200 if (index != ~0u && index >= count) return NULL;
4201 if (!(array = table_read_ensure(table, offset + 2, count * sizeof(*array)))) return FALSE;
4202 *data = index == ~0u ? count : GET_BE_WORD(array[index]);
4203 return array;
4206 static BOOL opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context *context,
4207 const struct lookup *lookup, unsigned int subtable_offset)
4209 const struct dwrite_fonttable *table = &context->table->table;
4210 WORD format;
4212 format = table_read_be_word(table, subtable_offset);
4214 if (format == 1)
4216 unsigned int mark_index, lig_index, comp_index, class_count, comp_count;
4217 const struct ot_gpos_mark_to_lig_format1 *format1;
4218 struct glyph_iterator lig_iter;
4219 unsigned int lig_array;
4220 UINT16 lig_attach;
4222 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4224 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4225 context->u.pos.glyphs[context->cur]);
4226 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4228 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &lig_iter);
4229 if (!glyph_iterator_prev(&lig_iter))
4230 return FALSE;
4232 lig_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->lig_coverage),
4233 context->u.pos.glyphs[lig_iter.pos]);
4234 if (lig_index == GLYPH_NOT_COVERED) return FALSE;
4236 class_count = GET_BE_WORD(format1->mark_class_count);
4238 lig_array = GET_BE_WORD(format1->lig_array);
4240 if (!table_read_array_be_word(table, subtable_offset + lig_array, lig_index, &lig_attach)) return FALSE;
4242 comp_count = table_read_be_word(table, subtable_offset + lig_array + lig_attach);
4243 if (!comp_count) return FALSE;
4245 comp_index = context->u.buffer.glyph_props[lig_iter.pos].components -
4246 context->u.buffer.glyph_props[context->cur].lig_component - 1;
4247 if (comp_index >= comp_count) return FALSE;
4249 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4250 comp_index, lig_array + lig_attach, class_count, lig_iter.pos);
4252 else
4253 WARN("Unknown mark-to-ligature format %u.\n", format);
4255 return FALSE;
4258 static BOOL opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context *context,
4259 const struct lookup *lookup, unsigned int subtable_offset)
4261 const struct dwrite_fonttable *table = &context->table->table;
4262 WORD format;
4264 format = table_read_be_word(table, subtable_offset);
4266 if (format == 1)
4268 const struct ot_gpos_mark_to_mark_format1 *format1;
4269 unsigned int mark1_index, mark2_index;
4270 struct glyph_iterator mark_iter;
4272 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4274 mark1_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark1_coverage),
4275 context->u.pos.glyphs[context->cur]);
4276 if (mark1_index == GLYPH_NOT_COVERED) return FALSE;
4278 glyph_iterator_init(context, lookup->flags & ~LOOKUP_FLAG_IGNORE_MASK, context->cur, 1, &mark_iter);
4279 if (!glyph_iterator_prev(&mark_iter))
4280 return FALSE;
4282 if (!context->u.pos.glyph_props[mark_iter.pos].isDiacritic)
4283 return FALSE;
4285 mark2_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark2_coverage),
4286 context->u.pos.glyphs[mark_iter.pos]);
4287 if (mark2_index == GLYPH_NOT_COVERED) return FALSE;
4289 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark1_array), mark1_index,
4290 mark2_index, GET_BE_WORD(format1->mark2_array), GET_BE_WORD(format1->mark_class_count), mark_iter.pos);
4292 else
4294 WARN("Unknown mark-to-mark format %u.\n", format);
4295 return FALSE;
4298 return TRUE;
4301 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
4302 unsigned int subtable_offset);
4303 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
4304 unsigned int subtable_offset);
4306 static BOOL opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
4308 unsigned int i, lookup_type;
4309 BOOL ret = FALSE;
4311 for (i = 0; i < lookup->subtable_count; ++i)
4313 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
4315 switch (lookup_type)
4317 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
4318 ret = opentype_layout_apply_gpos_single_adjustment(context, lookup, subtable_offset);
4319 break;
4320 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
4321 ret = opentype_layout_apply_gpos_pair_adjustment(context, lookup, subtable_offset);
4322 break;
4323 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
4324 ret = opentype_layout_apply_gpos_cursive_attachment(context, lookup, subtable_offset);
4325 break;
4326 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
4327 ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, lookup, subtable_offset);
4328 break;
4329 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
4330 ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, lookup, subtable_offset);
4331 break;
4332 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
4333 ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, lookup, subtable_offset);
4334 break;
4335 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
4336 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
4337 break;
4338 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
4339 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
4340 break;
4341 case GPOS_LOOKUP_EXTENSION_POSITION:
4342 WARN("Recursive extension lookup.\n");
4343 break;
4344 default:
4345 WARN("Unknown lookup type %u.\n", lookup_type);
4348 if (ret)
4349 break;
4352 return ret;
4355 struct lookups
4357 struct lookup *lookups;
4358 size_t capacity;
4359 size_t count;
4362 static int lookups_sorting_compare(const void *a, const void *b)
4364 const struct lookup *left = (const struct lookup *)a;
4365 const struct lookup *right = (const struct lookup *)b;
4366 return left->index < right->index ? -1 : left->index > right->index ? 1 : 0;
4369 static BOOL opentype_layout_init_lookup(const struct ot_gsubgpos_table *table, unsigned short lookup_index,
4370 const struct shaping_feature *feature, struct lookup *lookup)
4372 unsigned short subtable_count, lookup_type, flags, mark_filtering_set;
4373 const struct ot_lookup_table *lookup_table;
4374 unsigned int offset;
4376 if (!(offset = table_read_be_word(&table->table, table->lookup_list +
4377 FIELD_OFFSET(struct ot_lookup_list, lookup[lookup_index]))))
4379 return FALSE;
4382 offset += table->lookup_list;
4384 if (!(lookup_table = table_read_ensure(&table->table, offset, sizeof(*lookup_table))))
4385 return FALSE;
4387 if (!(subtable_count = GET_BE_WORD(lookup_table->subtable_count)))
4388 return FALSE;
4390 lookup_type = GET_BE_WORD(lookup_table->lookup_type);
4391 flags = GET_BE_WORD(lookup_table->flags);
4393 if (flags & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
4395 mark_filtering_set = table_read_be_word(&table->table, offset +
4396 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable_count]));
4397 flags |= mark_filtering_set << 16;
4400 lookup->index = lookup_index;
4401 lookup->type = lookup_type;
4402 lookup->flags = flags;
4403 lookup->subtable_count = subtable_count;
4404 lookup->offset = offset;
4405 if (feature)
4407 lookup->mask = feature->mask;
4408 lookup->auto_zwnj = !(feature->flags & FEATURE_MANUAL_ZWNJ);
4409 lookup->auto_zwj = !(feature->flags & FEATURE_MANUAL_ZWJ);
4412 return TRUE;
4415 static void opentype_layout_add_lookups(const struct ot_feature_list *feature_list, UINT16 total_lookup_count,
4416 const struct ot_gsubgpos_table *table, struct shaping_feature *feature, struct lookups *lookups)
4418 UINT16 feature_offset, lookup_count;
4419 unsigned int i;
4421 /* Feature wasn't found */
4422 if (feature->index == 0xffff)
4423 return;
4425 feature_offset = GET_BE_WORD(feature_list->features[feature->index].offset);
4427 lookup_count = table_read_be_word(&table->table, table->feature_list + feature_offset +
4428 FIELD_OFFSET(struct ot_feature, lookup_count));
4429 if (!lookup_count)
4430 return;
4432 if (!dwrite_array_reserve((void **)&lookups->lookups, &lookups->capacity, lookups->count + lookup_count,
4433 sizeof(*lookups->lookups)))
4435 return;
4438 for (i = 0; i < lookup_count; ++i)
4440 UINT16 lookup_index = table_read_be_word(&table->table, table->feature_list + feature_offset +
4441 FIELD_OFFSET(struct ot_feature, lookuplist_index[i]));
4443 if (lookup_index >= total_lookup_count)
4444 continue;
4446 if (opentype_layout_init_lookup(table, lookup_index, feature, &lookups->lookups[lookups->count]))
4447 lookups->count++;
4451 static void opentype_layout_collect_lookups(struct scriptshaping_context *context, unsigned int script_index,
4452 unsigned int language_index, struct shaping_features *features, const struct ot_gsubgpos_table *table,
4453 struct lookups *lookups)
4455 unsigned int last_num_lookups = 0, stage, script_feature_count = 0;
4456 UINT16 total_feature_count, total_lookup_count;
4457 struct shaping_feature required_feature = { 0 };
4458 const struct ot_feature_list *feature_list;
4459 const struct ot_langsys *langsys = NULL;
4460 struct shaping_feature *feature;
4461 unsigned int i, j, next_bit;
4462 unsigned int global_bit_shift = 1;
4463 unsigned int global_bit_mask = 2;
4464 UINT16 feature_index;
4466 if (!table->table.data)
4467 return;
4469 if (script_index != ~0u)
4471 unsigned int table_offset, langsys_offset;
4473 /* ScriptTable offset. */
4474 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
4475 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
4476 if (!table_offset)
4477 return;
4479 if (language_index == ~0u)
4480 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
4481 else
4482 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
4483 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
4484 FIELD_OFFSET(struct ot_langsys_record, langsys));
4485 langsys_offset += table->script_list + table_offset;
4487 script_feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
4488 if (script_feature_count)
4489 langsys = table_read_ensure(&table->table, langsys_offset,
4490 FIELD_OFFSET(struct ot_langsys, feature_index[script_feature_count]));
4491 if (!langsys)
4492 script_feature_count = 0;
4495 total_feature_count = table_read_be_word(&table->table, table->feature_list);
4496 if (!total_feature_count)
4497 return;
4499 total_lookup_count = table_read_be_word(&table->table, table->lookup_list);
4500 if (!total_lookup_count)
4501 return;
4503 feature_list = table_read_ensure(&table->table, table->feature_list,
4504 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
4505 if (!feature_list)
4506 return;
4508 /* Required feature. */
4509 required_feature.index = langsys ? GET_BE_WORD(langsys->required_feature_index) : 0xffff;
4510 if (required_feature.index < total_feature_count)
4511 required_feature.tag = feature_list->features[required_feature.index].tag;
4512 required_feature.mask = global_bit_mask;
4514 context->global_mask = global_bit_mask;
4515 next_bit = global_bit_shift + 1;
4516 for (i = 0; i < features->count; ++i)
4518 unsigned int bits_needed;
4519 BOOL found = FALSE;
4521 feature = &features->features[i];
4523 feature->index = 0xffff;
4525 if ((feature->flags & FEATURE_GLOBAL) && feature->max_value == 1)
4526 bits_needed = 0;
4527 else
4529 BitScanReverse(&bits_needed, min(feature->max_value, 256));
4530 bits_needed++;
4533 if (!feature->max_value || next_bit + bits_needed > 8 * sizeof (feature->mask))
4534 continue;
4536 if (required_feature.tag == feature->tag)
4537 required_feature.stage = feature->stage;
4539 for (j = 0; j < script_feature_count; ++j)
4541 feature_index = GET_BE_WORD(langsys->feature_index[j]);
4542 if (feature_index >= total_feature_count)
4543 continue;
4544 if ((found = feature_list->features[feature_index].tag == feature->tag))
4546 feature->index = feature_index;
4547 break;
4551 if (!found && (features->features[i].flags & FEATURE_GLOBAL_SEARCH))
4553 for (j = 0; j < total_feature_count; ++j)
4555 if ((found = (feature_list->features[j].tag == feature->tag)))
4557 feature->index = j;
4558 break;
4563 if (!found && !(features->features[i].flags & FEATURE_HAS_FALLBACK))
4564 continue;
4566 if (feature->flags & FEATURE_GLOBAL && feature->max_value == 1)
4568 feature->shift = global_bit_shift;
4569 feature->mask = global_bit_mask;
4571 else
4573 feature->shift = next_bit;
4574 feature->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
4575 next_bit += bits_needed;
4576 context->global_mask |= (feature->default_value << feature->shift) & feature->mask;
4578 if (!found)
4579 feature->flags |= FEATURE_NEEDS_FALLBACK;
4582 for (stage = 0; stage <= features->stage; ++stage)
4584 if (required_feature.index != 0xffff && required_feature.stage == stage)
4585 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &required_feature, lookups);
4587 for (i = 0; i < features->count; ++i)
4589 if (features->features[i].stage == stage)
4590 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &features->features[i], lookups);
4593 /* Sort and merge lookups for current stage. */
4594 if (last_num_lookups < lookups->count)
4596 qsort(lookups->lookups + last_num_lookups, lookups->count - last_num_lookups, sizeof(*lookups->lookups),
4597 lookups_sorting_compare);
4599 j = last_num_lookups;
4600 for (i = j + 1; i < lookups->count; ++i)
4602 if (lookups->lookups[i].index != lookups->lookups[j].index)
4604 lookups->lookups[++j] = lookups->lookups[i];
4606 else
4608 lookups->lookups[j].mask |= lookups->lookups[i].mask;
4609 lookups->lookups[j].auto_zwnj &= lookups->lookups[i].auto_zwnj;
4610 lookups->lookups[j].auto_zwj &= lookups->lookups[i].auto_zwj;
4613 lookups->count = j + 1;
4616 last_num_lookups = lookups->count;
4617 features->stages[stage].last_lookup = last_num_lookups;
4621 static int feature_search_compare(const void *a, const void* b)
4623 unsigned int tag = *(unsigned int *)a;
4624 const struct shaping_feature *feature = b;
4626 return tag < feature->tag ? -1 : tag > feature->tag ? 1 : 0;
4629 static unsigned int shaping_features_get_mask(const struct shaping_features *features, unsigned int tag, unsigned int *shift)
4631 struct shaping_feature *feature;
4633 feature = bsearch(&tag, features->features, features->count, sizeof(*features->features), feature_search_compare);
4635 if (!feature || feature->index == 0xffff)
4636 return 0;
4638 if (shift) *shift = feature->shift;
4639 return feature->mask;
4642 unsigned int shape_get_feature_1_mask(const struct shaping_features *features, unsigned int tag)
4644 unsigned int shift, mask = shaping_features_get_mask(features, tag, &shift);
4645 return (1 << shift) & mask;
4648 static void opentype_layout_get_glyph_range_for_text(struct scriptshaping_context *context, unsigned int start_char,
4649 unsigned int end_char, unsigned int *start_glyph, unsigned int *end_glyph)
4651 *start_glyph = context->u.buffer.clustermap[start_char];
4652 if (end_char >= context->length - 1)
4653 *end_glyph = context->glyph_count - 1;
4654 else
4655 *end_glyph = context->u.buffer.clustermap[end_char + 1] - 1;
4658 static void opentype_layout_set_glyph_masks(struct scriptshaping_context *context, const struct shaping_features *features)
4660 const DWRITE_TYPOGRAPHIC_FEATURES **user_features = context->user_features.features;
4661 unsigned int f, r, g, start_char, mask, shift, value;
4663 for (g = 0; g < context->glyph_count; ++g)
4664 context->glyph_infos[g].mask = context->global_mask;
4666 if (context->shaper->setup_masks)
4667 context->shaper->setup_masks(context, features);
4669 for (r = 0, start_char = 0; r < context->user_features.range_count; ++r)
4671 unsigned int start_glyph, end_glyph;
4673 if (start_char >= context->length)
4674 break;
4676 opentype_layout_get_glyph_range_for_text(context, start_char, start_char + context->user_features.range_lengths[r],
4677 &start_glyph, &end_glyph);
4678 start_char += context->user_features.range_lengths[r];
4680 if (start_glyph > end_glyph || end_glyph >= context->glyph_count)
4681 continue;
4683 for (f = 0; f < user_features[r]->featureCount; ++f)
4685 mask = shaping_features_get_mask(features, user_features[r]->features[f].nameTag, &shift);
4686 if (!mask)
4687 continue;
4689 value = (user_features[r]->features[f].parameter << shift) & mask;
4691 for (g = start_glyph; g <= end_glyph; ++g)
4692 context->glyph_infos[g].mask = (context->glyph_infos[g].mask & ~mask) | value;
4697 static void opentype_layout_apply_gpos_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
4699 struct lookup lookup = { 0 };
4700 if (opentype_layout_init_lookup(context->table, lookup_index, NULL, &lookup))
4701 opentype_layout_apply_gpos_lookup(context, &lookup);
4704 void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index,
4705 unsigned int language_index, struct shaping_features *features)
4707 struct lookups lookups = { 0 };
4708 unsigned int i;
4709 BOOL ret;
4711 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
4712 context->u.buffer.apply_context_lookup = opentype_layout_apply_gpos_context_lookup;
4713 opentype_layout_collect_lookups(context, script_index, language_index, features, &context->cache->gpos, &lookups);
4715 for (i = 0; i < context->glyph_count; ++i)
4716 opentype_set_glyph_props(context, i);
4717 opentype_layout_set_glyph_masks(context, features);
4719 for (i = 0; i < lookups.count; ++i)
4721 const struct lookup *lookup = &lookups.lookups[i];
4723 context->cur = 0;
4724 context->lookup_mask = lookup->mask;
4725 context->auto_zwnj = lookup->auto_zwnj;
4726 context->auto_zwj = lookup->auto_zwj;
4728 while (context->cur < context->glyph_count)
4730 ret = FALSE;
4732 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
4733 lookup_is_glyph_match(context, context->cur, lookup->flags))
4735 ret = opentype_layout_apply_gpos_lookup(context, lookup);
4738 if (!ret)
4739 context->cur++;
4743 heap_free(lookups.lookups);
4746 static void opentype_layout_replace_glyph(struct scriptshaping_context *context, UINT16 glyph)
4748 UINT16 orig_glyph = context->u.subst.glyphs[context->cur];
4749 if (glyph != orig_glyph)
4751 context->u.subst.glyphs[context->cur] = glyph;
4752 opentype_set_subst_glyph_props(context, context->cur);
4756 static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4757 unsigned int subtable_offset)
4759 const struct dwrite_fonttable *table = &context->table->table;
4760 UINT16 format, coverage, orig_glyph, glyph;
4761 unsigned int coverage_index;
4763 orig_glyph = glyph = context->u.subst.glyphs[context->cur];
4765 format = table_read_be_word(table, subtable_offset);
4767 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, coverage));
4769 if (format == 1)
4771 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4772 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4774 glyph = orig_glyph + table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, delta));
4776 else if (format == 2)
4778 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4779 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4781 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count),
4782 coverage_index, &glyph))
4784 return FALSE;
4787 else
4789 WARN("Unknown single substitution format %u.\n", format);
4790 return FALSE;
4793 opentype_layout_replace_glyph(context, glyph);
4794 context->cur++;
4796 return TRUE;
4799 static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *context, unsigned int count)
4801 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
4802 struct shaping_glyph_info *glyph_infos;
4803 unsigned int new_capacity;
4804 UINT16 *glyphs;
4805 BOOL ret;
4807 if (context->u.subst.capacity >= count)
4808 return TRUE;
4810 new_capacity = context->u.subst.capacity * 2;
4812 if ((glyphs = heap_realloc(context->u.subst.glyphs, new_capacity * sizeof(*glyphs))))
4813 context->u.subst.glyphs = glyphs;
4814 if ((glyph_props = heap_realloc(context->u.subst.glyph_props, new_capacity * sizeof(*glyph_props))))
4815 context->u.subst.glyph_props = glyph_props;
4816 if ((glyph_infos = heap_realloc(context->glyph_infos, new_capacity * sizeof(*glyph_infos))))
4817 context->glyph_infos = glyph_infos;
4819 if ((ret = (glyphs && glyph_props && glyph_infos)))
4820 context->u.subst.capacity = new_capacity;
4822 return ret;
4825 static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4826 unsigned int subtable_offset)
4828 const struct dwrite_fonttable *table = &context->table->table;
4829 UINT16 format, coverage, glyph, glyph_count;
4830 unsigned int i, idx, coverage_index;
4831 const UINT16 *glyphs;
4833 idx = context->cur;
4834 glyph = context->u.subst.glyphs[idx];
4836 format = table_read_be_word(table, subtable_offset);
4838 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, coverage));
4840 if (format == 1)
4842 UINT16 seq_offset;
4844 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4845 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4847 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, seq_count),
4848 coverage_index, &seq_offset))
4850 return FALSE;
4853 if (!(glyphs = table_read_array_be_word(table, subtable_offset + seq_offset, ~0u, &glyph_count))) return FALSE;
4855 if (glyph_count == 1)
4857 /* Equivalent of single substitution. */
4858 opentype_layout_replace_glyph(context, GET_BE_WORD(glyphs[0]));
4859 context->cur++;
4861 else if (glyph_count == 0)
4863 context->cur++;
4865 else
4867 unsigned int shift_len, src_idx, dest_idx, mask;
4869 /* Current glyph is also replaced. */
4870 glyph_count--;
4872 if (!(opentype_layout_gsub_ensure_buffer(context, context->glyph_count + glyph_count)))
4873 return FALSE;
4875 shift_len = context->cur + 1 < context->glyph_count ? context->glyph_count - context->cur - 1 : 0;
4877 if (shift_len)
4879 src_idx = context->cur + 1;
4880 dest_idx = src_idx + glyph_count;
4882 memmove(&context->u.subst.glyphs[dest_idx], &context->u.subst.glyphs[src_idx],
4883 shift_len * sizeof(*context->u.subst.glyphs));
4884 memmove(&context->u.subst.glyph_props[dest_idx], &context->u.subst.glyph_props[src_idx],
4885 shift_len * sizeof(*context->u.subst.glyph_props));
4886 memmove(&context->glyph_infos[dest_idx], &context->glyph_infos[src_idx],
4887 shift_len * sizeof(*context->glyph_infos));
4890 mask = context->glyph_infos[context->cur].mask;
4891 for (i = 0, idx = context->cur; i <= glyph_count; ++i)
4893 glyph = GET_BE_WORD(glyphs[i]);
4894 context->u.subst.glyphs[idx + i] = glyph;
4895 if (i)
4897 context->u.subst.glyph_props[idx + i].isClusterStart = 0;
4898 context->u.buffer.glyph_props[idx + i].components = 0;
4899 context->glyph_infos[idx + i].start_text_idx = 0;
4901 opentype_set_subst_glyph_props(context, idx + i);
4902 /* Inherit feature mask from original matched glyph. */
4903 context->glyph_infos[idx + i].mask = mask;
4906 context->cur += glyph_count + 1;
4907 context->glyph_count += glyph_count;
4910 else
4912 WARN("Unknown multiple substitution format %u.\n", format);
4913 return FALSE;
4916 return TRUE;
4919 static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4920 unsigned int subtable_offset)
4922 const struct dwrite_fonttable *table = &context->table->table;
4923 unsigned int idx, coverage_index;
4924 UINT16 format, coverage, glyph;
4926 idx = context->cur;
4927 glyph = context->u.subst.glyphs[idx];
4929 format = table_read_be_word(table, subtable_offset);
4931 if (format == 1)
4933 const struct ot_gsub_altsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
4934 unsigned int shift, alt_index;
4935 UINT16 set_offset;
4937 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, coverage));
4939 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
4940 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
4942 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, count),
4943 coverage_index, &set_offset))
4944 return FALSE;
4946 /* Argument is 1-based. */
4947 BitScanForward(&shift, context->lookup_mask);
4948 alt_index = (context->lookup_mask & context->glyph_infos[idx].mask) >> shift;
4949 if (!alt_index) return FALSE;
4951 if (!table_read_array_be_word(table, subtable_offset + set_offset, alt_index - 1, &glyph)) return FALSE;
4953 else
4955 WARN("Unexpected alternate substitution format %d.\n", format);
4956 return FALSE;
4959 opentype_layout_replace_glyph(context, glyph);
4960 context->cur++;
4962 return TRUE;
4965 static BOOL opentype_layout_context_match_input(const struct match_context *mc, unsigned int count, const UINT16 *input,
4966 unsigned int *end_offset, unsigned int *match_positions)
4968 struct match_data match_data = { .mc = mc, .subtable_offset = mc->input_offset };
4969 struct scriptshaping_context *context = mc->context;
4970 struct glyph_iterator iter;
4971 unsigned int i;
4973 if (count > GLYPH_CONTEXT_MAX_LENGTH)
4974 return FALSE;
4976 match_positions[0] = context->cur;
4978 glyph_iterator_init(context, mc->lookup->flags, context->cur, count - 1, &iter);
4979 iter.mask = context->lookup_mask;
4980 iter.match_func = mc->match_func;
4981 iter.match_data = &match_data;
4982 iter.glyph_data = input;
4984 for (i = 1; i < count; ++i)
4986 if (!glyph_iterator_next(&iter))
4987 return FALSE;
4989 match_positions[i] = iter.pos;
4992 *end_offset = iter.pos - context->cur + 1;
4994 return TRUE;
4997 static void opentype_layout_unsafe_to_break(struct scriptshaping_context *context, unsigned int idx)
4999 if (context->u.buffer.glyph_props[idx].isClusterStart)
5000 context->u.buffer.text_props[context->glyph_infos[idx].start_text_idx].canBreakShapingAfter = 0;
5003 static void opentype_layout_delete_glyph(struct scriptshaping_context *context, unsigned int idx)
5005 unsigned int shift_len;
5007 shift_len = context->glyph_count - context->cur - 1;
5009 if (shift_len)
5011 memmove(&context->u.buffer.glyphs[idx], &context->u.buffer.glyphs[idx + 1],
5012 shift_len * sizeof(*context->u.buffer.glyphs));
5013 memmove(&context->u.buffer.glyph_props[idx], &context->u.buffer.glyph_props[idx + 1],
5014 shift_len * sizeof(*context->u.buffer.glyph_props));
5015 memmove(&context->glyph_infos[idx], &context->glyph_infos[idx + 1], shift_len * sizeof(*context->glyph_infos));
5018 context->glyph_count--;
5021 static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset,
5022 const struct lookup *lookup)
5024 struct match_context mc = { .context = context, .lookup = lookup, .match_func = opentype_match_glyph_func };
5025 const struct dwrite_fonttable *gsub = &context->table->table;
5026 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5027 unsigned int i, j, comp_count, match_length = 0;
5028 const struct ot_gsub_lig *lig;
5029 UINT16 lig_glyph;
5031 comp_count = table_read_be_word(gsub, offset + FIELD_OFFSET(struct ot_gsub_lig, comp_count));
5033 if (!comp_count)
5034 return FALSE;
5036 lig = table_read_ensure(gsub, offset, FIELD_OFFSET(struct ot_gsub_lig, components[comp_count-1]));
5037 if (!lig)
5038 return FALSE;
5040 lig_glyph = GET_BE_WORD(lig->lig_glyph);
5042 if (comp_count == 1)
5044 opentype_layout_replace_glyph(context, lig_glyph);
5045 context->cur++;
5046 return TRUE;
5049 if (!opentype_layout_context_match_input(&mc, comp_count, lig->components, &match_length, match_positions))
5050 return FALSE;
5052 opentype_layout_replace_glyph(context, lig_glyph);
5053 context->u.buffer.glyph_props[context->cur].components = comp_count;
5055 /* Positioning against a ligature implies keeping track of ligature component
5056 glyph should be attached to. Update per-glyph property for interleaving glyphs,
5057 0 means attaching to last component, n - attaching to n-th glyph before last. */
5058 for (i = 1; i < comp_count; ++i)
5060 j = match_positions[i - 1] + 1;
5061 while (j < match_positions[i])
5063 context->u.buffer.glyph_props[j++].lig_component = comp_count - i;
5065 opentype_layout_unsafe_to_break(context, i);
5066 context->u.buffer.glyph_props[i].isClusterStart = 0;
5067 context->glyph_infos[i].start_text_idx = 0;
5070 /* Delete ligated glyphs, backwards to preserve index. */
5071 for (i = 1; i < comp_count; ++i)
5073 opentype_layout_delete_glyph(context, match_positions[comp_count - i]);
5076 /* Skip whole matched sequence, accounting for deleted glyphs. */
5077 context->cur += match_length - (comp_count - 1);
5079 return TRUE;
5082 static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5083 unsigned int subtable_offset)
5085 const struct dwrite_fonttable *table = &context->table->table;
5086 UINT16 format, coverage, glyph, lig_set_offset;
5087 unsigned int coverage_index;
5089 glyph = context->u.subst.glyphs[context->cur];
5091 format = table_read_be_word(table, subtable_offset);
5093 if (format == 1)
5095 const struct ot_gsub_ligsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
5096 unsigned int i;
5097 const UINT16 *offsets;
5098 UINT16 lig_count;
5100 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, coverage));
5102 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5103 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5105 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_set_count),
5106 coverage_index, &lig_set_offset))
5107 return FALSE;
5109 if (!(offsets = table_read_array_be_word(table, subtable_offset + lig_set_offset, ~0u, &lig_count)))
5110 return FALSE;
5112 /* First applicable ligature is used. */
5113 for (i = 0; i < lig_count; ++i)
5115 if (opentype_layout_apply_ligature(context, subtable_offset + lig_set_offset + GET_BE_WORD(offsets[i]), lookup))
5116 return TRUE;
5119 else
5120 WARN("Unexpected ligature substitution format %d.\n", format);
5122 return FALSE;
5125 static BOOL opentype_layout_context_match_backtrack(const struct match_context *mc, unsigned int count,
5126 const UINT16 *backtrack, unsigned int *match_start)
5128 struct match_data match_data = { .mc = mc, .subtable_offset = mc->backtrack_offset };
5129 struct scriptshaping_context *context = mc->context;
5130 struct glyph_iterator iter;
5131 unsigned int i;
5133 glyph_iterator_init(context, mc->lookup->flags, context->cur, count, &iter);
5134 iter.match_func = mc->match_func;
5135 iter.match_data = &match_data;
5136 iter.glyph_data = backtrack;
5137 iter.ignore_zwnj |= context->auto_zwnj;
5138 iter.ignore_zwj = 1;
5140 for (i = 0; i < count; ++i)
5142 if (!glyph_iterator_prev(&iter))
5143 return FALSE;
5146 *match_start = iter.pos;
5148 return TRUE;
5151 static BOOL opentype_layout_context_match_lookahead(const struct match_context *mc, unsigned int count,
5152 const UINT16 *lookahead, unsigned int offset, unsigned int *end_index)
5154 struct match_data match_data = { .mc = mc, .subtable_offset = mc->lookahead_offset };
5155 struct scriptshaping_context *context = mc->context;
5156 struct glyph_iterator iter;
5157 unsigned int i;
5159 glyph_iterator_init(context, mc->lookup->flags, context->cur + offset - 1, count, &iter);
5160 iter.match_func = mc->match_func;
5161 iter.match_data = &match_data;
5162 iter.glyph_data = lookahead;
5163 iter.ignore_zwnj |= context->auto_zwnj;
5164 iter.ignore_zwj = 1;
5166 for (i = 0; i < count; ++i)
5168 if (!glyph_iterator_next(&iter))
5169 return FALSE;
5172 *end_index = iter.pos;
5174 return TRUE;
5177 static BOOL opentype_layout_context_apply_lookup(struct scriptshaping_context *context, unsigned int count,
5178 unsigned int *match_positions, unsigned int lookup_count, const UINT16 *lookup_records, unsigned int match_length)
5180 unsigned int i, j;
5181 int end, delta;
5183 if (!context->nesting_level_left)
5184 return TRUE;
5186 end = context->cur + match_length;
5188 for (i = 0; i < lookup_count; ++i)
5190 unsigned int idx = GET_BE_WORD(lookup_records[i]);
5191 unsigned int orig_len, lookup_index, next;
5193 if (idx >= count)
5194 continue;
5196 context->cur = match_positions[idx];
5198 orig_len = context->glyph_count;
5200 lookup_index = GET_BE_WORD(lookup_records[i+1]);
5202 --context->nesting_level_left;
5203 context->u.buffer.apply_context_lookup(context, lookup_index);
5204 ++context->nesting_level_left;
5206 delta = context->glyph_count - orig_len;
5207 if (!delta)
5208 continue;
5210 end += delta;
5211 if (end <= (int)match_positions[idx])
5213 end = match_positions[idx];
5214 break;
5217 next = idx + 1;
5219 if (delta > 0)
5221 if (delta + count > GLYPH_CONTEXT_MAX_LENGTH)
5222 break;
5224 else
5226 delta = max(delta, (int)next - (int)count);
5227 next -= delta;
5230 memmove(match_positions + next + delta, match_positions + next,
5231 (count - next) * sizeof (*match_positions));
5232 next += delta;
5233 count += delta;
5235 for (j = idx + 1; j < next; j++)
5236 match_positions[j] = match_positions[j - 1] + 1;
5238 for (; next < count; next++)
5239 match_positions[next] += delta;
5242 context->cur = end;
5244 return TRUE;
5247 static BOOL opentype_layout_apply_chain_context_match(unsigned int backtrack_count, const UINT16 *backtrack,
5248 unsigned int input_count, const UINT16 *input, unsigned int lookahead_count, const UINT16 *lookahead,
5249 unsigned int lookup_count, const UINT16 *lookup_records, const struct match_context *mc)
5251 unsigned int start_index = 0, match_length = 0, end_index = 0;
5252 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5254 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5255 opentype_layout_context_match_backtrack(mc, backtrack_count, backtrack, &start_index) &&
5256 opentype_layout_context_match_lookahead(mc, lookahead_count, lookahead, input_count, &end_index) &&
5257 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count, lookup_records, match_length);
5260 static BOOL opentype_layout_apply_chain_rule_set(const struct match_context *mc, unsigned int offset)
5262 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5263 const struct dwrite_fonttable *table = &mc->context->table->table;
5264 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5265 const struct ot_gsubgpos_ruleset *ruleset;
5266 unsigned int i, count;
5268 count = table_read_be_word(table, offset);
5269 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5271 for (i = 0; i < count; ++i)
5273 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5275 backtrack_count = table_read_be_word(table, rule_offset);
5276 rule_offset += 2;
5277 backtrack = table_read_ensure(table, rule_offset, backtrack_count * sizeof(*backtrack));
5278 rule_offset += backtrack_count * sizeof(*backtrack);
5280 if (!(input_count = table_read_be_word(table, rule_offset)))
5281 continue;
5283 rule_offset += 2;
5284 input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input));
5285 rule_offset += (input_count - 1) * sizeof(*input);
5287 lookahead_count = table_read_be_word(table, rule_offset);
5288 rule_offset += 2;
5289 lookahead = table_read_ensure(table, rule_offset, lookahead_count * sizeof(*lookahead));
5290 rule_offset += lookahead_count * sizeof(*lookahead);
5292 lookup_count = table_read_be_word(table, rule_offset);
5293 rule_offset += 2;
5294 lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records));
5296 /* First applicable rule is used. */
5297 if (opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input, lookahead_count,
5298 lookahead, lookup_count, lookup_records, mc))
5300 return TRUE;
5304 return FALSE;
5307 static BOOL opentype_layout_apply_context_match(unsigned int input_count, const UINT16 *input, unsigned int lookup_count,
5308 const UINT16 *lookup_records, const struct match_context *mc)
5310 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5311 unsigned int match_length = 0;
5313 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5314 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count,
5315 lookup_records, match_length);
5318 static BOOL opentype_layout_apply_rule_set(const struct match_context *mc, unsigned int offset)
5320 unsigned int input_count, lookup_count;
5321 const struct dwrite_fonttable *table = &mc->context->table->table;
5322 const UINT16 *input, *lookup_records;
5323 const struct ot_gsubgpos_ruleset *ruleset;
5324 unsigned int i, count;
5326 count = table_read_be_word(table, offset);
5327 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5329 for (i = 0; i < count; ++i)
5331 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5333 if (!(input_count = table_read_be_word(table, rule_offset)))
5334 continue;
5335 rule_offset += 2;
5337 if (!(lookup_count = table_read_be_word(table, rule_offset)))
5338 continue;
5339 rule_offset += 2;
5341 if (!(input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input))))
5342 continue;
5343 rule_offset += (input_count - 1) * sizeof(*input);
5345 if (!(lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records))))
5346 continue;
5348 /* First applicable rule is used. */
5349 if (opentype_layout_apply_context_match(input_count, input, lookup_count, lookup_records, mc))
5350 return TRUE;
5353 return FALSE;
5356 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
5357 unsigned int subtable_offset)
5359 struct match_context mc = { .context = context, .lookup = lookup };
5360 const struct dwrite_fonttable *table = &context->table->table;
5361 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5362 UINT16 glyph, format, coverage;
5363 BOOL ret = FALSE;
5365 glyph = context->u.subst.glyphs[context->cur];
5367 format = table_read_be_word(table, subtable_offset);
5369 if (format == 1)
5371 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5373 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5374 if (coverage_index == GLYPH_NOT_COVERED)
5375 return FALSE;
5377 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5378 if (coverage_index >= count)
5379 return FALSE;
5381 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5382 rulesets[coverage_index]));
5383 offset += subtable_offset;
5385 mc.match_func = opentype_match_glyph_func;
5387 ret = opentype_layout_apply_rule_set(&mc, offset);
5389 else if (format == 2)
5391 unsigned int input_classdef, rule_set_idx;
5393 offset = subtable_offset + 2 /* format */;
5395 coverage = table_read_be_word(table, offset);
5396 offset += 2;
5398 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5399 if (coverage_index == GLYPH_NOT_COVERED)
5400 return FALSE;
5402 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5403 offset += 2;
5405 count = table_read_be_word(table, offset);
5406 offset+= 2;
5408 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5409 if (rule_set_idx >= count)
5410 return FALSE;
5412 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5413 offset += subtable_offset;
5415 mc.input_offset = input_classdef;
5416 mc.match_func = opentype_match_class_func;
5418 ret = opentype_layout_apply_rule_set(&mc, offset);
5420 else if (format == 3)
5422 unsigned int input_count, lookup_count;
5423 const UINT16 *input, *lookup_records;
5425 offset = subtable_offset + 2 /* format */;
5427 input_count = table_read_be_word(table, offset);
5428 offset += 2;
5430 if (!input_count)
5431 return FALSE;
5433 lookup_count = table_read_be_word(table, offset);
5434 offset += 2;
5436 if (!(input = table_read_ensure(table, offset, sizeof(*input) * input_count)))
5437 return FALSE;
5438 offset += sizeof(*input) * input_count;
5440 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5441 if (coverage_index == GLYPH_NOT_COVERED)
5442 return FALSE;
5444 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5446 mc.input_offset = subtable_offset;
5447 mc.match_func = opentype_match_coverage_func;
5449 ret = opentype_layout_apply_context_match(input_count, input + 1, lookup_count, lookup_records, &mc);
5451 else
5452 WARN("Unknown contextual substitution format %u.\n", format);
5454 return ret;
5457 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
5458 unsigned int subtable_offset)
5460 struct match_context mc = { .context = context, .lookup = lookup };
5461 const struct dwrite_fonttable *table = &context->table->table;
5462 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5463 UINT16 glyph, format, coverage;
5464 BOOL ret = FALSE;
5466 glyph = context->u.subst.glyphs[context->cur];
5468 format = table_read_be_word(table, subtable_offset);
5470 if (format == 1)
5472 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5474 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5475 if (coverage_index == GLYPH_NOT_COVERED)
5476 return FALSE;
5478 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5479 if (coverage_index >= count)
5480 return FALSE;
5482 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5483 rulesets[coverage_index]));
5484 offset += subtable_offset;
5486 mc.match_func = opentype_match_glyph_func;
5488 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5490 else if (format == 2)
5492 unsigned int backtrack_classdef, input_classdef, lookahead_classdef, rule_set_idx;
5494 offset = subtable_offset + 2 /* format */;
5496 coverage = table_read_be_word(table, offset);
5497 offset += 2;
5499 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5500 if (coverage_index == GLYPH_NOT_COVERED)
5501 return FALSE;
5503 backtrack_classdef = table_read_be_word(table, offset) + subtable_offset;
5504 offset += 2;
5506 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5507 offset += 2;
5509 lookahead_classdef = table_read_be_word(table, offset) + subtable_offset;
5510 offset += 2;
5512 count = table_read_be_word(table, offset);
5513 offset+= 2;
5515 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5516 if (rule_set_idx >= count)
5517 return FALSE;
5519 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5520 offset += subtable_offset;
5522 mc.backtrack_offset = backtrack_classdef;
5523 mc.input_offset = input_classdef;
5524 mc.lookahead_offset = lookahead_classdef;
5525 mc.match_func = opentype_match_class_func;
5527 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5529 else if (format == 3)
5531 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5532 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5534 offset = subtable_offset + 2 /* format */;
5536 backtrack_count = table_read_be_word(table, offset);
5537 offset += 2;
5538 backtrack = table_read_ensure(table, offset, backtrack_count * sizeof(*backtrack));
5539 offset += backtrack_count * sizeof(*backtrack);
5541 input_count = table_read_be_word(table, offset);
5542 offset += 2;
5543 input = table_read_ensure(table, offset, input_count * sizeof(*input));
5544 offset += input_count * sizeof(*input);
5546 lookahead_count = table_read_be_word(table, offset);
5547 offset += 2;
5548 lookahead = table_read_ensure(table, offset, lookahead_count * sizeof(*lookahead));
5549 offset += lookahead_count * sizeof(*lookahead);
5551 lookup_count = table_read_be_word(table, offset);
5552 offset += 2;
5553 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5555 if (input)
5556 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5558 if (coverage_index == GLYPH_NOT_COVERED)
5559 return FALSE;
5561 mc.backtrack_offset = subtable_offset;
5562 mc.input_offset = subtable_offset;
5563 mc.lookahead_offset = subtable_offset;
5564 mc.match_func = opentype_match_coverage_func;
5566 ret = opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input + 1, lookahead_count,
5567 lookahead, lookup_count, lookup_records, &mc);
5569 else
5570 WARN("Unknown chaining contextual substitution format %u.\n", format);
5572 return ret;
5575 static BOOL opentype_layout_apply_gsub_reverse_chain_context_substitution(struct scriptshaping_context *context,
5576 const struct lookup *lookup, unsigned int subtable_offset)
5578 const struct dwrite_fonttable *table = &context->table->table;
5579 unsigned int offset = subtable_offset;
5580 UINT16 glyph, format;
5582 if (context->nesting_level_left != SHAPE_MAX_NESTING_LEVEL)
5583 return FALSE;
5585 glyph = context->u.subst.glyphs[context->cur];
5587 format = table_read_be_word(table, offset);
5588 offset += 2;
5590 if (format == 1)
5592 struct match_context mc = { .context = context, .lookup = lookup };
5593 unsigned int start_index = 0, end_index = 0, backtrack_count, lookahead_count;
5594 unsigned int coverage, coverage_index;
5595 const UINT16 *backtrack, *lookahead;
5597 coverage = table_read_be_word(table, offset);
5598 offset += 2;
5600 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5601 if (coverage_index == GLYPH_NOT_COVERED)
5602 return FALSE;
5604 backtrack_count = table_read_be_word(table, offset);
5605 offset += 2;
5607 backtrack = table_read_ensure(table, offset, sizeof(*backtrack) * backtrack_count);
5608 offset += sizeof(*backtrack) * backtrack_count;
5610 lookahead_count = table_read_be_word(table, offset);
5611 offset += 2;
5613 lookahead = table_read_ensure(table, offset, sizeof(*lookahead) * lookahead_count);
5614 offset += sizeof(*lookahead) * lookahead_count;
5616 mc.match_func = opentype_match_coverage_func;
5617 mc.backtrack_offset = subtable_offset;
5618 mc.lookahead_offset = subtable_offset;
5620 if (opentype_layout_context_match_backtrack(&mc, backtrack_count, backtrack, &start_index) &&
5621 opentype_layout_context_match_lookahead(&mc, lookahead_count, lookahead, 1, &end_index))
5623 unsigned int glyph_count = table_read_be_word(table, offset);
5624 if (coverage_index >= glyph_count)
5625 return FALSE;
5626 offset += 2;
5628 glyph = table_read_be_word(table, offset + coverage_index * sizeof(glyph));
5629 opentype_layout_replace_glyph(context, glyph);
5631 return TRUE;
5634 else
5635 WARN("Unknown reverse chaining contextual substitution format %u.\n", format);
5637 return FALSE;
5640 static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
5642 unsigned int i, lookup_type;
5643 BOOL ret = FALSE;
5645 for (i = 0; i < lookup->subtable_count; ++i)
5647 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
5649 switch (lookup_type)
5651 case GSUB_LOOKUP_SINGLE_SUBST:
5652 ret = opentype_layout_apply_gsub_single_substitution(context, lookup, subtable_offset);
5653 break;
5654 case GSUB_LOOKUP_MULTIPLE_SUBST:
5655 ret = opentype_layout_apply_gsub_mult_substitution(context, lookup, subtable_offset);
5656 break;
5657 case GSUB_LOOKUP_ALTERNATE_SUBST:
5658 ret = opentype_layout_apply_gsub_alt_substitution(context, lookup, subtable_offset);
5659 break;
5660 case GSUB_LOOKUP_LIGATURE_SUBST:
5661 ret = opentype_layout_apply_gsub_lig_substitution(context, lookup, subtable_offset);
5662 break;
5663 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
5664 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
5665 break;
5666 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
5667 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
5668 break;
5669 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
5670 ret = opentype_layout_apply_gsub_reverse_chain_context_substitution(context, lookup, subtable_offset);
5671 break;
5672 case GSUB_LOOKUP_EXTENSION_SUBST:
5673 WARN("Invalid lookup type for extension substitution %#x.\n", lookup_type);
5674 break;
5675 default:
5676 WARN("Unknown lookup type %u.\n", lookup_type);
5679 if (ret)
5680 break;
5683 return ret;
5686 static unsigned int unicode_get_mirrored_char(unsigned int codepoint)
5688 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
5689 WCHAR mirror;
5690 /* TODO: check if mirroring for higher planes makes sense at all */
5691 if (codepoint > 0xffff) return codepoint;
5692 mirror = get_table_entry(wine_mirror_map, codepoint);
5693 return mirror ? mirror : codepoint;
5697 * 034F # Mn COMBINING GRAPHEME JOINER
5698 * 061C # Cf ARABIC LETTER MARK
5699 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5700 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5701 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5702 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5704 static unsigned int opentype_is_zero_width(unsigned int codepoint)
5706 return codepoint == 0x34f || codepoint == 0x61c || codepoint == 0xfeff ||
5707 (codepoint >= 0x180b && codepoint <= 0x180e) || (codepoint >= 0x200b && codepoint <= 0x200f);
5711 * 00AD # Cf SOFT HYPHEN
5712 * 034F # Mn COMBINING GRAPHEME JOINER
5713 * 061C # Cf ARABIC LETTER MARK
5714 * 115F..1160 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER
5715 * 17B4..17B5 # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
5716 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5717 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5718 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5719 * 202A..202E # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
5720 * 2060..2064 # Cf [5] WORD JOINER..INVISIBLE PLUS
5721 * 2065 # Cn <reserved-2065>
5722 * 2066..206F # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
5723 * 3164 # Lo HANGUL FILLER
5724 * FE00..FE0F # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
5725 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5726 * FFA0 # Lo HALFWIDTH HANGUL FILLER
5727 * FFF0..FFF8 # Cn [9] <reserved-FFF0>..<reserved-FFF8>
5728 * 1BCA0..1BCA3 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
5729 * 1D173..1D17A # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
5730 * E0000 # Cn <reserved-E0000>
5731 * E0001 # Cf LANGUAGE TAG
5732 * E0002..E001F # Cn [30] <reserved-E0002>..<reserved-E001F>
5733 * E0020..E007F # Cf [96] TAG SPACE..CANCEL TAG
5734 * E0080..E00FF # Cn [128] <reserved-E0080>..<reserved-E00FF>
5735 * E0100..E01EF # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
5736 * E01F0..E0FFF # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
5738 static unsigned int opentype_is_default_ignorable(unsigned int codepoint)
5740 if (codepoint < 0x80) return 0;
5741 return codepoint == 0xad ||
5742 codepoint == 0x34f ||
5743 codepoint == 0x61c ||
5744 (codepoint >= 0x17b4 && codepoint <= 0x17b5) ||
5745 (codepoint >= 0x180b && codepoint <= 0x180e) ||
5746 (codepoint >= 0x200b && codepoint <= 0x200f) ||
5747 (codepoint >= 0x202a && codepoint <= 0x202e) ||
5748 (codepoint >= 0x2060 && codepoint <= 0x206f) ||
5749 (codepoint >= 0xfe00 && codepoint <= 0xfe0f) ||
5750 codepoint == 0xfeff ||
5751 (codepoint >= 0xfff0 && codepoint <= 0xfff8) ||
5752 (codepoint >= 0x1d173 && codepoint <= 0x1d17a) ||
5753 (codepoint >= 0xe0000 && codepoint <= 0xe0fff);
5756 static unsigned int opentype_is_diacritic(unsigned int codepoint)
5758 WCHAR ch = codepoint;
5759 WORD type = 0;
5760 /* Ignore higher planes for now. */
5761 if (codepoint > 0xffff) return 0;
5762 GetStringTypeW(CT_CTYPE3, &ch, 1, &type);
5763 return !!(type & C3_DIACRITIC);
5766 static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, const struct shaping_features *features)
5768 unsigned int rtlm_mask = shaping_features_get_mask(features, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), NULL);
5769 const struct shaping_font_ops *font = context->cache->font;
5770 unsigned int i, g, c, codepoint, cluster_start_idx = 0;
5771 UINT16 *clustermap = context->u.subst.clustermap;
5772 const WCHAR *text = context->text;
5773 BOOL bmp;
5775 memset(context->u.subst.glyph_props, 0, context->u.subst.max_glyph_count * sizeof(*context->u.subst.glyph_props));
5776 memset(context->u.buffer.text_props, 0, context->length * sizeof(*context->u.buffer.text_props));
5778 for (i = 0; i < context->length; ++i)
5780 g = context->glyph_count;
5782 if ((bmp = !(IS_HIGH_SURROGATE(text[i]) && (i < context->length - 1) && IS_LOW_SURROGATE(text[i + 1]))))
5784 codepoint = text[i];
5786 else
5788 codepoint = 0x10000 + ((text[i] - 0xd800) << 10) + (text[i + 1] - 0xdc00);
5791 if (context->is_rtl)
5793 c = unicode_get_mirrored_char(codepoint);
5794 if (c != codepoint && font->has_glyph(context->cache->context, c))
5795 codepoint = c;
5796 else
5797 context->glyph_infos[i].mask |= rtlm_mask;
5800 /* TODO: should this check for glyph availability? */
5801 if (*context->u.subst.digits && codepoint >= '0' && codepoint <= '9')
5802 codepoint = context->u.subst.digits[codepoint - '0'];
5804 context->u.buffer.glyphs[g] = font->get_glyph(context->cache->context, codepoint);
5805 context->u.buffer.glyph_props[g].justification = SCRIPT_JUSTIFY_CHARACTER;
5806 opentype_set_subst_glyph_props(context, g);
5807 if (opentype_is_default_ignorable(codepoint))
5809 context->glyph_infos[g].props |= GLYPH_PROP_IGNORABLE;
5810 if (codepoint == 0x200d)
5811 context->glyph_infos[g].props |= GLYPH_PROP_ZWJ;
5812 else if (codepoint == 0x200c)
5813 context->glyph_infos[g].props |= GLYPH_PROP_ZWNJ;
5814 /* Mongolian FVSs, TAGs, COMBINING GRAPHEME JOINER */
5815 else if ((codepoint >= 0x180b && codepoint <= 0x180d) ||
5816 (codepoint >= 0xe0020 && codepoint <= 0xe007f) ||
5817 codepoint == 0x34f)
5819 context->glyph_infos[g].props |= GLYPH_PROP_HIDDEN;
5823 /* Group diacritics with preceding base. Glyph class is ignored here. */
5824 if (!g || !opentype_is_diacritic(codepoint))
5826 context->u.buffer.glyph_props[g].isClusterStart = 1;
5827 context->glyph_infos[g].start_text_idx = i;
5828 cluster_start_idx = g;
5830 if (opentype_is_zero_width(codepoint))
5831 context->u.buffer.glyph_props[g].isZeroWidthSpace = 1;
5833 context->u.buffer.glyph_props[g].components = 1;
5834 context->glyph_count++;
5836 /* Set initial cluster map here, it's used for setting user features masks. */
5837 clustermap[i] = cluster_start_idx;
5838 if (bmp)
5839 context->u.buffer.text_props[i].canBreakShapingAfter = 1;
5840 else
5842 clustermap[i + 1] = cluster_start_idx;
5843 context->u.buffer.text_props[i + 1].canBreakShapingAfter = 1;
5844 ++i;
5849 static BOOL opentype_is_gsub_lookup_reversed(const struct scriptshaping_context *context, const struct lookup *lookup)
5851 unsigned int lookup_type;
5853 opentype_layout_get_gsubgpos_subtable(context, lookup, 0, &lookup_type);
5854 return lookup_type == GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST;
5857 static void opentype_layout_apply_gsub_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
5859 struct lookup lookup = { 0 };
5860 if (opentype_layout_init_lookup(context->table, lookup_index, NULL, &lookup))
5861 opentype_layout_apply_gsub_lookup(context, &lookup);
5864 void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int script_index,
5865 unsigned int language_index, struct shaping_features *features)
5867 struct lookups lookups = { 0 };
5868 unsigned int i = 0, j, start_idx;
5869 BOOL ret;
5871 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
5872 context->u.buffer.apply_context_lookup = opentype_layout_apply_gsub_context_lookup;
5873 opentype_layout_collect_lookups(context, script_index, language_index, features, context->table, &lookups);
5875 opentype_get_nominal_glyphs(context, features);
5876 opentype_layout_set_glyph_masks(context, features);
5878 for (j = 0; j <= features->stage; ++j)
5880 for (; i < features->stages[j].last_lookup; ++i)
5882 const struct lookup *lookup = &lookups.lookups[i];
5884 context->lookup_mask = lookup->mask;
5885 context->auto_zwnj = lookup->auto_zwnj;
5886 context->auto_zwj = lookup->auto_zwj;
5888 if (!opentype_is_gsub_lookup_reversed(context, lookup))
5890 context->cur = 0;
5891 while (context->cur < context->glyph_count)
5893 ret = FALSE;
5895 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
5896 lookup_is_glyph_match(context, context->cur, lookup->flags))
5898 ret = opentype_layout_apply_gsub_lookup(context, lookup);
5901 if (!ret)
5902 context->cur++;
5905 else
5907 context->cur = context->glyph_count - 1;
5909 for (;;)
5911 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
5912 lookup_is_glyph_match(context, context->cur, lookup->flags))
5914 opentype_layout_apply_gsub_lookup(context, lookup);
5917 if (context->cur == 0) break;
5918 --context->cur;
5923 if (features->stages[j].func)
5924 features->stages[j].func(context);
5927 /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding
5928 text span to start_idx. */
5929 start_idx = 0;
5930 for (i = 1; i < context->glyph_count; ++i)
5932 if (context->u.buffer.glyph_props[i].isClusterStart)
5934 unsigned int start_text, end_text;
5936 start_text = context->glyph_infos[start_idx].start_text_idx;
5937 end_text = context->glyph_infos[i].start_text_idx;
5939 for (j = start_text; j < end_text; ++j)
5940 context->u.buffer.clustermap[j] = start_idx;
5942 start_idx = i;
5946 /* Fill the tail. */
5947 for (j = context->glyph_infos[start_idx].start_text_idx; j < context->length; ++j)
5948 context->u.buffer.clustermap[j] = start_idx;
5950 heap_free(lookups.lookups);
5953 static BOOL opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5954 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
5956 const struct dwrite_fonttable *table = &context->table->table;
5957 const UINT16 *offsets;
5958 unsigned int count;
5960 if (format == 1 || format == 2)
5962 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5963 return TRUE;
5965 else if (format == 3)
5967 count = table_read_be_word(table, subtable_offset + 2);
5968 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6, count * sizeof(*offsets))))
5969 return FALSE;
5971 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
5972 return TRUE;
5975 return FALSE;
5978 static BOOL opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
5979 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
5981 const struct dwrite_fonttable *table = &context->table->table;
5982 unsigned int count, backtrack_count;
5983 const UINT16 *offsets;
5985 if (format == 1 || format == 2)
5987 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
5988 return TRUE;
5990 else if (format == 3)
5992 backtrack_count = table_read_be_word(table, subtable_offset + 2);
5994 count = table_read_be_word(table, subtable_offset + 4 + backtrack_count * sizeof(*offsets));
5996 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6 + backtrack_count * sizeof(*offsets),
5997 count * sizeof(*offsets))))
5998 return FALSE;
6000 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
6001 return TRUE;
6004 return FALSE;
6007 static BOOL opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6008 const struct lookup *lookup)
6010 const struct dwrite_fonttable *gsub = &context->table->table;
6011 static const unsigned short gsub_formats[] =
6013 0, /* Unused */
6014 1, /* SingleSubst */
6015 1, /* MultipleSubst */
6016 1, /* AlternateSubst */
6017 1, /* LigatureSubst */
6018 3, /* ContextSubst */
6019 3, /* ChainContextSubst */
6020 0, /* Extension, unused */
6021 1, /* ReverseChainSubst */
6023 unsigned int i, coverage, lookup_type, format;
6025 for (i = 0; i < lookup->subtable_count; ++i)
6027 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
6029 format = table_read_be_word(gsub, subtable_offset);
6031 if (!format || format > ARRAY_SIZE(gsub_formats) || format > gsub_formats[lookup_type])
6032 break;
6034 coverage = table_read_be_word(gsub, subtable_offset + 2);
6036 switch (lookup_type)
6038 case GSUB_LOOKUP_SINGLE_SUBST:
6039 case GSUB_LOOKUP_MULTIPLE_SUBST:
6040 case GSUB_LOOKUP_ALTERNATE_SUBST:
6041 case GSUB_LOOKUP_LIGATURE_SUBST:
6042 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
6044 if (opentype_layout_is_glyph_covered(gsub, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6045 return TRUE;
6047 break;
6049 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
6051 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6052 return TRUE;
6054 break;
6056 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
6058 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6059 return TRUE;
6061 break;
6063 default:
6064 WARN("Unknown lookup type %u.\n", lookup_type);
6068 return FALSE;
6071 static BOOL opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6072 const struct lookup *lookup)
6074 const struct dwrite_fonttable *gpos = &context->table->table;
6075 static const unsigned short gpos_formats[] =
6077 0, /* Unused */
6078 2, /* SinglePos */
6079 2, /* PairPos */
6080 1, /* CursivePos */
6081 1, /* MarkBasePos */
6082 1, /* MarkLigPos */
6083 1, /* MarkMarkPos */
6084 3, /* ContextPos */
6085 3, /* ChainContextPos */
6086 0, /* Extension, unused */
6088 unsigned int i, coverage, lookup_type, format;
6090 for (i = 0; i < lookup->subtable_count; ++i)
6092 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
6094 format = table_read_be_word(gpos, subtable_offset);
6096 if (!format || format > ARRAY_SIZE(gpos_formats) || format > gpos_formats[lookup_type])
6097 break;
6099 coverage = table_read_be_word(gpos, subtable_offset + 2);
6101 switch (lookup_type)
6103 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
6104 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
6105 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
6106 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
6107 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
6108 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
6110 if (opentype_layout_is_glyph_covered(gpos, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6111 return TRUE;
6113 break;
6115 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
6117 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6118 return TRUE;
6120 break;
6122 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
6124 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6125 return TRUE;
6127 break;
6129 default:
6130 WARN("Unknown lookup type %u.\n", lookup_type);
6134 return FALSE;
6137 typedef BOOL (*p_lookup_is_glyph_covered_func)(struct scriptshaping_context *context, UINT16 glyph, const struct lookup *lookup);
6139 BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index,
6140 unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count,
6141 const UINT16 *glyphs, UINT8 *feature_applies)
6143 p_lookup_is_glyph_covered_func func_is_covered;
6144 struct shaping_features features = { 0 };
6145 struct lookups lookups = { 0 };
6146 BOOL ret = FALSE, is_covered;
6147 unsigned int i, j, applies;
6149 features.features = feature;
6150 features.count = 1;
6152 for (i = 0; i < context->glyph_count; ++i)
6153 opentype_set_glyph_props(context, i);
6155 opentype_layout_collect_lookups(context, script_index, language_index, &features, context->table, &lookups);
6157 func_is_covered = context->table == &context->cache->gsub ? opentype_layout_gsub_lookup_is_glyph_covered :
6158 opentype_layout_gpos_lookup_is_glyph_covered;
6160 for (i = 0; i < lookups.count; ++i)
6162 struct lookup *lookup = &lookups.lookups[i];
6164 applies = 0;
6165 for (j = 0; j < context->glyph_count; ++j)
6167 if (lookup_is_glyph_match(context, j, lookup->flags))
6169 if ((is_covered = func_is_covered(context, glyphs[i], lookup)))
6170 ++applies;
6171 feature_applies[j] |= is_covered;
6175 if ((ret = (applies == context->glyph_count)))
6176 break;
6179 heap_free(lookups.lookups);
6181 return ret;
6184 BOOL opentype_has_vertical_variants(struct dwrite_fontface *fontface)
6186 unsigned int i, j, count = 0, lookup_type, subtable_offset;
6187 struct shaping_features features = { 0 };
6188 struct shaping_feature vert_feature = { 0 };
6189 struct scriptshaping_context context = { 0 };
6190 struct lookups lookups = { 0 };
6191 UINT16 format;
6193 context.cache = fontface_get_shaping_cache(fontface);
6194 context.table = &context.cache->gsub;
6196 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6197 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6198 vert_feature.max_value = 1;
6199 vert_feature.default_value = 1;
6201 features.features = &vert_feature;
6202 features.count = features.capacity = 1;
6204 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6206 for (i = 0; i < lookups.count && !count; ++i)
6208 const struct dwrite_fonttable *table = &context.table->table;
6209 const struct lookup *lookup = &lookups.lookups[i];
6211 for (j = 0; j < lookup->subtable_count && !count; ++j)
6213 subtable_offset = opentype_layout_get_gsubgpos_subtable(&context, lookup, j, &lookup_type);
6215 if (lookup_type != GSUB_LOOKUP_SINGLE_SUBST)
6216 continue;
6218 format = table_read_be_word(table, subtable_offset);
6220 if (format == 1)
6222 count = 1;
6224 else if (format == 2)
6226 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count));
6228 else
6229 WARN("Unrecognized single substitution format %u.\n", format);
6233 heap_free(lookups.lookups);
6235 return !!count;
6238 HRESULT opentype_get_vertical_glyph_variants(struct dwrite_fontface *fontface, unsigned int glyph_count,
6239 const UINT16 *nominal_glyphs, UINT16 *glyphs)
6241 struct shaping_features features = { 0 };
6242 struct shaping_feature vert_feature = { 0 };
6243 struct scriptshaping_context context = { 0 };
6244 struct lookups lookups = { 0 };
6245 unsigned int i;
6247 memcpy(glyphs, nominal_glyphs, glyph_count * sizeof(*glyphs));
6249 if (!(fontface->flags & FONTFACE_HAS_VERTICAL_VARIANTS))
6250 return S_OK;
6252 context.cache = fontface_get_shaping_cache(fontface);
6253 context.u.subst.glyphs = glyphs;
6254 context.u.subst.glyph_props = heap_calloc(glyph_count, sizeof(*context.u.subst.glyph_props));
6255 context.u.subst.max_glyph_count = glyph_count;
6256 context.u.subst.capacity = glyph_count;
6257 context.glyph_infos = heap_alloc_zero(sizeof(*context.glyph_infos) * glyph_count);
6258 context.table = &context.cache->gsub;
6260 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6261 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6262 vert_feature.max_value = 1;
6263 vert_feature.default_value = 1;
6265 features.features = &vert_feature;
6266 features.count = features.capacity = 1;
6268 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6269 opentype_layout_set_glyph_masks(&context, &features);
6271 for (i = 0; i < lookups.count; ++i)
6273 const struct lookup *lookup = &lookups.lookups[i];
6275 context.cur = 0;
6276 while (context.cur < context.glyph_count)
6278 BOOL ret = FALSE;
6280 if (lookup_is_glyph_match(&context, context.cur, lookup->flags))
6281 ret = opentype_layout_apply_gsub_lookup(&context, lookup);
6283 if (!ret)
6284 context.cur++;
6288 heap_free(context.u.subst.glyph_props);
6289 heap_free(context.glyph_infos);
6290 heap_free(lookups.lookups);
6292 return S_OK;