msi: Make TransformView_Create static.
[wine.git] / dlls / dwrite / opentype.c
blobfeacdfd9353b0c0705e392b84f4297d85b758909
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 <stdint.h>
25 #include "dwrite_private.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
29 #define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d')
30 #define MS_HHEA_TAG DWRITE_MAKE_OPENTYPE_TAG('h','h','e','a')
31 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
32 #define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2')
33 #define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t')
34 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
35 #define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
36 #define MS_NAME_TAG DWRITE_MAKE_OPENTYPE_TAG('n','a','m','e')
37 #define MS_GLYF_TAG DWRITE_MAKE_OPENTYPE_TAG('g','l','y','f')
38 #define MS_CFF__TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F',' ')
39 #define MS_CFF2_TAG DWRITE_MAKE_OPENTYPE_TAG('C','F','F','2')
40 #define MS_CPAL_TAG DWRITE_MAKE_OPENTYPE_TAG('C','P','A','L')
41 #define MS_COLR_TAG DWRITE_MAKE_OPENTYPE_TAG('C','O','L','R')
42 #define MS_SVG__TAG DWRITE_MAKE_OPENTYPE_TAG('S','V','G',' ')
43 #define MS_SBIX_TAG DWRITE_MAKE_OPENTYPE_TAG('s','b','i','x')
44 #define MS_MAXP_TAG DWRITE_MAKE_OPENTYPE_TAG('m','a','x','p')
45 #define MS_CBLC_TAG DWRITE_MAKE_OPENTYPE_TAG('C','B','L','C')
46 #define MS_CMAP_TAG DWRITE_MAKE_OPENTYPE_TAG('c','m','a','p')
47 #define MS_META_TAG DWRITE_MAKE_OPENTYPE_TAG('m','e','t','a')
48 #define MS_KERN_TAG DWRITE_MAKE_OPENTYPE_TAG('k','e','r','n')
49 #define MS_FVAR_TAG DWRITE_MAKE_OPENTYPE_TAG('f','v','a','r')
51 /* 'sbix' formats */
52 #define MS_PNG__TAG DWRITE_MAKE_OPENTYPE_TAG('p','n','g',' ')
53 #define MS_JPG__TAG DWRITE_MAKE_OPENTYPE_TAG('j','p','g',' ')
54 #define MS_TIFF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','i','f','f')
56 #define MS_WOFF_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','F')
57 #define MS_WOF2_TAG DWRITE_MAKE_OPENTYPE_TAG('w','O','F','2')
59 /* 'meta' tags */
60 #define MS_DLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('d','l','n','g')
61 #define MS_SLNG_TAG DWRITE_MAKE_OPENTYPE_TAG('s','l','n','g')
63 #ifdef WORDS_BIGENDIAN
64 #define GET_BE_WORD(x) (x)
65 #define GET_BE_DWORD(x) (x)
66 #define GET_BE_FIXED(x) (x / 65536.0f)
67 #else
68 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
69 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
70 #define GET_BE_FIXED(x) ((int32_t)GET_BE_DWORD(x) / 65536.0f)
71 #endif
73 #define GLYPH_CONTEXT_MAX_LENGTH 64
74 #define SHAPE_MAX_NESTING_LEVEL 6
76 struct ttc_header
78 uint32_t tag;
79 uint16_t major_version;
80 uint16_t minor_version;
81 uint32_t num_fonts;
82 uint32_t offsets[1];
85 struct ot_table_dir
87 uint32_t version;
88 uint16_t numTables;
89 uint16_t searchRange;
90 uint16_t entrySelector;
91 uint16_t rangeShift;
94 struct ot_table_record
96 uint32_t tag;
97 uint32_t checksum;
98 uint32_t offset;
99 uint32_t length;
102 struct cmap_encoding_record
104 uint16_t platformID;
105 uint16_t encodingID;
106 uint32_t offset;
109 struct cmap_header
111 uint16_t version;
112 uint16_t num_tables;
113 struct cmap_encoding_record tables[1];
116 enum OPENTYPE_CMAP_TABLE_FORMAT
118 OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING = 4,
119 OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE = 12
122 enum opentype_cmap_table_platform
124 OPENTYPE_CMAP_TABLE_PLATFORM_WIN = 3,
127 enum opentype_cmap_table_encoding
129 OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL = 0,
130 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_BMP = 1,
131 OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_FULL = 10,
134 /* PANOSE is 10 bytes in size, need to pack the structure properly */
135 #include "pshpack2.h"
136 struct tt_head
138 USHORT majorVersion;
139 USHORT minorVersion;
140 ULONG revision;
141 ULONG checksumadj;
142 ULONG magic;
143 USHORT flags;
144 USHORT unitsPerEm;
145 ULONGLONG created;
146 ULONGLONG modified;
147 SHORT xMin;
148 SHORT yMin;
149 SHORT xMax;
150 SHORT yMax;
151 USHORT macStyle;
152 USHORT lowestRecPPEM;
153 SHORT direction_hint;
154 SHORT index_format;
155 SHORT glyphdata_format;
158 enum tt_head_macstyle
160 TT_HEAD_MACSTYLE_BOLD = 1 << 0,
161 TT_HEAD_MACSTYLE_ITALIC = 1 << 1,
162 TT_HEAD_MACSTYLE_UNDERLINE = 1 << 2,
163 TT_HEAD_MACSTYLE_OUTLINE = 1 << 3,
164 TT_HEAD_MACSTYLE_SHADOW = 1 << 4,
165 TT_HEAD_MACSTYLE_CONDENSED = 1 << 5,
166 TT_HEAD_MACSTYLE_EXTENDED = 1 << 6,
169 struct tt_post
171 uint32_t Version;
172 int32_t italicAngle;
173 int16_t underlinePosition;
174 int16_t underlineThickness;
175 uint32_t fixed_pitch;
176 uint32_t minmemType42;
177 uint32_t maxmemType42;
178 uint32_t minmemType1;
179 uint32_t maxmemType1;
182 struct tt_os2
184 USHORT version;
185 SHORT xAvgCharWidth;
186 USHORT usWeightClass;
187 USHORT usWidthClass;
188 SHORT fsType;
189 SHORT ySubscriptXSize;
190 SHORT ySubscriptYSize;
191 SHORT ySubscriptXOffset;
192 SHORT ySubscriptYOffset;
193 SHORT ySuperscriptXSize;
194 SHORT ySuperscriptYSize;
195 SHORT ySuperscriptXOffset;
196 SHORT ySuperscriptYOffset;
197 SHORT yStrikeoutSize;
198 SHORT yStrikeoutPosition;
199 SHORT sFamilyClass;
200 PANOSE panose;
201 ULONG ulUnicodeRange1;
202 ULONG ulUnicodeRange2;
203 ULONG ulUnicodeRange3;
204 ULONG ulUnicodeRange4;
205 CHAR achVendID[4];
206 USHORT fsSelection;
207 USHORT usFirstCharIndex;
208 USHORT usLastCharIndex;
209 /* According to the Apple spec, original version didn't have the below fields,
210 * version numbers were taken from the OpenType spec.
212 /* version 0 (TrueType 1.5) */
213 USHORT sTypoAscender;
214 USHORT sTypoDescender;
215 USHORT sTypoLineGap;
216 USHORT usWinAscent;
217 USHORT usWinDescent;
218 /* version 1 (TrueType 1.66) */
219 ULONG ulCodePageRange1;
220 ULONG ulCodePageRange2;
221 /* version 2 (OpenType 1.2) */
222 SHORT sxHeight;
223 SHORT sCapHeight;
224 USHORT usDefaultChar;
225 USHORT usBreakChar;
226 USHORT usMaxContext;
229 struct tt_hhea
231 uint16_t majorVersion;
232 uint16_t minorVersion;
233 int16_t ascender;
234 int16_t descender;
235 int16_t linegap;
236 uint16_t advanceWidthMax;
237 int16_t minLeftSideBearing;
238 int16_t minRightSideBearing;
239 int16_t xMaxExtent;
240 int16_t caretSlopeRise;
241 int16_t caretSlopeRun;
242 int16_t caretOffset;
243 int16_t reserved[4];
244 int16_t metricDataFormat;
245 uint16_t numberOfHMetrics;
248 struct sbix_header
250 uint16_t version;
251 uint16_t flags;
252 uint32_t num_strikes;
253 uint32_t strike_offset[1];
256 struct sbix_strike
258 uint16_t ppem;
259 uint16_t ppi;
260 uint32_t glyphdata_offsets[1];
263 struct sbix_glyph_data
265 int16_t originOffsetX;
266 int16_t originOffsetY;
267 uint32_t graphic_type;
268 uint8_t data[1];
271 struct maxp
273 DWORD version;
274 WORD num_glyphs;
277 struct cblc_header
279 uint16_t major_version;
280 uint16_t minor_version;
281 uint32_t num_sizes;
284 struct sbit_line_metrics
286 int8_t ascender;
287 int8_t descender;
288 uint8_t widthMax;
289 int8_t caretSlopeNumerator;
290 int8_t caretSlopeDenominator;
291 int8_t caretOffset;
292 int8_t minOriginSB;
293 int8_t minAdvanceSB;
294 int8_t maxBeforeBL;
295 int8_t minAfterBL;
296 int8_t pad1;
297 int8_t pad2;
300 struct cblc_bitmapsize_table
302 uint32_t indexSubTableArrayOffset;
303 uint32_t indexTablesSize;
304 uint32_t numberofIndexSubTables;
305 uint32_t colorRef;
306 struct sbit_line_metrics hori;
307 struct sbit_line_metrics vert;
308 uint16_t startGlyphIndex;
309 uint16_t endGlyphIndex;
310 uint8_t ppemX;
311 uint8_t ppemY;
312 uint8_t bit_depth;
313 int8_t flags;
316 struct gasp_range
318 WORD max_ppem;
319 WORD flags;
322 struct gasp_header
324 WORD version;
325 WORD num_ranges;
326 struct gasp_range ranges[1];
329 enum OS2_FSSELECTION {
330 OS2_FSSELECTION_ITALIC = 1 << 0,
331 OS2_FSSELECTION_UNDERSCORE = 1 << 1,
332 OS2_FSSELECTION_NEGATIVE = 1 << 2,
333 OS2_FSSELECTION_OUTLINED = 1 << 3,
334 OS2_FSSELECTION_STRIKEOUT = 1 << 4,
335 OS2_FSSELECTION_BOLD = 1 << 5,
336 OS2_FSSELECTION_REGULAR = 1 << 6,
337 OS2_FSSELECTION_USE_TYPO_METRICS = 1 << 7,
338 OS2_FSSELECTION_WWS = 1 << 8,
339 OS2_FSSELECTION_OBLIQUE = 1 << 9
342 struct name_record
344 uint16_t platformID;
345 uint16_t encodingID;
346 uint16_t languageID;
347 uint16_t nameID;
348 uint16_t length;
349 uint16_t offset;
352 struct name_header
354 uint16_t format;
355 uint16_t count;
356 uint16_t stringOffset;
357 struct name_record records[1];
360 struct vdmx_header
362 uint16_t version;
363 uint16_t num_recs;
364 uint16_t num_ratios;
367 struct vdmx_ratio
369 uint8_t bCharSet;
370 uint8_t xRatio;
371 uint8_t yStartRatio;
372 uint8_t yEndRatio;
375 struct vdmx_vtable
377 uint16_t yPelHeight;
378 int16_t yMax;
379 int16_t yMin;
382 struct vdmx_group
384 uint16_t recs;
385 uint8_t startsz;
386 uint8_t endsz;
387 struct vdmx_vtable entries[1];
390 struct ot_feature_record
392 uint32_t tag;
393 uint16_t offset;
396 struct ot_feature_list
398 uint16_t feature_count;
399 struct ot_feature_record features[1];
402 struct ot_langsys
404 uint16_t lookup_order; /* Reserved */
405 uint16_t required_feature_index;
406 uint16_t feature_count;
407 uint16_t feature_index[1];
410 struct ot_langsys_record
412 uint32_t tag;
413 uint16_t langsys;
416 struct ot_script
418 uint16_t default_langsys;
419 uint16_t langsys_count;
420 struct ot_langsys_record langsys[1];
423 struct ot_script_record
425 uint32_t tag;
426 uint16_t script;
429 struct ot_script_list
431 uint16_t script_count;
432 struct ot_script_record scripts[1];
435 enum ot_gdef_class
437 GDEF_CLASS_UNCLASSIFIED = 0,
438 GDEF_CLASS_BASE = 1,
439 GDEF_CLASS_LIGATURE = 2,
440 GDEF_CLASS_MARK = 3,
441 GDEF_CLASS_COMPONENT = 4,
442 GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
445 struct gdef_header
447 uint16_t major_version;
448 uint16_t minor_version;
449 uint16_t classdef;
450 uint16_t attach_list;
451 uint16_t ligcaret_list;
452 uint16_t markattach_classdef;
453 uint16_t markglyphsetdef;
456 struct ot_gdef_classdef_format1
458 uint16_t format;
459 uint16_t start_glyph;
460 uint16_t glyph_count;
461 uint16_t classes[1];
464 struct ot_gdef_class_range
466 uint16_t start_glyph;
467 uint16_t end_glyph;
468 uint16_t glyph_class;
471 struct ot_gdef_classdef_format2
473 uint16_t format;
474 uint16_t range_count;
475 struct ot_gdef_class_range ranges[1];
478 struct ot_gdef_markglyphsets
480 uint16_t format;
481 uint16_t count;
482 uint32_t offsets[1];
485 struct gpos_gsub_header
487 uint16_t major_version;
488 uint16_t minor_version;
489 uint16_t script_list;
490 uint16_t feature_list;
491 uint16_t lookup_list;
494 enum gsub_gpos_lookup_flags
496 LOOKUP_FLAG_RTL = 0x1, /* Only used for GPOS cursive attachments. */
498 LOOKUP_FLAG_IGNORE_BASE = 0x2,
499 LOOKUP_FLAG_IGNORE_LIGATURES = 0x4,
500 LOOKUP_FLAG_IGNORE_MARKS = 0x8,
501 LOOKUP_FLAG_IGNORE_MASK = 0xe, /* Combined LOOKUP_FLAG_IGNORE_* flags. */
503 LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x10,
504 LOOKUP_FLAG_MARK_ATTACHMENT_TYPE = 0xff00,
507 enum attach_type
509 GLYPH_ATTACH_NONE = 0,
510 GLYPH_ATTACH_MARK,
511 GLYPH_ATTACH_CURSIVE,
514 enum glyph_prop_flags
516 GLYPH_PROP_BASE = LOOKUP_FLAG_IGNORE_BASE,
517 GLYPH_PROP_LIGATURE = LOOKUP_FLAG_IGNORE_LIGATURES,
518 GLYPH_PROP_MARK = LOOKUP_FLAG_IGNORE_MARKS,
520 GLYPH_PROP_ZWNJ = 0x10,
521 GLYPH_PROP_ZWJ = 0x20,
522 GLYPH_PROP_IGNORABLE = 0x40,
523 GLYPH_PROP_HIDDEN = 0x80,
525 GLYPH_PROP_MARK_ATTACH_CLASS_MASK = 0xff00, /* Used with LOOKUP_FLAG_MARK_ATTACHMENT_TYPE. */
526 GLYPH_PROP_ATTACH_TYPE_MASK = 0xff0000,
529 enum gpos_lookup_type
531 GPOS_LOOKUP_SINGLE_ADJUSTMENT = 1,
532 GPOS_LOOKUP_PAIR_ADJUSTMENT = 2,
533 GPOS_LOOKUP_CURSIVE_ATTACHMENT = 3,
534 GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT = 4,
535 GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT = 5,
536 GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT = 6,
537 GPOS_LOOKUP_CONTEXTUAL_POSITION = 7,
538 GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION = 8,
539 GPOS_LOOKUP_EXTENSION_POSITION = 9,
542 enum gsub_lookup_type
544 GSUB_LOOKUP_SINGLE_SUBST = 1,
545 GSUB_LOOKUP_MULTIPLE_SUBST = 2,
546 GSUB_LOOKUP_ALTERNATE_SUBST = 3,
547 GSUB_LOOKUP_LIGATURE_SUBST = 4,
548 GSUB_LOOKUP_CONTEXTUAL_SUBST = 5,
549 GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST = 6,
550 GSUB_LOOKUP_EXTENSION_SUBST = 7,
551 GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST = 8,
554 enum gpos_value_format
556 GPOS_VALUE_X_PLACEMENT = 0x1,
557 GPOS_VALUE_Y_PLACEMENT = 0x2,
558 GPOS_VALUE_X_ADVANCE = 0x4,
559 GPOS_VALUE_Y_ADVANCE = 0x8,
560 GPOS_VALUE_X_PLACEMENT_DEVICE = 0x10,
561 GPOS_VALUE_Y_PLACEMENT_DEVICE = 0x20,
562 GPOS_VALUE_X_ADVANCE_DEVICE = 0x40,
563 GPOS_VALUE_Y_ADVANCE_DEVICE = 0x80,
566 enum OPENTYPE_PLATFORM_ID
568 OPENTYPE_PLATFORM_UNICODE = 0,
569 OPENTYPE_PLATFORM_MAC,
570 OPENTYPE_PLATFORM_ISO,
571 OPENTYPE_PLATFORM_WIN,
572 OPENTYPE_PLATFORM_CUSTOM
575 struct ot_gsubgpos_extension_format1
577 uint16_t format;
578 uint16_t lookup_type;
579 uint32_t extension_offset;
582 struct ot_gsub_singlesubst_format1
584 uint16_t format;
585 uint16_t coverage;
586 int16_t delta;
589 struct ot_gsub_singlesubst_format2
591 uint16_t format;
592 uint16_t coverage;
593 uint16_t count;
594 uint16_t substitutes[1];
597 struct ot_gsub_multsubst_format1
599 uint16_t format;
600 uint16_t coverage;
601 uint16_t seq_count;
602 uint16_t seq[1];
605 struct ot_gsub_altsubst_format1
607 uint16_t format;
608 uint16_t coverage;
609 uint16_t count;
610 uint16_t sets[1];
613 struct ot_gsub_ligsubst_format1
615 uint16_t format;
616 uint16_t coverage;
617 uint16_t lig_set_count;
618 uint16_t lig_sets[1];
621 struct ot_gsub_ligset
623 uint16_t count;
624 uint16_t offsets[1];
627 struct ot_gsub_lig
629 uint16_t lig_glyph;
630 uint16_t comp_count;
631 uint16_t components[1];
634 struct ot_gsubgpos_context_format1
636 uint16_t format;
637 uint16_t coverage;
638 uint16_t ruleset_count;
639 uint16_t rulesets[1];
642 struct ot_gsubgpos_ruleset
644 uint16_t count;
645 uint16_t offsets[1];
648 struct ot_feature
650 uint16_t feature_params;
651 uint16_t lookup_count;
652 uint16_t lookuplist_index[1];
655 struct ot_lookup_list
657 uint16_t lookup_count;
658 uint16_t lookup[1];
661 struct ot_lookup_table
663 uint16_t lookup_type;
664 uint16_t flags;
665 uint16_t subtable_count;
666 uint16_t subtable[1];
669 #define GLYPH_NOT_COVERED (~0u)
671 struct ot_coverage_format1
673 uint16_t format;
674 uint16_t glyph_count;
675 uint16_t glyphs[1];
678 struct ot_coverage_range
680 uint16_t start_glyph;
681 uint16_t end_glyph;
682 uint16_t startcoverage_index;
685 struct ot_coverage_format2
687 uint16_t format;
688 uint16_t range_count;
689 struct ot_coverage_range ranges[1];
692 struct ot_gpos_device_table
694 uint16_t start_size;
695 uint16_t end_size;
696 uint16_t format;
697 uint16_t values[1];
700 struct ot_gpos_singlepos_format1
702 uint16_t format;
703 uint16_t coverage;
704 uint16_t value_format;
705 uint16_t value[1];
708 struct ot_gpos_singlepos_format2
710 uint16_t format;
711 uint16_t coverage;
712 uint16_t value_format;
713 uint16_t value_count;
714 uint16_t values[1];
717 struct ot_gpos_pairvalue
719 uint16_t second_glyph;
720 uint8_t data[1];
723 struct ot_gpos_pairset
725 uint16_t pairvalue_count;
726 struct ot_gpos_pairvalue pairvalues[1];
729 struct ot_gpos_pairpos_format1
731 uint16_t format;
732 uint16_t coverage;
733 uint16_t value_format1;
734 uint16_t value_format2;
735 uint16_t pairset_count;
736 uint16_t pairsets[1];
739 struct ot_gpos_pairpos_format2
741 uint16_t format;
742 uint16_t coverage;
743 uint16_t value_format1;
744 uint16_t value_format2;
745 uint16_t class_def1;
746 uint16_t class_def2;
747 uint16_t class1_count;
748 uint16_t class2_count;
749 uint16_t values[1];
752 struct ot_gpos_anchor_format1
754 uint16_t format;
755 int16_t x_coord;
756 int16_t y_coord;
759 struct ot_gpos_anchor_format2
761 uint16_t format;
762 int16_t x_coord;
763 int16_t y_coord;
764 uint16_t anchor_point;
767 struct ot_gpos_anchor_format3
769 uint16_t format;
770 int16_t x_coord;
771 int16_t y_coord;
772 uint16_t x_dev_offset;
773 uint16_t y_dev_offset;
776 struct ot_gpos_cursive_format1
778 uint16_t format;
779 uint16_t coverage;
780 uint16_t count;
781 uint16_t anchors[1];
784 struct ot_gpos_mark_record
786 uint16_t mark_class;
787 uint16_t mark_anchor;
790 struct ot_gpos_mark_array
792 uint16_t count;
793 struct ot_gpos_mark_record records[1];
796 struct ot_gpos_base_array
798 uint16_t count;
799 uint16_t offsets[1];
802 struct ot_gpos_mark_to_base_format1
804 uint16_t format;
805 uint16_t mark_coverage;
806 uint16_t base_coverage;
807 uint16_t mark_class_count;
808 uint16_t mark_array;
809 uint16_t base_array;
812 struct ot_gpos_mark_to_lig_format1
814 uint16_t format;
815 uint16_t mark_coverage;
816 uint16_t lig_coverage;
817 uint16_t mark_class_count;
818 uint16_t mark_array;
819 uint16_t lig_array;
822 struct ot_gpos_mark_to_mark_format1
824 uint16_t format;
825 uint16_t mark1_coverage;
826 uint16_t mark2_coverage;
827 uint16_t mark_class_count;
828 uint16_t mark1_array;
829 uint16_t mark2_array;
832 struct kern_header
834 uint16_t version;
835 uint16_t table_count;
838 struct kern_subtable_header
840 uint16_t version;
841 uint16_t length;
842 uint16_t coverage;
845 #include "poppack.h"
847 enum TT_NAME_WINDOWS_ENCODING_ID
849 TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
850 TT_NAME_WINDOWS_ENCODING_UNICODE_BMP,
851 TT_NAME_WINDOWS_ENCODING_SJIS,
852 TT_NAME_WINDOWS_ENCODING_PRC,
853 TT_NAME_WINDOWS_ENCODING_BIG5,
854 TT_NAME_WINDOWS_ENCODING_WANSUNG,
855 TT_NAME_WINDOWS_ENCODING_JOHAB,
856 TT_NAME_WINDOWS_ENCODING_RESERVED1,
857 TT_NAME_WINDOWS_ENCODING_RESERVED2,
858 TT_NAME_WINDOWS_ENCODING_RESERVED3,
859 TT_NAME_WINDOWS_ENCODING_UNICODE_FULL
862 enum TT_NAME_MAC_ENCODING_ID
864 TT_NAME_MAC_ENCODING_ROMAN = 0,
865 TT_NAME_MAC_ENCODING_JAPANESE,
866 TT_NAME_MAC_ENCODING_TRAD_CHINESE,
867 TT_NAME_MAC_ENCODING_KOREAN,
868 TT_NAME_MAC_ENCODING_ARABIC,
869 TT_NAME_MAC_ENCODING_HEBREW,
870 TT_NAME_MAC_ENCODING_GREEK,
871 TT_NAME_MAC_ENCODING_RUSSIAN,
872 TT_NAME_MAC_ENCODING_RSYMBOL,
873 TT_NAME_MAC_ENCODING_DEVANAGARI,
874 TT_NAME_MAC_ENCODING_GURMUKHI,
875 TT_NAME_MAC_ENCODING_GUJARATI,
876 TT_NAME_MAC_ENCODING_ORIYA,
877 TT_NAME_MAC_ENCODING_BENGALI,
878 TT_NAME_MAC_ENCODING_TAMIL,
879 TT_NAME_MAC_ENCODING_TELUGU,
880 TT_NAME_MAC_ENCODING_KANNADA,
881 TT_NAME_MAC_ENCODING_MALAYALAM,
882 TT_NAME_MAC_ENCODING_SINHALESE,
883 TT_NAME_MAC_ENCODING_BURMESE,
884 TT_NAME_MAC_ENCODING_KHMER,
885 TT_NAME_MAC_ENCODING_THAI,
886 TT_NAME_MAC_ENCODING_LAOTIAN,
887 TT_NAME_MAC_ENCODING_GEORGIAN,
888 TT_NAME_MAC_ENCODING_ARMENIAN,
889 TT_NAME_MAC_ENCODING_SIMPL_CHINESE,
890 TT_NAME_MAC_ENCODING_TIBETAN,
891 TT_NAME_MAC_ENCODING_MONGOLIAN,
892 TT_NAME_MAC_ENCODING_GEEZ,
893 TT_NAME_MAC_ENCODING_SLAVIC,
894 TT_NAME_MAC_ENCODING_VIETNAMESE,
895 TT_NAME_MAC_ENCODING_SINDHI,
896 TT_NAME_MAC_ENCODING_UNINTERPRETED
899 enum TT_NAME_MAC_LANGUAGE_ID
901 TT_NAME_MAC_LANGID_ENGLISH = 0,
902 TT_NAME_MAC_LANGID_FRENCH,
903 TT_NAME_MAC_LANGID_GERMAN,
904 TT_NAME_MAC_LANGID_ITALIAN,
905 TT_NAME_MAC_LANGID_DUTCH,
906 TT_NAME_MAC_LANGID_SWEDISH,
907 TT_NAME_MAC_LANGID_SPANISH,
908 TT_NAME_MAC_LANGID_DANISH,
909 TT_NAME_MAC_LANGID_PORTUGUESE,
910 TT_NAME_MAC_LANGID_NORWEGIAN,
911 TT_NAME_MAC_LANGID_HEBREW,
912 TT_NAME_MAC_LANGID_JAPANESE,
913 TT_NAME_MAC_LANGID_ARABIC,
914 TT_NAME_MAC_LANGID_FINNISH,
915 TT_NAME_MAC_LANGID_GREEK,
916 TT_NAME_MAC_LANGID_ICELANDIC,
917 TT_NAME_MAC_LANGID_MALTESE,
918 TT_NAME_MAC_LANGID_TURKISH,
919 TT_NAME_MAC_LANGID_CROATIAN,
920 TT_NAME_MAC_LANGID_TRAD_CHINESE,
921 TT_NAME_MAC_LANGID_URDU,
922 TT_NAME_MAC_LANGID_HINDI,
923 TT_NAME_MAC_LANGID_THAI,
924 TT_NAME_MAC_LANGID_KOREAN,
925 TT_NAME_MAC_LANGID_LITHUANIAN,
926 TT_NAME_MAC_LANGID_POLISH,
927 TT_NAME_MAC_LANGID_HUNGARIAN,
928 TT_NAME_MAC_LANGID_ESTONIAN,
929 TT_NAME_MAC_LANGID_LATVIAN,
930 TT_NAME_MAC_LANGID_SAMI,
931 TT_NAME_MAC_LANGID_FAROESE,
932 TT_NAME_MAC_LANGID_FARSI,
933 TT_NAME_MAC_LANGID_RUSSIAN,
934 TT_NAME_MAC_LANGID_SIMPL_CHINESE,
935 TT_NAME_MAC_LANGID_FLEMISH,
936 TT_NAME_MAC_LANGID_GAELIC,
937 TT_NAME_MAC_LANGID_ALBANIAN,
938 TT_NAME_MAC_LANGID_ROMANIAN,
939 TT_NAME_MAC_LANGID_CZECH,
940 TT_NAME_MAC_LANGID_SLOVAK,
941 TT_NAME_MAC_LANGID_SLOVENIAN,
942 TT_NAME_MAC_LANGID_YIDDISH,
943 TT_NAME_MAC_LANGID_SERBIAN,
944 TT_NAME_MAC_LANGID_MACEDONIAN,
945 TT_NAME_MAC_LANGID_BULGARIAN,
946 TT_NAME_MAC_LANGID_UKRAINIAN,
947 TT_NAME_MAC_LANGID_BYELORUSSIAN,
948 TT_NAME_MAC_LANGID_UZBEK,
949 TT_NAME_MAC_LANGID_KAZAKH,
950 TT_NAME_MAC_LANGID_AZERB_CYR,
951 TT_NAME_MAC_LANGID_AZERB_ARABIC,
952 TT_NAME_MAC_LANGID_ARMENIAN,
953 TT_NAME_MAC_LANGID_GEORGIAN,
954 TT_NAME_MAC_LANGID_MOLDAVIAN,
955 TT_NAME_MAC_LANGID_KIRGHIZ,
956 TT_NAME_MAC_LANGID_TAJIKI,
957 TT_NAME_MAC_LANGID_TURKMEN,
958 TT_NAME_MAC_LANGID_MONGOLIAN,
959 TT_NAME_MAC_LANGID_MONGOLIAN_CYR,
960 TT_NAME_MAC_LANGID_PASHTO,
961 TT_NAME_MAC_LANGID_KURDISH,
962 TT_NAME_MAC_LANGID_KASHMIRI,
963 TT_NAME_MAC_LANGID_SINDHI,
964 TT_NAME_MAC_LANGID_TIBETAN,
965 TT_NAME_MAC_LANGID_NEPALI,
966 TT_NAME_MAC_LANGID_SANSKRIT,
967 TT_NAME_MAC_LANGID_MARATHI,
968 TT_NAME_MAC_LANGID_BENGALI,
969 TT_NAME_MAC_LANGID_ASSAMESE,
970 TT_NAME_MAC_LANGID_GUJARATI,
971 TT_NAME_MAC_LANGID_PUNJABI,
972 TT_NAME_MAC_LANGID_ORIYA,
973 TT_NAME_MAC_LANGID_MALAYALAM,
974 TT_NAME_MAC_LANGID_KANNADA,
975 TT_NAME_MAC_LANGID_TAMIL,
976 TT_NAME_MAC_LANGID_TELUGU,
977 TT_NAME_MAC_LANGID_SINHALESE,
978 TT_NAME_MAC_LANGID_BURMESE,
979 TT_NAME_MAC_LANGID_KHMER,
980 TT_NAME_MAC_LANGID_LAO,
981 TT_NAME_MAC_LANGID_VIETNAMESE,
982 TT_NAME_MAC_LANGID_INDONESIAN,
983 TT_NAME_MAC_LANGID_TAGALOG,
984 TT_NAME_MAC_LANGID_MALAY_ROMAN,
985 TT_NAME_MAC_LANGID_MALAY_ARABIC,
986 TT_NAME_MAC_LANGID_AMHARIC,
987 TT_NAME_MAC_LANGID_TIGRINYA,
988 TT_NAME_MAC_LANGID_GALLA,
989 TT_NAME_MAC_LANGID_SOMALI,
990 TT_NAME_MAC_LANGID_SWAHILI,
991 TT_NAME_MAC_LANGID_KINYARWANDA,
992 TT_NAME_MAC_LANGID_RUNDI,
993 TT_NAME_MAC_LANGID_NYANJA,
994 TT_NAME_MAC_LANGID_MALAGASY,
995 TT_NAME_MAC_LANGID_ESPERANTO,
996 TT_NAME_MAC_LANGID_WELSH = 128,
997 TT_NAME_MAC_LANGID_BASQUE,
998 TT_NAME_MAC_LANGID_CATALAN,
999 TT_NAME_MAC_LANGID_LATIN,
1000 TT_NAME_MAC_LANGID_QUECHUA,
1001 TT_NAME_MAC_LANGID_GUARANI,
1002 TT_NAME_MAC_LANGID_AYMARA,
1003 TT_NAME_MAC_LANGID_TATAR,
1004 TT_NAME_MAC_LANGID_UIGHUR,
1005 TT_NAME_MAC_LANGID_DZONGKHA,
1006 TT_NAME_MAC_LANGID_JAVANESE,
1007 TT_NAME_MAC_LANGID_SUNDANESE,
1008 TT_NAME_MAC_LANGID_GALICIAN,
1009 TT_NAME_MAC_LANGID_AFRIKAANS,
1010 TT_NAME_MAC_LANGID_BRETON,
1011 TT_NAME_MAC_LANGID_INUKTITUT,
1012 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC,
1013 TT_NAME_MAC_LANGID_MANX_GAELIC,
1014 TT_NAME_MAC_LANGID_IRISH_GAELIC,
1015 TT_NAME_MAC_LANGID_TONGAN,
1016 TT_NAME_MAC_LANGID_GREEK_POLYTONIC,
1017 TT_NAME_MAC_LANGID_GREENLANDIC,
1018 TT_NAME_MAC_LANGID_AZER_ROMAN
1021 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
1022 static const char name_mac_langid_to_locale[][10] = {
1023 "en-US",
1024 "fr-FR",
1025 "de-DE",
1026 "it-IT",
1027 "nl-NL",
1028 "sv-SE",
1029 "es-ES",
1030 "da-DA",
1031 "pt-PT",
1032 "no-NO",
1033 "he-IL",
1034 "ja-JP",
1035 "ar-AR",
1036 "fi-FI",
1037 "el-GR",
1038 "is-IS",
1039 "mt-MT",
1040 "tr-TR",
1041 "hr-HR",
1042 "zh-HK",
1043 "ur-PK",
1044 "hi-IN",
1045 "th-TH",
1046 "ko-KR",
1047 "lt-LT",
1048 "pl-PL",
1049 "hu-HU",
1050 "et-EE",
1051 "lv-LV",
1052 "se-NO",
1053 "fo-FO",
1054 "fa-IR",
1055 "ru-RU",
1056 "zh-CN",
1057 "nl-BE",
1058 "gd-GB",
1059 "sq-AL",
1060 "ro-RO",
1061 "cs-CZ",
1062 "sk-SK",
1063 "sl-SI",
1065 "sr-Latn",
1066 "mk-MK",
1067 "bg-BG",
1068 "uk-UA",
1069 "be-BY",
1070 "uz-Latn",
1071 "kk-KZ",
1072 "az-Cyrl-AZ",
1073 "az-AZ",
1074 "hy-AM",
1075 "ka-GE",
1078 "tg-TJ",
1079 "tk-TM",
1080 "mn-Mong",
1081 "mn-MN",
1082 "ps-AF",
1083 "ku-Arab",
1085 "sd-Arab",
1086 "bo-CN",
1087 "ne-NP",
1088 "sa-IN",
1089 "mr-IN",
1090 "bn-IN",
1091 "as-IN",
1092 "gu-IN",
1093 "pa-Arab",
1094 "or-IN",
1095 "ml-IN",
1096 "kn-IN",
1097 "ta-LK",
1098 "te-IN",
1099 "si-LK",
1101 "km-KH",
1102 "lo-LA",
1103 "vi-VN",
1104 "id-ID",
1106 "ms-MY",
1107 "ms-Arab",
1108 "am-ET",
1109 "ti-ET",
1112 "sw-KE",
1113 "rw-RW",
1151 "cy-GB",
1152 "eu-ES",
1153 "ca-ES",
1158 "tt-RU",
1159 "ug-CN",
1163 "gl-ES",
1164 "af-ZA",
1165 "br-FR",
1166 "iu-Latn-CA",
1167 "gd-GB",
1169 "ga-IE",
1172 "kl-GL",
1173 "az-Latn"
1176 enum OPENTYPE_STRING_ID
1178 OPENTYPE_STRING_COPYRIGHT_NOTICE = 0,
1179 OPENTYPE_STRING_FAMILY_NAME,
1180 OPENTYPE_STRING_SUBFAMILY_NAME,
1181 OPENTYPE_STRING_UNIQUE_IDENTIFIER,
1182 OPENTYPE_STRING_FULL_FONTNAME,
1183 OPENTYPE_STRING_VERSION_STRING,
1184 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1185 OPENTYPE_STRING_TRADEMARK,
1186 OPENTYPE_STRING_MANUFACTURER,
1187 OPENTYPE_STRING_DESIGNER,
1188 OPENTYPE_STRING_DESCRIPTION,
1189 OPENTYPE_STRING_VENDOR_URL,
1190 OPENTYPE_STRING_DESIGNER_URL,
1191 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1192 OPENTYPE_STRING_LICENSE_INFO_URL,
1193 OPENTYPE_STRING_RESERVED_ID15,
1194 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1195 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1196 OPENTYPE_STRING_COMPATIBLE_FULLNAME,
1197 OPENTYPE_STRING_SAMPLE_TEXT,
1198 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1199 OPENTYPE_STRING_WWS_FAMILY_NAME,
1200 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
1203 static const UINT16 dwriteid_to_opentypeid[DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME + 1] =
1205 (UINT16)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
1206 OPENTYPE_STRING_COPYRIGHT_NOTICE,
1207 OPENTYPE_STRING_VERSION_STRING,
1208 OPENTYPE_STRING_TRADEMARK,
1209 OPENTYPE_STRING_MANUFACTURER,
1210 OPENTYPE_STRING_DESIGNER,
1211 OPENTYPE_STRING_DESIGNER_URL,
1212 OPENTYPE_STRING_DESCRIPTION,
1213 OPENTYPE_STRING_VENDOR_URL,
1214 OPENTYPE_STRING_LICENSE_DESCRIPTION,
1215 OPENTYPE_STRING_LICENSE_INFO_URL,
1216 OPENTYPE_STRING_FAMILY_NAME,
1217 OPENTYPE_STRING_SUBFAMILY_NAME,
1218 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
1219 OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME,
1220 OPENTYPE_STRING_SAMPLE_TEXT,
1221 OPENTYPE_STRING_FULL_FONTNAME,
1222 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
1223 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
1224 OPENTYPE_STRING_WWS_FAMILY_NAME,
1227 /* CPAL table */
1228 struct cpal_header_0
1230 uint16_t version;
1231 uint16_t num_palette_entries;
1232 uint16_t num_palettes;
1233 uint16_t num_color_records;
1234 uint32_t offset_first_color_record;
1235 uint16_t color_record_indices[1];
1238 struct cpal_color_record
1240 uint8_t blue;
1241 uint8_t green;
1242 uint8_t red;
1243 uint8_t alpha;
1246 /* COLR table */
1247 struct colr_header
1249 uint16_t version;
1250 uint16_t num_baseglyph_records;
1251 uint32_t offset_baseglyph_records;
1252 uint32_t offset_layer_records;
1253 uint16_t num_layer_records;
1256 struct colr_baseglyph_record
1258 uint16_t glyph;
1259 uint16_t first_layer_index;
1260 uint16_t num_layers;
1263 struct colr_layer_record
1265 uint16_t glyph;
1266 uint16_t palette_index;
1269 struct meta_data_map
1271 uint32_t tag;
1272 uint32_t offset;
1273 uint32_t length;
1276 struct meta_header
1278 uint32_t version;
1279 uint32_t flags;
1280 uint32_t reserved;
1281 uint32_t data_maps_count;
1282 struct meta_data_map maps[1];
1285 struct fvar_header
1287 uint16_t major_version;
1288 uint16_t minor_version;
1289 uint16_t axes_array_offset;
1290 uint16_t reserved;
1291 uint16_t axis_count;
1292 uint16_t axis_size;
1293 uint16_t instance_count;
1294 uint16_t instance_size;
1297 struct var_axis_record
1299 uint32_t tag;
1300 int32_t min_value;
1301 int32_t default_value;
1302 int32_t max_value;
1303 uint16_t flags;
1304 uint16_t nameid;
1307 static const void *table_read_ensure(const struct dwrite_fonttable *table, unsigned int offset, unsigned int size)
1309 if (size > table->size || offset > table->size - size)
1310 return NULL;
1312 return table->data + offset;
1315 static WORD table_read_be_word(const struct dwrite_fonttable *table, unsigned int offset)
1317 const WORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1318 return ptr ? GET_BE_WORD(*ptr) : 0;
1321 static DWORD table_read_be_dword(const struct dwrite_fonttable *table, unsigned int offset)
1323 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1324 return ptr ? GET_BE_DWORD(*ptr) : 0;
1327 static float table_read_be_fixed(const struct dwrite_fonttable *table, unsigned int offset)
1329 return (int32_t)table_read_be_dword(table, offset) / 65536.0;
1332 static DWORD table_read_dword(const struct dwrite_fonttable *table, unsigned int offset)
1334 const DWORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1335 return ptr ? *ptr : 0;
1338 BOOL is_face_type_supported(DWRITE_FONT_FACE_TYPE type)
1340 return (type == DWRITE_FONT_FACE_TYPE_CFF) ||
1341 (type == DWRITE_FONT_FACE_TYPE_TRUETYPE) ||
1342 (type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION) ||
1343 (type == DWRITE_FONT_FACE_TYPE_RAW_CFF);
1346 typedef HRESULT (*dwrite_fontfile_analyzer)(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1347 DWRITE_FONT_FACE_TYPE *face_type);
1349 static HRESULT opentype_ttc_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1350 DWRITE_FONT_FACE_TYPE *face_type)
1352 const struct ttc_header *header;
1353 void *context;
1354 HRESULT hr;
1356 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(header), &context);
1357 if (FAILED(hr))
1358 return hr;
1360 if (header->tag == MS_TTCF_TAG)
1362 *font_count = GET_BE_DWORD(header->num_fonts);
1363 *file_type = DWRITE_FONT_FILE_TYPE_OPENTYPE_COLLECTION;
1364 *face_type = DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION;
1367 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1369 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1372 static HRESULT opentype_ttf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1373 DWRITE_FONT_FACE_TYPE *face_type)
1375 const DWORD *header;
1376 void *context;
1377 HRESULT hr;
1379 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1380 if (FAILED(hr))
1381 return hr;
1383 if (GET_BE_DWORD(*header) == 0x10000) {
1384 *font_count = 1;
1385 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
1386 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
1389 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1391 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1394 static HRESULT opentype_otf_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1395 DWRITE_FONT_FACE_TYPE *face_type)
1397 const DWORD *header;
1398 void *context;
1399 HRESULT hr;
1401 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1402 if (FAILED(hr))
1403 return hr;
1405 if (GET_BE_DWORD(*header) == MS_OTTO_TAG) {
1406 *font_count = 1;
1407 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
1408 *face_type = DWRITE_FONT_FACE_TYPE_CFF;
1411 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1413 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1416 static HRESULT opentype_type1_analyzer(IDWriteFontFileStream *stream, UINT32 *font_count, DWRITE_FONT_FILE_TYPE *file_type,
1417 DWRITE_FONT_FACE_TYPE *face_type)
1419 #include "pshpack1.h"
1420 /* Specified in Adobe TechNote #5178 */
1421 struct pfm_header {
1422 WORD dfVersion;
1423 DWORD dfSize;
1424 char data0[95];
1425 DWORD dfDevice;
1426 char data1[12];
1428 #include "poppack.h"
1429 struct type1_header {
1430 WORD tag;
1431 char data[14];
1433 const struct type1_header *header;
1434 void *context;
1435 HRESULT hr;
1437 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&header, 0, sizeof(*header), &context);
1438 if (FAILED(hr))
1439 return hr;
1441 /* tag is followed by plain text section */
1442 if (header->tag == 0x8001 &&
1443 (!memcmp(header->data, "%!PS-AdobeFont", 14) ||
1444 !memcmp(header->data, "%!FontType", 10))) {
1445 *font_count = 1;
1446 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFB;
1447 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1450 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1452 /* let's see if it's a .pfm metrics file */
1453 if (*file_type == DWRITE_FONT_FILE_TYPE_UNKNOWN) {
1454 const struct pfm_header *pfm_header;
1455 UINT64 filesize;
1456 DWORD offset;
1457 BOOL header_checked;
1459 hr = IDWriteFontFileStream_GetFileSize(stream, &filesize);
1460 if (FAILED(hr))
1461 return hr;
1463 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&pfm_header, 0, sizeof(*pfm_header), &context);
1464 if (FAILED(hr))
1465 return hr;
1467 offset = pfm_header->dfDevice;
1468 header_checked = pfm_header->dfVersion == 0x100 && pfm_header->dfSize == filesize;
1469 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1471 /* as a last test check static string in PostScript information section */
1472 if (header_checked) {
1473 static const char postscript[] = "PostScript";
1474 char *devtype_name;
1476 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&devtype_name, offset, sizeof(postscript), &context);
1477 if (FAILED(hr))
1478 return hr;
1480 if (!memcmp(devtype_name, postscript, sizeof(postscript))) {
1481 *font_count = 1;
1482 *file_type = DWRITE_FONT_FILE_TYPE_TYPE1_PFM;
1483 *face_type = DWRITE_FONT_FACE_TYPE_TYPE1;
1486 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
1490 return *file_type != DWRITE_FONT_FILE_TYPE_UNKNOWN ? S_OK : S_FALSE;
1493 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, BOOL *supported, DWRITE_FONT_FILE_TYPE *file_type,
1494 DWRITE_FONT_FACE_TYPE *face_type, UINT32 *face_count)
1496 static dwrite_fontfile_analyzer fontfile_analyzers[] = {
1497 opentype_ttf_analyzer,
1498 opentype_otf_analyzer,
1499 opentype_ttc_analyzer,
1500 opentype_type1_analyzer,
1501 NULL
1503 dwrite_fontfile_analyzer *analyzer = fontfile_analyzers;
1504 DWRITE_FONT_FACE_TYPE face;
1505 HRESULT hr;
1507 if (!face_type)
1508 face_type = &face;
1510 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
1511 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
1512 *face_count = 0;
1514 while (*analyzer) {
1515 hr = (*analyzer)(stream, face_count, file_type, face_type);
1516 if (FAILED(hr))
1517 return hr;
1519 if (hr == S_OK)
1520 break;
1522 analyzer++;
1525 *supported = is_face_type_supported(*face_type);
1526 return S_OK;
1529 HRESULT opentype_try_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag, const void **table_data,
1530 void **table_context, UINT32 *table_size, BOOL *found)
1532 void *table_directory_context, *sfnt_context;
1533 const struct ot_table_record *table_record = NULL;
1534 const struct ot_table_dir *table_dir = NULL;
1535 UINT32 table_offset = 0;
1536 UINT16 table_count;
1537 HRESULT hr;
1539 if (found) *found = FALSE;
1540 if (table_size) *table_size = 0;
1542 *table_data = NULL;
1543 *table_context = NULL;
1545 if (stream_desc->face_type == DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION)
1547 const struct ttc_header *ttc_header;
1548 void * ttc_context;
1550 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void **)&ttc_header, 0,
1551 sizeof(*ttc_header), &ttc_context);
1552 if (SUCCEEDED(hr))
1554 if (stream_desc->face_index >= GET_BE_DWORD(ttc_header->num_fonts))
1555 hr = E_INVALIDARG;
1556 else
1558 table_offset = GET_BE_DWORD(ttc_header->offsets[stream_desc->face_index]);
1559 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void **)&table_dir, table_offset,
1560 sizeof(*table_dir), &sfnt_context);
1562 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, ttc_context);
1565 else
1566 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void **)&table_dir, 0,
1567 sizeof(*table_dir), &sfnt_context);
1569 if (FAILED(hr))
1570 return hr;
1572 table_count = GET_BE_WORD(table_dir->numTables);
1573 table_offset += sizeof(*table_dir);
1575 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, sfnt_context);
1577 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, (const void **)&table_record, table_offset,
1578 table_count * sizeof(*table_record), &table_directory_context);
1579 if (hr == S_OK)
1581 UINT16 i;
1583 for (i = 0; i < table_count; ++i)
1585 if (table_record->tag == tag)
1587 UINT32 offset = GET_BE_DWORD(table_record->offset);
1588 UINT32 length = GET_BE_DWORD(table_record->length);
1590 if (found)
1591 *found = TRUE;
1592 if (table_size)
1593 *table_size = length;
1594 hr = IDWriteFontFileStream_ReadFileFragment(stream_desc->stream, table_data, offset,
1595 length, table_context);
1596 break;
1598 table_record++;
1601 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, table_directory_context);
1604 return hr;
1607 static HRESULT opentype_get_font_table(const struct file_stream_desc *stream_desc, UINT32 tag,
1608 struct dwrite_fonttable *table)
1610 return opentype_try_get_font_table(stream_desc, tag, (const void **)&table->data, &table->context, &table->size, &table->exists);
1613 /**********
1614 * CMAP
1615 **********/
1617 static UINT16 opentype_cmap_format0_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1619 const UINT8 *glyphs = cmap->data;
1620 return (ch < 0xff) ? glyphs[ch] : 0;
1623 static unsigned int opentype_cmap_format0_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1624 DWRITE_UNICODE_RANGE *ranges)
1626 if (count > 0)
1628 ranges->first = 0;
1629 ranges->last = 255;
1632 return 1;
1635 struct cmap_format4_compare_context
1637 const struct dwrite_cmap *cmap;
1638 unsigned int ch;
1641 static int __cdecl cmap_format4_compare_range(const void *a, const void *b)
1643 const struct cmap_format4_compare_context *key = a;
1644 const UINT16 *end = b;
1645 unsigned int idx;
1647 if (key->ch > GET_BE_WORD(*end))
1648 return 1;
1650 idx = end - key->cmap->u.format4.ends;
1651 if (key->ch < GET_BE_WORD(key->cmap->u.format4.starts[idx]))
1652 return -1;
1654 return 0;
1657 static UINT16 opentype_cmap_format4_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1659 struct cmap_format4_compare_context key = { .cmap = cmap, .ch = ch };
1660 unsigned int glyph, idx, range_offset;
1661 const UINT16 *end_found;
1663 /* Look up range. */
1664 end_found = bsearch(&key, cmap->u.format4.ends, cmap->u.format4.seg_count, sizeof(*cmap->u.format4.ends),
1665 cmap_format4_compare_range);
1666 if (!end_found)
1667 return 0;
1669 idx = end_found - cmap->u.format4.ends;
1671 range_offset = GET_BE_WORD(cmap->u.format4.id_range_offset[idx]);
1673 if (!range_offset)
1675 glyph = ch + GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1677 else
1679 unsigned int index = range_offset / 2 + (ch - GET_BE_WORD(cmap->u.format4.starts[idx])) + idx - cmap->u.format4.seg_count;
1680 if (index >= cmap->u.format4.glyph_id_array_len)
1681 return 0;
1682 glyph = GET_BE_WORD(cmap->u.format4.glyph_id_array[index]);
1683 if (!glyph)
1684 return 0;
1685 glyph += GET_BE_WORD(cmap->u.format4.id_delta[idx]);
1688 return glyph & 0xffff;
1691 static unsigned int opentype_cmap_format4_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1692 DWRITE_UNICODE_RANGE *ranges)
1694 unsigned int i;
1696 count = min(count, cmap->u.format4.seg_count);
1698 for (i = 0; i < count; ++i)
1700 ranges[i].first = GET_BE_WORD(cmap->u.format4.starts[i]);
1701 ranges[i].last = GET_BE_WORD(cmap->u.format4.ends[i]);
1704 return cmap->u.format4.seg_count;
1707 static UINT16 opentype_cmap_format6_10_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1709 const UINT16 *glyphs = cmap->data;
1710 if (ch < cmap->u.format6_10.first || ch > cmap->u.format6_10.last) return 0;
1711 return glyphs[ch - cmap->u.format6_10.first];
1714 static unsigned int opentype_cmap_format6_10_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1715 DWRITE_UNICODE_RANGE *ranges)
1717 if (count > 0)
1719 ranges->first = cmap->u.format6_10.first;
1720 ranges->last = cmap->u.format6_10.last;
1723 return 1;
1726 static int __cdecl cmap_format12_13_compare_group(const void *a, const void *b)
1728 const unsigned int *ch = a;
1729 const UINT32 *group = b;
1731 if (*ch > GET_BE_DWORD(group[1]))
1732 return 1;
1734 if (*ch < GET_BE_DWORD(group[0]))
1735 return -1;
1737 return 0;
1740 static UINT16 opentype_cmap_format12_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1742 const UINT32 *groups = cmap->data;
1743 const UINT32 *group_found;
1745 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1746 cmap_format12_13_compare_group)))
1747 return 0;
1749 return GET_BE_DWORD(group_found[0]) <= GET_BE_DWORD(group_found[1]) ?
1750 GET_BE_DWORD(group_found[2]) + (ch - GET_BE_DWORD(group_found[0])) : 0;
1753 static unsigned int opentype_cmap_format12_13_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1754 DWRITE_UNICODE_RANGE *ranges)
1756 unsigned int i, group_count = cmap->u.format12_13.group_count;
1757 const UINT32 *groups = cmap->data;
1759 count = min(count, group_count);
1761 for (i = 0; i < count; ++i)
1763 ranges[i].first = GET_BE_DWORD(groups[3 * i]);
1764 ranges[i].last = GET_BE_DWORD(groups[3 * i + 1]);
1767 return group_count;
1770 static UINT16 opentype_cmap_format13_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1772 const UINT32 *groups = cmap->data;
1773 const UINT32 *group_found;
1775 if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
1776 cmap_format12_13_compare_group)))
1777 return 0;
1779 return GET_BE_DWORD(group_found[2]);
1782 static UINT16 opentype_cmap_dummy_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1784 return 0;
1787 static unsigned int opentype_cmap_dummy_get_ranges(const struct dwrite_cmap *cmap, unsigned int count,
1788 DWRITE_UNICODE_RANGE *ranges)
1790 return 0;
1793 UINT16 opentype_cmap_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
1795 UINT16 glyph;
1797 if (!cmap->get_glyph) return 0;
1798 glyph = cmap->get_glyph(cmap, ch);
1799 if (!glyph && cmap->symbol && ch <= 0xff)
1800 glyph = cmap->get_glyph(cmap, ch + 0xf000);
1801 return glyph;
1804 static int __cdecl cmap_header_compare(const void *a, const void *b)
1806 const UINT16 *key = a;
1807 const UINT16 *record = b;
1809 /* Platform. */
1810 if (key[0] < GET_BE_WORD(record[0])) return -1;
1811 if (key[0] > GET_BE_WORD(record[0])) return 1;
1812 /* Encoding. */
1813 if (key[1] < GET_BE_WORD(record[1])) return -1;
1814 if (key[1] > GET_BE_WORD(record[1])) return 1;
1816 return 0;
1819 void dwrite_cmap_init(struct dwrite_cmap *cmap, IDWriteFontFile *file, unsigned int face_index,
1820 DWRITE_FONT_FACE_TYPE face_type)
1822 static const UINT16 encodings[][2] =
1824 { 3, 0 }, /* MS Symbol encoding is preferred. */
1825 { 3, 10 },
1826 { 0, 6 },
1827 { 0, 4 },
1828 { 3, 1 },
1829 { 0, 3 },
1830 { 0, 2 },
1831 { 0, 1 },
1832 { 0, 0 },
1834 const struct cmap_encoding_record *records, *found_record = NULL;
1835 unsigned int length, offset, format, count, f, i, num_records;
1836 struct file_stream_desc stream_desc;
1837 struct dwrite_fonttable table;
1838 const UINT16 *pair = NULL;
1839 HRESULT hr;
1841 if (cmap->data) return;
1843 /* For fontface stream is already available and preset. */
1844 if (!cmap->stream && FAILED(hr = get_filestream_from_file(file, &cmap->stream)))
1846 WARN("Failed to get file stream, hr %#lx.\n", hr);
1847 goto failed;
1850 stream_desc.stream = cmap->stream;
1851 stream_desc.face_type = face_type;
1852 stream_desc.face_index = face_index;
1854 opentype_get_font_table(&stream_desc, MS_CMAP_TAG, &table);
1855 if (!table.exists)
1856 goto failed;
1857 cmap->table_context = table.context;
1859 num_records = table_read_be_word(&table, 2);
1860 records = table_read_ensure(&table, 4, sizeof(*records) * num_records);
1862 for (i = 0; i < ARRAY_SIZE(encodings); ++i)
1864 pair = encodings[i];
1865 if ((found_record = bsearch(pair, records, num_records, sizeof(*records), cmap_header_compare)))
1866 break;
1869 if (!found_record)
1871 WARN("No suitable cmap table were found.\n");
1872 goto failed;
1875 /* Symbol encoding. */
1876 cmap->symbol = pair[0] == 3 && pair[1] == 0;
1877 offset = GET_BE_DWORD(found_record->offset);
1879 format = table_read_be_word(&table, offset);
1881 switch (format)
1883 case 0:
1884 cmap->data = table_read_ensure(&table, offset + 6, 256);
1885 cmap->get_glyph = opentype_cmap_format0_get_glyph;
1886 cmap->get_ranges = opentype_cmap_format0_get_ranges;
1887 break;
1888 case 4:
1889 length = table_read_be_word(&table, offset + 2);
1890 cmap->u.format4.seg_count = count = table_read_be_word(&table, offset + 6) / 2;
1891 cmap->u.format4.ends = table_read_ensure(&table, offset + 14, count * 2);
1892 cmap->u.format4.starts = cmap->u.format4.ends + count + 1;
1893 cmap->u.format4.id_delta = cmap->u.format4.starts + count;
1894 cmap->u.format4.id_range_offset = cmap->u.format4.id_delta + count;
1895 cmap->u.format4.glyph_id_array = cmap->data = cmap->u.format4.id_range_offset + count;
1896 cmap->u.format4.glyph_id_array_len = (length - 16 - 8 * count) / 2;
1897 cmap->get_glyph = opentype_cmap_format4_get_glyph;
1898 cmap->get_ranges = opentype_cmap_format4_get_ranges;
1899 break;
1900 case 6:
1901 case 10:
1902 /* Format 10 uses 4 byte fields. */
1903 f = format == 6 ? 1 : 2;
1904 cmap->u.format6_10.first = table_read_be_word(&table, offset + f * 6);
1905 count = table_read_be_word(&table, offset + f * 8);
1906 cmap->u.format6_10.last = cmap->u.format6_10.first + count;
1907 cmap->data = table_read_ensure(&table, offset + f * 10, count * 2);
1908 cmap->get_glyph = opentype_cmap_format6_10_get_glyph;
1909 cmap->get_ranges = opentype_cmap_format6_10_get_ranges;
1910 break;
1911 case 12:
1912 case 13:
1913 cmap->u.format12_13.group_count = count = table_read_be_dword(&table, offset + 12);
1914 cmap->data = table_read_ensure(&table, offset + 16, count * 3 * 4);
1915 cmap->get_glyph = format == 12 ? opentype_cmap_format12_get_glyph : opentype_cmap_format13_get_glyph;
1916 cmap->get_ranges = opentype_cmap_format12_13_get_ranges;
1917 break;
1918 default:
1919 WARN("Unhandled subtable format %u.\n", format);
1922 failed:
1924 if (!cmap->data)
1926 /* Dummy implementation, returns 0 unconditionally. */
1927 cmap->data = cmap;
1928 cmap->get_glyph = opentype_cmap_dummy_get_glyph;
1929 cmap->get_ranges = opentype_cmap_dummy_get_ranges;
1933 void dwrite_cmap_release(struct dwrite_cmap *cmap)
1935 if (cmap->stream)
1937 IDWriteFontFileStream_ReleaseFileFragment(cmap->stream, cmap->table_context);
1938 IDWriteFontFileStream_Release(cmap->stream);
1940 cmap->data = NULL;
1941 cmap->stream = NULL;
1944 HRESULT opentype_cmap_get_unicode_ranges(const struct dwrite_cmap *cmap, unsigned int max_count, DWRITE_UNICODE_RANGE *ranges,
1945 unsigned int *count)
1947 if (!cmap->data)
1948 return E_FAIL;
1950 *count = cmap->get_ranges(cmap, max_count, ranges);
1952 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
1955 void opentype_get_font_typo_metrics(struct file_stream_desc *stream_desc, unsigned int *ascent, unsigned int *descent)
1957 struct dwrite_fonttable os2;
1959 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1961 *ascent = *descent = 0;
1963 if (os2.size >= FIELD_OFFSET(struct tt_os2, sTypoLineGap))
1965 SHORT value = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoDescender));
1966 *ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoAscender));
1967 *descent = value < 0 ? -value : 0;
1970 if (os2.data)
1971 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
1974 void opentype_get_font_metrics(struct file_stream_desc *stream_desc, DWRITE_FONT_METRICS1 *metrics, DWRITE_CARET_METRICS *caret)
1976 struct dwrite_fonttable os2, head, post, hhea;
1978 memset(metrics, 0, sizeof(*metrics));
1980 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
1981 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
1982 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
1983 opentype_get_font_table(stream_desc, MS_HHEA_TAG, &hhea);
1985 if (head.data)
1987 metrics->designUnitsPerEm = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, unitsPerEm));
1988 metrics->glyphBoxLeft = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, xMin));
1989 metrics->glyphBoxTop = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, yMax));
1990 metrics->glyphBoxRight = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, xMax));
1991 metrics->glyphBoxBottom = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, yMin));
1994 if (caret)
1996 if (hhea.data)
1998 caret->slopeRise = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretSlopeRise));
1999 caret->slopeRun = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretSlopeRun));
2000 caret->offset = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, caretOffset));
2002 else
2003 memset(caret, 0, sizeof(*caret));
2006 if (os2.data)
2008 USHORT version = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, version));
2010 metrics->ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWinAscent));
2011 /* Some fonts have usWinDescent value stored as signed short, which could be wrongly
2012 interpreted as large unsigned value. */
2013 metrics->descent = abs((SHORT)table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWinDescent)));
2015 /* Line gap is estimated using two sets of ascender/descender values and 'hhea' line gap. */
2016 if (hhea.data)
2018 SHORT descender = (SHORT)table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, descender));
2019 INT32 linegap;
2021 linegap = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, ascender)) + abs(descender) +
2022 table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, linegap)) - metrics->ascent - metrics->descent;
2023 metrics->lineGap = linegap > 0 ? linegap : 0;
2026 metrics->strikethroughPosition = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, yStrikeoutPosition));
2027 metrics->strikethroughThickness = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, yStrikeoutSize));
2028 metrics->subscriptPositionX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptXOffset));
2029 /* Y offset is stored as positive offset below baseline */
2030 metrics->subscriptPositionY = -table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptYOffset));
2031 metrics->subscriptSizeX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptXSize));
2032 metrics->subscriptSizeY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySubscriptYSize));
2033 metrics->superscriptPositionX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptXOffset));
2034 metrics->superscriptPositionY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptYOffset));
2035 metrics->superscriptSizeX = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptXSize));
2036 metrics->superscriptSizeY = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, ySuperscriptYSize));
2038 /* version 2 fields */
2039 if (version >= 2)
2041 metrics->capHeight = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sCapHeight));
2042 metrics->xHeight = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sxHeight));
2045 if (table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) & OS2_FSSELECTION_USE_TYPO_METRICS)
2047 SHORT descent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoDescender));
2048 metrics->ascent = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoAscender));
2049 metrics->descent = descent < 0 ? -descent : 0;
2050 metrics->lineGap = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, sTypoLineGap));
2051 metrics->hasTypographicMetrics = TRUE;
2054 else
2056 metrics->strikethroughPosition = metrics->designUnitsPerEm / 3;
2057 if (hhea.data)
2059 metrics->ascent = table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, ascender));
2060 metrics->descent = abs((SHORT)table_read_be_word(&hhea, FIELD_OFFSET(struct tt_hhea, descender)));
2064 if (post.data)
2066 metrics->underlinePosition = table_read_be_word(&post, FIELD_OFFSET(struct tt_post, underlinePosition));
2067 metrics->underlineThickness = table_read_be_word(&post, FIELD_OFFSET(struct tt_post, underlineThickness));
2070 if (metrics->underlineThickness == 0)
2071 metrics->underlineThickness = metrics->designUnitsPerEm / 14;
2072 if (metrics->strikethroughThickness == 0)
2073 metrics->strikethroughThickness = metrics->underlineThickness;
2075 /* estimate missing metrics */
2076 if (metrics->xHeight == 0)
2077 metrics->xHeight = metrics->designUnitsPerEm / 2;
2078 if (metrics->capHeight == 0)
2079 metrics->capHeight = metrics->designUnitsPerEm * 7 / 10;
2081 if (os2.data)
2082 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2083 if (head.data)
2084 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2085 if (post.data)
2086 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2087 if (hhea.data)
2088 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, hhea.context);
2091 void opentype_get_font_properties(const struct file_stream_desc *stream_desc, struct dwrite_font_props *props)
2093 struct dwrite_fonttable os2, head, post, colr, cpal;
2094 BOOL is_symbol, is_monospaced;
2096 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2097 opentype_get_font_table(stream_desc, MS_HEAD_TAG, &head);
2099 memset(props, 0, sizeof(*props));
2101 /* Default stretch, weight and style to normal */
2102 props->stretch = DWRITE_FONT_STRETCH_NORMAL;
2103 props->weight = DWRITE_FONT_WEIGHT_NORMAL;
2104 props->style = DWRITE_FONT_STYLE_NORMAL;
2106 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
2107 if (os2.data)
2109 USHORT version = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, version));
2110 USHORT fsSelection = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection));
2111 USHORT usWeightClass = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWeightClass));
2112 USHORT usWidthClass = table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, usWidthClass));
2113 const void *panose;
2115 if (usWidthClass > DWRITE_FONT_STRETCH_UNDEFINED && usWidthClass <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
2116 props->stretch = usWidthClass;
2118 if (usWeightClass >= 1 && usWeightClass <= 9)
2119 usWeightClass *= 100;
2121 if (usWeightClass > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
2122 props->weight = DWRITE_FONT_WEIGHT_ULTRA_BLACK;
2123 else if (usWeightClass > 0)
2124 props->weight = usWeightClass;
2126 if (version >= 4 && (fsSelection & OS2_FSSELECTION_OBLIQUE))
2127 props->style = DWRITE_FONT_STYLE_OBLIQUE;
2128 else if (fsSelection & OS2_FSSELECTION_ITALIC)
2129 props->style = DWRITE_FONT_STYLE_ITALIC;
2130 props->lf.lfItalic = !!(fsSelection & OS2_FSSELECTION_ITALIC);
2132 if ((panose = table_read_ensure(&os2, FIELD_OFFSET(struct tt_os2, panose), sizeof(props->panose))))
2133 memcpy(&props->panose, panose, sizeof(props->panose));
2135 /* FONTSIGNATURE */
2136 props->fontsig.fsUsb[0] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange1));
2137 props->fontsig.fsUsb[1] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange2));
2138 props->fontsig.fsUsb[2] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange3));
2139 props->fontsig.fsUsb[3] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulUnicodeRange4));
2141 if (version)
2143 props->fontsig.fsCsb[0] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulCodePageRange1));
2144 props->fontsig.fsCsb[1] = table_read_be_dword(&os2, FIELD_OFFSET(struct tt_os2, ulCodePageRange2));
2147 else if (head.data)
2149 USHORT macStyle = table_read_be_word(&head, FIELD_OFFSET(struct tt_head, macStyle));
2151 if (macStyle & TT_HEAD_MACSTYLE_CONDENSED)
2152 props->stretch = DWRITE_FONT_STRETCH_CONDENSED;
2153 else if (macStyle & TT_HEAD_MACSTYLE_EXTENDED)
2154 props->stretch = DWRITE_FONT_STRETCH_EXPANDED;
2156 if (macStyle & TT_HEAD_MACSTYLE_BOLD)
2157 props->weight = DWRITE_FONT_WEIGHT_BOLD;
2159 if (macStyle & TT_HEAD_MACSTYLE_ITALIC) {
2160 props->style = DWRITE_FONT_STYLE_ITALIC;
2161 props->lf.lfItalic = 1;
2165 props->lf.lfWeight = props->weight;
2167 /* FONT_IS_SYMBOL */
2168 if (!(is_symbol = props->panose.familyKind == DWRITE_PANOSE_FAMILY_SYMBOL))
2170 struct dwrite_fonttable cmap;
2171 int i, offset, num_tables;
2173 opentype_get_font_table(stream_desc, MS_CMAP_TAG, &cmap);
2175 if (cmap.data)
2177 num_tables = table_read_be_word(&cmap, FIELD_OFFSET(struct cmap_header, num_tables));
2178 offset = FIELD_OFFSET(struct cmap_header, tables);
2180 for (i = 0; !is_symbol && i < num_tables; ++i)
2182 WORD platform, encoding;
2184 platform = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2185 FIELD_OFFSET(struct cmap_encoding_record, platformID));
2186 encoding = table_read_be_word(&cmap, offset + i * sizeof(struct cmap_encoding_record) +
2187 FIELD_OFFSET(struct cmap_encoding_record, encodingID));
2189 is_symbol = platform == OPENTYPE_CMAP_TABLE_PLATFORM_WIN &&
2190 encoding == OPENTYPE_CMAP_TABLE_ENCODING_SYMBOL;
2193 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cmap.context);
2196 if (is_symbol)
2197 props->flags |= FONT_IS_SYMBOL;
2199 /* FONT_IS_MONOSPACED, slant angle */
2200 opentype_get_font_table(stream_desc, MS_POST_TAG, &post);
2201 is_monospaced = props->panose.text.proportion == DWRITE_PANOSE_PROPORTION_MONOSPACED;
2202 if (post.data)
2204 if (!is_monospaced)
2205 is_monospaced = !!table_read_dword(&post, FIELD_OFFSET(struct tt_post, fixed_pitch));
2206 props->slant_angle = table_read_be_fixed(&post, FIELD_OFFSET(struct tt_post, italicAngle));
2208 if (post.context)
2209 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, post.context);
2211 if (is_monospaced)
2212 props->flags |= FONT_IS_MONOSPACED;
2214 /* FONT_IS_COLORED */
2215 opentype_get_font_table(stream_desc, MS_COLR_TAG, &colr);
2216 if (colr.data)
2218 opentype_get_font_table(stream_desc, MS_CPAL_TAG, &cpal);
2219 if (cpal.data)
2221 props->flags |= FONT_IS_COLORED;
2222 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cpal.context);
2225 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, colr.context);
2228 TRACE("stretch %d, weight %d, style %d\n", props->stretch, props->weight, props->style);
2230 if (os2.data)
2231 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2232 if (head.data)
2233 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, head.context);
2236 static UINT get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform, USHORT encoding)
2238 UINT codepage = 0;
2240 switch (platform) {
2241 case OPENTYPE_PLATFORM_UNICODE:
2242 break;
2243 case OPENTYPE_PLATFORM_MAC:
2244 switch (encoding)
2246 case TT_NAME_MAC_ENCODING_ROMAN:
2247 codepage = 10000;
2248 break;
2249 case TT_NAME_MAC_ENCODING_JAPANESE:
2250 codepage = 10001;
2251 break;
2252 case TT_NAME_MAC_ENCODING_TRAD_CHINESE:
2253 codepage = 10002;
2254 break;
2255 case TT_NAME_MAC_ENCODING_KOREAN:
2256 codepage = 10003;
2257 break;
2258 case TT_NAME_MAC_ENCODING_ARABIC:
2259 codepage = 10004;
2260 break;
2261 case TT_NAME_MAC_ENCODING_HEBREW:
2262 codepage = 10005;
2263 break;
2264 case TT_NAME_MAC_ENCODING_GREEK:
2265 codepage = 10006;
2266 break;
2267 case TT_NAME_MAC_ENCODING_RUSSIAN:
2268 codepage = 10007;
2269 break;
2270 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE:
2271 codepage = 10008;
2272 break;
2273 case TT_NAME_MAC_ENCODING_THAI:
2274 codepage = 10021;
2275 break;
2276 default:
2277 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2278 break;
2280 break;
2281 case OPENTYPE_PLATFORM_WIN:
2282 switch (encoding)
2284 case TT_NAME_WINDOWS_ENCODING_SYMBOL:
2285 case TT_NAME_WINDOWS_ENCODING_UNICODE_BMP:
2286 case TT_NAME_WINDOWS_ENCODING_UNICODE_FULL:
2287 break;
2288 case TT_NAME_WINDOWS_ENCODING_SJIS:
2289 codepage = 932;
2290 break;
2291 case TT_NAME_WINDOWS_ENCODING_PRC:
2292 codepage = 936;
2293 break;
2294 case TT_NAME_WINDOWS_ENCODING_BIG5:
2295 codepage = 950;
2296 break;
2297 case TT_NAME_WINDOWS_ENCODING_WANSUNG:
2298 codepage = 20949;
2299 break;
2300 case TT_NAME_WINDOWS_ENCODING_JOHAB:
2301 codepage = 1361;
2302 break;
2303 default:
2304 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
2305 break;
2307 break;
2308 default:
2309 FIXME("unknown platform %d\n", platform);
2312 return codepage;
2315 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform, USHORT lang_id, WCHAR *locale, USHORT locale_len)
2317 switch (platform)
2319 case OPENTYPE_PLATFORM_MAC:
2321 const char *locale_name = NULL;
2323 if (lang_id > TT_NAME_MAC_LANGID_AZER_ROMAN)
2324 WARN("invalid mac lang id %d\n", lang_id);
2325 else if (!name_mac_langid_to_locale[lang_id][0])
2326 FIXME("failed to map mac lang id %d to locale name\n", lang_id);
2327 else
2328 locale_name = name_mac_langid_to_locale[lang_id];
2330 if (locale_name)
2331 MultiByteToWideChar(CP_ACP, 0, name_mac_langid_to_locale[lang_id], -1, locale, locale_len);
2332 else
2333 wcscpy(locale, L"en-US");
2334 break;
2336 case OPENTYPE_PLATFORM_WIN:
2337 if (!LCIDToLocaleName(MAKELCID(lang_id, SORT_DEFAULT), locale, locale_len, 0))
2339 FIXME("failed to get locale name for lcid=0x%08lx\n", MAKELCID(lang_id, SORT_DEFAULT));
2340 wcscpy(locale, L"en-US");
2342 break;
2343 case OPENTYPE_PLATFORM_UNICODE:
2344 wcscpy(locale, L"en-US");
2345 break;
2346 default:
2347 FIXME("unknown platform %d\n", platform);
2351 static BOOL opentype_is_english_namerecord(const struct dwrite_fonttable *table, unsigned int idx)
2353 const struct name_header *header = (const struct name_header *)table->data;
2354 const struct name_record *record;
2356 record = &header->records[idx];
2358 return GET_BE_WORD(record->platformID) == OPENTYPE_PLATFORM_MAC &&
2359 GET_BE_WORD(record->languageID) == TT_NAME_MAC_LANGID_ENGLISH;
2362 static BOOL opentype_decode_namerecord(const struct dwrite_fonttable *table, unsigned int idx,
2363 IDWriteLocalizedStrings *strings)
2365 USHORT lang_id, length, offset, encoding, platform;
2366 const struct name_header *header = (const struct name_header *)table->data;
2367 const struct name_record *record;
2368 unsigned int i, string_offset;
2369 BOOL ret = FALSE;
2370 const void *name;
2372 string_offset = table_read_be_word(table, FIELD_OFFSET(struct name_header, stringOffset));
2374 record = &header->records[idx];
2376 platform = GET_BE_WORD(record->platformID);
2377 lang_id = GET_BE_WORD(record->languageID);
2378 length = GET_BE_WORD(record->length);
2379 offset = GET_BE_WORD(record->offset);
2380 encoding = GET_BE_WORD(record->encodingID);
2382 if (!(name = table_read_ensure(table, string_offset + offset, length)))
2383 return FALSE;
2385 if (lang_id < 0x8000)
2387 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
2388 WCHAR *name_string;
2389 UINT codepage;
2391 codepage = get_name_record_codepage(platform, encoding);
2392 get_name_record_locale(platform, lang_id, locale, ARRAY_SIZE(locale));
2394 if (codepage)
2396 DWORD len = MultiByteToWideChar(codepage, 0, name, length, NULL, 0);
2397 name_string = malloc(sizeof(WCHAR) * (len+1));
2398 MultiByteToWideChar(codepage, 0, name, length, name_string, len);
2399 name_string[len] = 0;
2401 else
2403 length /= sizeof(WCHAR);
2404 name_string = heap_strdupnW(name, length);
2405 for (i = 0; i < length; i++)
2406 name_string[i] = GET_BE_WORD(name_string[i]);
2409 TRACE("string %s for locale %s found\n", debugstr_w(name_string), debugstr_w(locale));
2410 add_localizedstring(strings, locale, name_string);
2411 free(name_string);
2413 ret = !wcscmp(locale, L"en-US");
2415 else
2416 FIXME("handle NAME format 1\n");
2418 return ret;
2421 static HRESULT opentype_get_font_strings_from_id(const struct dwrite_fonttable *table, enum OPENTYPE_STRING_ID id,
2422 IDWriteLocalizedStrings **strings)
2424 int i, count, candidate_mac, candidate_mac_en, candidate_unicode;
2425 const struct name_record *records;
2426 BOOL has_english;
2427 WORD format;
2428 HRESULT hr;
2430 if (!table->data)
2431 return E_FAIL;
2433 if (FAILED(hr = create_localizedstrings(strings)))
2434 return hr;
2436 format = table_read_be_word(table, FIELD_OFFSET(struct name_header, format));
2438 if (format != 0 && format != 1)
2439 FIXME("unsupported NAME format %d\n", format);
2441 count = table_read_be_word(table, FIELD_OFFSET(struct name_header, count));
2443 if (!(records = table_read_ensure(table, FIELD_OFFSET(struct name_header, records),
2444 count * sizeof(struct name_record))))
2446 count = 0;
2449 has_english = FALSE;
2450 candidate_unicode = candidate_mac = candidate_mac_en = -1;
2452 for (i = 0; i < count; i++)
2454 unsigned short platform;
2456 if (GET_BE_WORD(records[i].nameID) != id)
2457 continue;
2459 platform = GET_BE_WORD(records[i].platformID);
2460 switch (platform)
2462 /* Skip Unicode or Mac entries for now, fonts tend to duplicate those
2463 strings as WIN platform entries. If font does not have WIN entry for
2464 this id, we will use Mac or Unicode platform entry while assuming
2465 en-US locale. */
2466 case OPENTYPE_PLATFORM_UNICODE:
2467 if (candidate_unicode == -1)
2468 candidate_unicode = i;
2469 break;
2470 case OPENTYPE_PLATFORM_MAC:
2471 if (candidate_mac == -1)
2472 candidate_mac = i;
2473 if (candidate_mac_en == -1 && opentype_is_english_namerecord(table, i))
2474 candidate_mac_en = i;
2475 break;
2476 case OPENTYPE_PLATFORM_WIN:
2477 has_english |= opentype_decode_namerecord(table, i, *strings);
2478 break;
2479 default:
2480 FIXME("platform %i not supported\n", platform);
2481 break;
2485 if (!get_localizedstrings_count(*strings) && candidate_mac != -1)
2486 has_english |= opentype_decode_namerecord(table, candidate_mac, *strings);
2487 if (!get_localizedstrings_count(*strings) && candidate_unicode != -1)
2488 has_english |= opentype_decode_namerecord(table, candidate_unicode, *strings);
2489 if (!has_english && candidate_mac_en != -1)
2490 opentype_decode_namerecord(table, candidate_mac_en, *strings);
2492 if (!get_localizedstrings_count(*strings))
2494 IDWriteLocalizedStrings_Release(*strings);
2495 *strings = NULL;
2498 if (*strings)
2499 sort_localizedstrings(*strings);
2501 return *strings ? S_OK : E_FAIL;
2504 static WCHAR *meta_get_lng_name(WCHAR *str, WCHAR **ctx)
2506 WCHAR *ret;
2508 if (!str) str = *ctx;
2509 while (*str && wcschr(L", ", *str)) str++;
2510 if (!*str) return NULL;
2511 ret = str++;
2512 while (*str && !wcschr(L", ", *str)) str++;
2513 if (*str) *str++ = 0;
2514 *ctx = str;
2516 return ret;
2519 static HRESULT opentype_get_font_strings_from_meta(const struct file_stream_desc *stream_desc,
2520 DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **ret)
2522 const struct meta_data_map *maps;
2523 IDWriteLocalizedStrings *strings;
2524 struct dwrite_fonttable meta;
2525 DWORD version, i, count, tag;
2526 HRESULT hr;
2528 *ret = NULL;
2530 switch (id)
2532 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2533 tag = MS_DLNG_TAG;
2534 break;
2535 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2536 tag = MS_SLNG_TAG;
2537 break;
2538 default:
2539 WARN("Unexpected id %d.\n", id);
2540 return S_OK;
2543 if (FAILED(hr = create_localizedstrings(&strings)))
2544 return hr;
2546 opentype_get_font_table(stream_desc, MS_META_TAG, &meta);
2548 if (meta.data)
2550 version = table_read_be_dword(&meta, 0);
2551 if (version != 1)
2553 WARN("Unexpected meta table version %ld.\n", version);
2554 goto end;
2557 count = table_read_be_dword(&meta, FIELD_OFFSET(struct meta_header, data_maps_count));
2558 if (!(maps = table_read_ensure(&meta, FIELD_OFFSET(struct meta_header, maps),
2559 count * sizeof(struct meta_data_map))))
2560 goto end;
2562 for (i = 0; i < count; ++i)
2564 const char *data;
2566 if (maps[i].tag == tag && maps[i].length)
2568 DWORD length = GET_BE_DWORD(maps[i].length), j;
2570 if ((data = table_read_ensure(&meta, GET_BE_DWORD(maps[i].offset), length)))
2572 WCHAR *ptrW, *ctx, *token;
2574 if (!(ptrW = malloc((length + 1) * sizeof(WCHAR))))
2576 hr = E_OUTOFMEMORY;
2577 goto end;
2580 /* Data is stored in comma separated list, ASCII range only. */
2581 for (j = 0; j < length; ++j)
2582 ptrW[j] = data[j];
2583 ptrW[length] = 0;
2585 token = meta_get_lng_name(ptrW, &ctx);
2587 while (token)
2589 add_localizedstring(strings, L"", token);
2590 token = meta_get_lng_name(NULL, &ctx);
2593 free(ptrW);
2597 end:
2598 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, meta.context);
2601 if (IDWriteLocalizedStrings_GetCount(strings))
2602 *ret = strings;
2603 else
2604 IDWriteLocalizedStrings_Release(strings);
2606 return hr;
2609 HRESULT opentype_get_font_info_strings(const struct file_stream_desc *stream_desc, DWRITE_INFORMATIONAL_STRING_ID id,
2610 IDWriteLocalizedStrings **strings)
2612 struct dwrite_fonttable name;
2614 switch (id)
2616 case DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG:
2617 case DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG:
2618 opentype_get_font_strings_from_meta(stream_desc, id, strings);
2619 break;
2620 default:
2621 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2622 opentype_get_font_strings_from_id(&name, dwriteid_to_opentypeid[id], strings);
2623 if (name.context)
2624 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2627 return S_OK;
2630 HRESULT opentype_get_font_familyname(const struct file_stream_desc *stream_desc, DWRITE_FONT_FAMILY_MODEL family_model,
2631 IDWriteLocalizedStrings **names)
2633 static const unsigned int wws_candidates[] =
2635 OPENTYPE_STRING_WWS_FAMILY_NAME,
2636 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
2637 OPENTYPE_STRING_FAMILY_NAME,
2638 ~0u,
2640 static const unsigned int typographic_candidates[] =
2642 OPENTYPE_STRING_TYPOGRAPHIC_FAMILY_NAME,
2643 OPENTYPE_STRING_WWS_FAMILY_NAME,
2644 OPENTYPE_STRING_FAMILY_NAME,
2645 ~0u,
2647 struct dwrite_fonttable os2, name;
2648 const unsigned int *id;
2649 BOOL try_wws_name;
2650 HRESULT hr;
2652 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2653 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2655 *names = NULL;
2657 if (family_model == DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC)
2659 id = typographic_candidates;
2661 else
2663 /* FamilyName locating order is WWS Family Name -> Preferred Family Name -> Family Name. If font claims to
2664 have 'Preferred Family Name' in WWS format, then WWS name is not used. */
2666 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2667 /* If Preferred Family doesn't conform to WWS model try WWS name. */
2668 try_wws_name = os2.data && !(table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) & OS2_FSSELECTION_WWS);
2669 if (os2.context)
2670 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2672 id = wws_candidates;
2673 if (!try_wws_name) id++;
2676 while (*id != ~0u)
2678 if (SUCCEEDED(hr = opentype_get_font_strings_from_id(&name, *id, names)))
2679 break;
2680 id++;
2683 if (name.context)
2684 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2686 return hr;
2689 /* FaceName locating order is WWS Face Name -> Preferred Face Name -> Face Name. If font claims to
2690 have 'Preferred Face Name' in WWS format, then WWS name is not used. */
2691 HRESULT opentype_get_font_facename(struct file_stream_desc *stream_desc, WCHAR *lfname, IDWriteLocalizedStrings **names)
2693 struct dwrite_fonttable os2, name;
2694 IDWriteLocalizedStrings *lfnames;
2695 UINT16 fsselection;
2696 HRESULT hr;
2698 opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2);
2699 opentype_get_font_table(stream_desc, MS_NAME_TAG, &name);
2701 *names = NULL;
2703 /* if Preferred Family doesn't conform to WWS model try WWS name */
2704 fsselection = os2.data ? table_read_be_word(&os2, FIELD_OFFSET(struct tt_os2, fsSelection)) : 0;
2705 if (os2.data && !(fsselection & OS2_FSSELECTION_WWS))
2706 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_WWS_SUBFAMILY_NAME, names);
2707 else
2708 hr = E_FAIL;
2710 if (FAILED(hr))
2711 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_TYPOGRAPHIC_SUBFAMILY_NAME, names);
2712 if (FAILED(hr))
2713 hr = opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_SUBFAMILY_NAME, names);
2715 /* User locale is preferred, with fallback to en-us. */
2716 *lfname = 0;
2717 if (SUCCEEDED(opentype_get_font_strings_from_id(&name, OPENTYPE_STRING_FAMILY_NAME, &lfnames)))
2719 WCHAR localeW[LOCALE_NAME_MAX_LENGTH];
2720 UINT32 index;
2721 BOOL exists;
2723 exists = FALSE;
2724 if (GetSystemDefaultLocaleName(localeW, ARRAY_SIZE(localeW)))
2725 IDWriteLocalizedStrings_FindLocaleName(lfnames, localeW, &index, &exists);
2727 if (!exists)
2728 IDWriteLocalizedStrings_FindLocaleName(lfnames, L"en-us", &index, &exists);
2730 if (exists) {
2731 UINT32 length = 0;
2732 WCHAR *nameW;
2734 IDWriteLocalizedStrings_GetStringLength(lfnames, index, &length);
2735 nameW = malloc((length + 1) * sizeof(WCHAR));
2736 if (nameW)
2738 *nameW = 0;
2739 IDWriteLocalizedStrings_GetString(lfnames, index, nameW, length + 1);
2740 lstrcpynW(lfname, nameW, LF_FACESIZE);
2741 free(nameW);
2745 IDWriteLocalizedStrings_Release(lfnames);
2748 if (os2.context)
2749 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, os2.context);
2750 if (name.context)
2751 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, name.context);
2753 return hr;
2756 static const struct ot_langsys *opentype_get_langsys(const struct ot_gsubgpos_table *table, unsigned int script_index,
2757 unsigned int language_index, unsigned int *feature_count)
2759 unsigned int table_offset, langsys_offset;
2760 const struct ot_langsys *langsys = NULL;
2762 *feature_count = 0;
2764 if (!table->table.data || script_index == ~0u)
2765 return NULL;
2767 /* ScriptTable offset. */
2768 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
2769 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
2770 if (!table_offset)
2771 return NULL;
2773 if (language_index == ~0u)
2774 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
2775 else
2776 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
2777 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
2778 FIELD_OFFSET(struct ot_langsys_record, langsys));
2779 langsys_offset += table->script_list + table_offset;
2781 *feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
2782 if (*feature_count)
2783 langsys = table_read_ensure(&table->table, langsys_offset, FIELD_OFFSET(struct ot_langsys, feature_index[*feature_count]));
2784 if (!langsys)
2785 *feature_count = 0;
2787 return langsys;
2790 void opentype_get_typographic_features(struct ot_gsubgpos_table *table, unsigned int script_index,
2791 unsigned int language_index, struct tag_array *t)
2793 unsigned int i, total_feature_count, script_feature_count;
2794 const struct ot_feature_list *feature_list;
2795 const struct ot_langsys *langsys = NULL;
2797 langsys = opentype_get_langsys(table, script_index, language_index, &script_feature_count);
2799 total_feature_count = table_read_be_word(&table->table, table->feature_list);
2800 if (!total_feature_count)
2801 return;
2803 feature_list = table_read_ensure(&table->table, table->feature_list,
2804 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
2805 if (!feature_list)
2806 return;
2808 for (i = 0; i < script_feature_count; ++i)
2810 unsigned int feature_index = GET_BE_WORD(langsys->feature_index[i]);
2811 if (feature_index >= total_feature_count)
2812 continue;
2814 if (!dwrite_array_reserve((void **)&t->tags, &t->capacity, t->count + 1, sizeof(*t->tags)))
2815 return;
2817 t->tags[t->count++] = feature_list->features[feature_index].tag;
2821 static unsigned int find_vdmx_group(const struct vdmx_header *hdr)
2823 WORD num_ratios, i;
2824 const struct vdmx_ratio *ratios = (struct vdmx_ratio *)(hdr + 1);
2825 BYTE dev_x_ratio = 1, dev_y_ratio = 1;
2826 unsigned int group_offset = 0;
2828 num_ratios = GET_BE_WORD(hdr->num_ratios);
2830 for (i = 0; i < num_ratios; i++) {
2832 if (!ratios[i].bCharSet) continue;
2834 if ((ratios[i].xRatio == 0 && ratios[i].yStartRatio == 0 &&
2835 ratios[i].yEndRatio == 0) ||
2836 (ratios[i].xRatio == dev_x_ratio && ratios[i].yStartRatio <= dev_y_ratio &&
2837 ratios[i].yEndRatio >= dev_y_ratio))
2839 group_offset = GET_BE_WORD(*((WORD *)(ratios + num_ratios) + i));
2840 break;
2844 return group_offset;
2847 BOOL opentype_get_vdmx_size(const struct dwrite_fonttable *vdmx, INT emsize, UINT16 *ascent, UINT16 *descent)
2849 unsigned int num_ratios, num_recs, group_offset, i;
2850 const struct vdmx_header *header;
2851 const struct vdmx_group *group;
2853 if (!vdmx->exists)
2854 return FALSE;
2856 num_ratios = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_ratios));
2857 num_recs = table_read_be_word(vdmx, FIELD_OFFSET(struct vdmx_header, num_recs));
2859 header = table_read_ensure(vdmx, 0, sizeof(*header) + num_ratios * sizeof(struct vdmx_ratio) +
2860 num_recs * sizeof(*group));
2862 if (!header)
2863 return FALSE;
2865 group_offset = find_vdmx_group(header);
2866 if (!group_offset)
2867 return FALSE;
2869 num_recs = table_read_be_word(vdmx, group_offset);
2870 group = table_read_ensure(vdmx, group_offset, FIELD_OFFSET(struct vdmx_group, entries[num_recs]));
2872 if (!group)
2873 return FALSE;
2875 if (emsize < group->startsz || emsize >= group->endsz)
2876 return FALSE;
2878 for (i = 0; i < num_recs; ++i)
2880 WORD ppem = GET_BE_WORD(group->entries[i].yPelHeight);
2881 if (ppem > emsize) {
2882 FIXME("interpolate %d\n", emsize);
2883 return FALSE;
2886 if (ppem == emsize) {
2887 *ascent = (SHORT)GET_BE_WORD(group->entries[i].yMax);
2888 *descent = -(SHORT)GET_BE_WORD(group->entries[i].yMin);
2889 return TRUE;
2893 return FALSE;
2896 unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable *gasp, float emsize)
2898 unsigned int version, num_ranges, i;
2899 const struct gasp_header *table;
2900 WORD flags = 0;
2902 if (!gasp->exists)
2903 return 0;
2905 num_ranges = table_read_be_word(gasp, FIELD_OFFSET(struct gasp_header, num_ranges));
2907 table = table_read_ensure(gasp, 0, FIELD_OFFSET(struct gasp_header, ranges[num_ranges]));
2908 if (!table)
2909 return 0;
2911 version = GET_BE_WORD(table->version);
2912 if (version > 1)
2914 ERR("Unsupported gasp table format version %u.\n", version);
2915 goto done;
2918 for (i = 0; i < num_ranges; ++i)
2920 flags = GET_BE_WORD(table->ranges[i].flags);
2921 if (emsize <= GET_BE_WORD(table->ranges[i].max_ppem)) break;
2924 done:
2925 return flags;
2928 unsigned int opentype_get_cpal_palettecount(const struct dwrite_fonttable *cpal)
2930 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palettes));
2933 unsigned int opentype_get_cpal_paletteentrycount(const struct dwrite_fonttable *cpal)
2935 return table_read_be_word(cpal, FIELD_OFFSET(struct cpal_header_0, num_palette_entries));
2938 HRESULT opentype_get_cpal_entries(const struct dwrite_fonttable *cpal, unsigned int palette,
2939 unsigned int first_entry_index, unsigned int entry_count, DWRITE_COLOR_F *entries)
2941 unsigned int num_palettes, num_palette_entries, i;
2942 const struct cpal_color_record *records;
2943 const struct cpal_header_0 *header;
2944 struct d3d_color
2946 float r;
2947 float g;
2948 float b;
2949 float a;
2950 } *colors = (void *)entries;
2952 header = table_read_ensure(cpal, 0, sizeof(*header));
2954 if (!cpal->exists || !header)
2955 return DWRITE_E_NOCOLOR;
2957 num_palettes = GET_BE_WORD(header->num_palettes);
2958 if (palette >= num_palettes)
2959 return DWRITE_E_NOCOLOR;
2961 header = table_read_ensure(cpal, 0, FIELD_OFFSET(struct cpal_header_0, color_record_indices[palette]));
2962 if (!header)
2963 return DWRITE_E_NOCOLOR;
2965 num_palette_entries = GET_BE_WORD(header->num_palette_entries);
2966 if (first_entry_index + entry_count > num_palette_entries)
2967 return E_INVALIDARG;
2969 records = table_read_ensure(cpal, GET_BE_DWORD(header->offset_first_color_record),
2970 sizeof(*records) * GET_BE_WORD(header->num_color_records));
2971 if (!records)
2972 return DWRITE_E_NOCOLOR;
2974 first_entry_index += GET_BE_WORD(header->color_record_indices[palette]);
2976 for (i = 0; i < entry_count; ++i)
2978 colors[i].r = records[first_entry_index + i].red / 255.0f;
2979 colors[i].g = records[first_entry_index + i].green / 255.0f;
2980 colors[i].b = records[first_entry_index + i].blue / 255.0f;
2981 colors[i].a = records[first_entry_index + i].alpha / 255.0f;
2984 return S_OK;
2987 static int __cdecl colr_compare_gid(const void *g, const void *r)
2989 const struct colr_baseglyph_record *record = r;
2990 UINT16 glyph = *(UINT16*)g, GID = GET_BE_WORD(record->glyph);
2991 int ret = 0;
2993 if (glyph > GID)
2994 ret = 1;
2995 else if (glyph < GID)
2996 ret = -1;
2998 return ret;
3001 HRESULT opentype_get_colr_glyph(const struct dwrite_fonttable *colr, UINT16 glyph, struct dwrite_colorglyph *ret)
3003 unsigned int num_baseglyph_records, offset_baseglyph_records;
3004 const struct colr_baseglyph_record *record;
3005 const struct colr_layer_record *layer;
3006 const struct colr_header *header;
3008 memset(ret, 0, sizeof(*ret));
3009 ret->glyph = glyph;
3010 ret->palette_index = 0xffff;
3012 header = table_read_ensure(colr, 0, sizeof(*header));
3013 if (!header)
3014 return S_FALSE;
3016 num_baseglyph_records = GET_BE_WORD(header->num_baseglyph_records);
3017 offset_baseglyph_records = GET_BE_DWORD(header->offset_baseglyph_records);
3018 if (!table_read_ensure(colr, offset_baseglyph_records, num_baseglyph_records * sizeof(*record)))
3020 return S_FALSE;
3023 record = bsearch(&glyph, colr->data + offset_baseglyph_records, num_baseglyph_records,
3024 sizeof(*record), colr_compare_gid);
3025 if (!record)
3026 return S_FALSE;
3028 ret->first_layer = GET_BE_WORD(record->first_layer_index);
3029 ret->num_layers = GET_BE_WORD(record->num_layers);
3031 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
3032 (ret->first_layer + ret->layer) * sizeof(*layer))))
3034 layer += ret->first_layer + ret->layer;
3035 ret->glyph = GET_BE_WORD(layer->glyph);
3036 ret->palette_index = GET_BE_WORD(layer->palette_index);
3039 return S_OK;
3042 void opentype_colr_next_glyph(const struct dwrite_fonttable *colr, struct dwrite_colorglyph *glyph)
3044 const struct colr_layer_record *layer;
3045 const struct colr_header *header;
3047 /* iterated all the way through */
3048 if (glyph->layer == glyph->num_layers)
3049 return;
3051 if (!(header = table_read_ensure(colr, 0, sizeof(*header))))
3052 return;
3054 glyph->layer++;
3056 if ((layer = table_read_ensure(colr, GET_BE_DWORD(header->offset_layer_records),
3057 (glyph->first_layer + glyph->layer) * sizeof(*layer))))
3059 layer += glyph->first_layer + glyph->layer;
3060 glyph->glyph = GET_BE_WORD(layer->glyph);
3061 glyph->palette_index = GET_BE_WORD(layer->palette_index);
3065 static BOOL opentype_has_font_table(IDWriteFontFace5 *fontface, UINT32 tag)
3067 BOOL exists = FALSE;
3068 const void *data;
3069 void *context;
3070 UINT32 size;
3071 HRESULT hr;
3073 hr = IDWriteFontFace5_TryGetFontTable(fontface, tag, &data, &size, &context, &exists);
3074 if (FAILED(hr))
3075 return FALSE;
3077 if (exists)
3078 IDWriteFontFace5_ReleaseFontTable(fontface, context);
3080 return exists;
3083 static unsigned int opentype_get_sbix_formats(IDWriteFontFace5 *fontface)
3085 unsigned int num_strikes, num_glyphs, i, j, ret = 0;
3086 const struct sbix_header *sbix_header;
3087 struct dwrite_fonttable table;
3089 memset(&table, 0, sizeof(table));
3090 table.exists = TRUE;
3092 if (!get_fontface_table(fontface, MS_MAXP_TAG, &table))
3093 return 0;
3095 num_glyphs = table_read_be_word(&table, FIELD_OFFSET(struct maxp, num_glyphs));
3097 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
3099 memset(&table, 0, sizeof(table));
3100 table.exists = TRUE;
3102 if (!get_fontface_table(fontface, MS_SBIX_TAG, &table))
3103 return 0;
3105 num_strikes = table_read_be_dword(&table, FIELD_OFFSET(struct sbix_header, num_strikes));
3106 sbix_header = table_read_ensure(&table, 0, FIELD_OFFSET(struct sbix_header, strike_offset[num_strikes]));
3108 if (sbix_header)
3110 for (i = 0; i < num_strikes; ++i)
3112 unsigned int strike_offset = GET_BE_DWORD(sbix_header->strike_offset[i]);
3113 const struct sbix_strike *strike = table_read_ensure(&table, strike_offset,
3114 FIELD_OFFSET(struct sbix_strike, glyphdata_offsets[num_glyphs + 1]));
3116 if (!strike)
3117 continue;
3119 for (j = 0; j < num_glyphs; j++)
3121 unsigned int offset = GET_BE_DWORD(strike->glyphdata_offsets[j]);
3122 unsigned int next_offset = GET_BE_DWORD(strike->glyphdata_offsets[j + 1]);
3123 const struct sbix_glyph_data *glyph_data;
3125 if (offset == next_offset)
3126 continue;
3128 glyph_data = table_read_ensure(&table, strike_offset + offset, sizeof(*glyph_data));
3129 if (!glyph_data)
3130 continue;
3132 switch (glyph_data->graphic_type)
3134 case MS_PNG__TAG:
3135 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3136 break;
3137 case MS_JPG__TAG:
3138 ret |= DWRITE_GLYPH_IMAGE_FORMATS_JPEG;
3139 break;
3140 case MS_TIFF_TAG:
3141 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TIFF;
3142 break;
3143 default:
3144 FIXME("unexpected bitmap format %s\n", debugstr_tag(GET_BE_DWORD(glyph_data->graphic_type)));
3150 IDWriteFontFace5_ReleaseFontTable(fontface, table.context);
3152 return ret;
3155 static unsigned int opentype_get_cblc_formats(IDWriteFontFace5 *fontface)
3157 const unsigned int format_mask = DWRITE_GLYPH_IMAGE_FORMATS_PNG |
3158 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3159 const struct cblc_bitmapsize_table *sizes;
3160 struct dwrite_fonttable cblc = { 0 };
3161 unsigned int num_sizes, i, ret = 0;
3162 const struct cblc_header *header;
3164 cblc.exists = TRUE;
3165 if (!get_fontface_table(fontface, MS_CBLC_TAG, &cblc))
3166 return 0;
3168 num_sizes = table_read_be_dword(&cblc, FIELD_OFFSET(struct cblc_header, num_sizes));
3169 sizes = table_read_ensure(&cblc, sizeof(*header), num_sizes * sizeof(*sizes));
3171 if (sizes)
3173 for (i = 0; i < num_sizes; ++i)
3175 BYTE bpp = sizes[i].bit_depth;
3177 if ((ret & format_mask) == format_mask)
3178 break;
3180 if (bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8)
3181 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PNG;
3182 else if (bpp == 32)
3183 ret |= DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
3187 IDWriteFontFace5_ReleaseFontTable(fontface, cblc.context);
3189 return ret;
3192 UINT32 opentype_get_glyph_image_formats(IDWriteFontFace5 *fontface)
3194 UINT32 ret = DWRITE_GLYPH_IMAGE_FORMATS_NONE;
3196 if (opentype_has_font_table(fontface, MS_GLYF_TAG))
3197 ret |= DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
3199 if (opentype_has_font_table(fontface, MS_CFF__TAG) ||
3200 opentype_has_font_table(fontface, MS_CFF2_TAG))
3201 ret |= DWRITE_GLYPH_IMAGE_FORMATS_CFF;
3203 if (opentype_has_font_table(fontface, MS_COLR_TAG))
3204 ret |= DWRITE_GLYPH_IMAGE_FORMATS_COLR;
3206 if (opentype_has_font_table(fontface, MS_SVG__TAG))
3207 ret |= DWRITE_GLYPH_IMAGE_FORMATS_SVG;
3209 if (opentype_has_font_table(fontface, MS_SBIX_TAG))
3210 ret |= opentype_get_sbix_formats(fontface);
3212 if (opentype_has_font_table(fontface, MS_CBLC_TAG))
3213 ret |= opentype_get_cblc_formats(fontface);
3215 return ret;
3218 DWRITE_CONTAINER_TYPE opentype_analyze_container_type(void const *data, UINT32 data_size)
3220 DWORD signature;
3222 if (data_size < sizeof(DWORD))
3223 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3225 /* Both WOFF and WOFF2 start with 4 bytes signature. */
3226 signature = *(DWORD *)data;
3228 switch (signature)
3230 case MS_WOFF_TAG:
3231 return DWRITE_CONTAINER_TYPE_WOFF;
3232 case MS_WOF2_TAG:
3233 return DWRITE_CONTAINER_TYPE_WOFF2;
3234 default:
3235 return DWRITE_CONTAINER_TYPE_UNKNOWN;
3239 void opentype_layout_scriptshaping_cache_init(struct scriptshaping_cache *cache)
3241 cache->font->grab_font_table(cache->context, MS_GSUB_TAG, &cache->gsub.table.data, &cache->gsub.table.size,
3242 &cache->gsub.table.context);
3244 if (cache->gsub.table.data)
3246 cache->gsub.script_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, script_list));
3247 cache->gsub.feature_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3248 cache->gsub.lookup_list = table_read_be_word(&cache->gsub.table, FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3251 cache->font->grab_font_table(cache->context, MS_GPOS_TAG, &cache->gpos.table.data, &cache->gpos.table.size,
3252 &cache->gpos.table.context);
3254 if (cache->gpos.table.data)
3256 cache->gpos.script_list = table_read_be_word(&cache->gpos.table,
3257 FIELD_OFFSET(struct gpos_gsub_header, script_list));
3258 cache->gpos.feature_list = table_read_be_word(&cache->gpos.table,
3259 FIELD_OFFSET(struct gpos_gsub_header, feature_list));
3260 cache->gpos.lookup_list = table_read_be_word(&cache->gpos.table,
3261 FIELD_OFFSET(struct gpos_gsub_header, lookup_list));
3264 cache->font->grab_font_table(cache->context, MS_GDEF_TAG, &cache->gdef.table.data, &cache->gdef.table.size,
3265 &cache->gdef.table.context);
3267 if (cache->gdef.table.data)
3269 unsigned int version = table_read_be_dword(&cache->gdef.table, 0);
3271 cache->gdef.classdef = table_read_be_word(&cache->gdef.table, FIELD_OFFSET(struct gdef_header, classdef));
3272 cache->gdef.markattachclassdef = table_read_be_word(&cache->gdef.table,
3273 FIELD_OFFSET(struct gdef_header, markattach_classdef));
3274 if (version >= 0x00010002)
3275 cache->gdef.markglyphsetdef = table_read_be_word(&cache->gdef.table,
3276 FIELD_OFFSET(struct gdef_header, markglyphsetdef));
3280 unsigned int opentype_layout_find_script(const struct scriptshaping_cache *cache, unsigned int kind, DWORD script,
3281 unsigned int *script_index)
3283 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3284 UINT16 script_count;
3285 unsigned int i;
3287 *script_index = ~0u;
3289 script_count = table_read_be_word(&table->table, table->script_list);
3290 if (!script_count)
3291 return 0;
3293 for (i = 0; i < script_count; i++)
3295 unsigned int tag = table_read_dword(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3296 i * sizeof(struct ot_script_record));
3297 if (!tag)
3298 continue;
3300 if (tag == script)
3302 *script_index = i;
3303 return script;
3307 return 0;
3310 unsigned int opentype_layout_find_language(const struct scriptshaping_cache *cache, unsigned int kind, DWORD language,
3311 unsigned int script_index, unsigned int *language_index)
3313 const struct ot_gsubgpos_table *table = kind == MS_GSUB_TAG ? &cache->gsub : &cache->gpos;
3314 UINT16 table_offset, lang_count;
3315 unsigned int i;
3317 *language_index = ~0u;
3319 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
3320 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
3321 if (!table_offset)
3322 return 0;
3324 lang_count = table_read_be_word(&table->table, table->script_list + table_offset +
3325 FIELD_OFFSET(struct ot_script, langsys_count));
3326 for (i = 0; i < lang_count; i++)
3328 unsigned int tag = table_read_dword(&table->table, table->script_list + table_offset +
3329 FIELD_OFFSET(struct ot_script, langsys) + i * sizeof(struct ot_langsys_record));
3331 if (tag == language)
3333 *language_index = i;
3334 return language;
3338 /* Try 'defaultLangSys' if it's set. */
3339 if (table_read_be_word(&table->table, table->script_list + table_offset))
3340 return ~0u;
3342 return 0;
3345 static int __cdecl gdef_class_compare_format2(const void *g, const void *r)
3347 const struct ot_gdef_class_range *range = r;
3348 UINT16 glyph = *(UINT16 *)g;
3350 if (glyph < GET_BE_WORD(range->start_glyph))
3351 return -1;
3352 else if (glyph > GET_BE_WORD(range->end_glyph))
3353 return 1;
3354 else
3355 return 0;
3358 static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttable *table,
3359 unsigned int offset, UINT16 glyph)
3361 WORD format = table_read_be_word(table, offset), count;
3362 unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED;
3364 if (format == 1)
3366 const struct ot_gdef_classdef_format1 *format1;
3368 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
3369 format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
3370 if (format1)
3372 WORD start_glyph = GET_BE_WORD(format1->start_glyph);
3373 if (glyph >= start_glyph && (glyph - start_glyph) < count)
3375 glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
3376 if (glyph_class > GDEF_CLASS_MAX)
3377 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3381 else if (format == 2)
3383 const struct ot_gdef_classdef_format2 *format2;
3385 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
3386 format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
3387 if (format2)
3389 const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
3390 sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
3391 glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
3392 GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
3393 if (glyph_class > GDEF_CLASS_MAX)
3394 glyph_class = GDEF_CLASS_UNCLASSIFIED;
3397 else
3398 WARN("Unknown GDEF format %u.\n", format);
3400 return glyph_class;
3403 static unsigned int opentype_set_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3405 struct scriptshaping_cache *cache = context->cache;
3406 unsigned int glyph_class = 0, props;
3408 if (cache->gdef.classdef)
3410 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.classdef,
3411 context->u.buffer.glyphs[idx]);
3414 switch (glyph_class)
3416 case GDEF_CLASS_BASE:
3417 props = GLYPH_PROP_BASE;
3418 break;
3419 case GDEF_CLASS_LIGATURE:
3420 props = GLYPH_PROP_LIGATURE;
3421 break;
3422 case GDEF_CLASS_MARK:
3423 props = GLYPH_PROP_MARK;
3424 if (cache->gdef.markattachclassdef)
3426 glyph_class = opentype_layout_get_glyph_class(&cache->gdef.table, cache->gdef.markattachclassdef,
3427 context->u.buffer.glyphs[idx]);
3428 props |= glyph_class << 8;
3430 break;
3431 default:
3432 props = 0;
3435 context->glyph_infos[idx].props = props;
3437 return props;
3440 static void opentype_set_subst_glyph_props(struct scriptshaping_context *context, unsigned int idx)
3442 unsigned int glyph_props = opentype_set_glyph_props(context, idx) & LOOKUP_FLAG_IGNORE_MASK;
3443 context->u.subst.glyph_props[idx].isDiacritic = !!(glyph_props == GLYPH_PROP_MARK);
3444 context->u.subst.glyph_props[idx].isZeroWidthSpace = !!(glyph_props == GLYPH_PROP_MARK);
3447 struct coverage_compare_format1_context
3449 UINT16 glyph;
3450 const UINT16 *table_base;
3451 unsigned int *coverage_index;
3454 static int __cdecl coverage_compare_format1(const void *left, const void *right)
3456 const struct coverage_compare_format1_context *context = left;
3457 UINT16 glyph = GET_BE_WORD(*(UINT16 *)right);
3458 int ret;
3460 ret = context->glyph - glyph;
3461 if (!ret)
3462 *context->coverage_index = (UINT16 *)right - context->table_base;
3464 return ret;
3467 static int __cdecl coverage_compare_format2(const void *g, const void *r)
3469 const struct ot_coverage_range *range = r;
3470 UINT16 glyph = *(UINT16 *)g;
3472 if (glyph < GET_BE_WORD(range->start_glyph))
3473 return -1;
3474 else if (glyph > GET_BE_WORD(range->end_glyph))
3475 return 1;
3476 else
3477 return 0;
3480 static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable *table, unsigned int coverage,
3481 UINT16 glyph)
3483 WORD format = table_read_be_word(table, coverage), count;
3485 count = table_read_be_word(table, coverage + 2);
3487 if (format == 1)
3489 const struct ot_coverage_format1 *format1 = table_read_ensure(table, coverage,
3490 FIELD_OFFSET(struct ot_coverage_format1, glyphs[count]));
3491 struct coverage_compare_format1_context context;
3492 unsigned int coverage_index = GLYPH_NOT_COVERED;
3494 if (format1)
3496 context.glyph = glyph;
3497 context.table_base = format1->glyphs;
3498 context.coverage_index = &coverage_index;
3500 bsearch(&context, format1->glyphs, count, sizeof(glyph), coverage_compare_format1);
3503 return coverage_index;
3505 else if (format == 2)
3507 const struct ot_coverage_format2 *format2 = table_read_ensure(table, coverage,
3508 FIELD_OFFSET(struct ot_coverage_format2, ranges[count]));
3509 if (format2)
3511 const struct ot_coverage_range *range = bsearch(&glyph, format2->ranges, count,
3512 sizeof(struct ot_coverage_range), coverage_compare_format2);
3513 return range && glyph <= GET_BE_WORD(range->end_glyph) ?
3514 GET_BE_WORD(range->startcoverage_index) + glyph - GET_BE_WORD(range->start_glyph) :
3515 GLYPH_NOT_COVERED;
3518 else
3519 WARN("Unknown coverage format %u.\n", format);
3521 return -1;
3524 static inline unsigned int dwrite_popcount(unsigned int x)
3526 #if defined(__MINGW32__)
3527 return __builtin_popcount(x);
3528 #else
3529 x -= x >> 1 & 0x55555555;
3530 x = (x & 0x33333333) + (x >> 2 & 0x33333333);
3531 return ((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
3532 #endif
3535 static float opentype_scale_gpos_be_value(WORD value, float emsize, UINT16 upem)
3537 return (short)GET_BE_WORD(value) * emsize / upem;
3540 static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context *context, unsigned int offset)
3542 const struct dwrite_fonttable *table = &context->table->table;
3543 unsigned int start_size, end_size, format, value_word;
3544 unsigned int index, ppem, mask;
3545 int value;
3547 if (!offset)
3548 return 0;
3550 start_size = table_read_be_word(table, offset);
3551 end_size = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, end_size));
3553 ppem = context->emsize;
3554 if (ppem < start_size || ppem > end_size)
3555 return 0;
3557 format = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, format));
3559 if (format < 1 || format > 3)
3560 return 0;
3562 index = ppem - start_size;
3564 value_word = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gpos_device_table, values[index >> (4 - format)]));
3565 mask = 0xffff >> (16 - (1 << format));
3567 value = (value_word >> ((index % (4 - format)) * (1 << format))) & mask;
3569 if ((unsigned int)value >= ((mask + 1) >> 1))
3570 value -= mask + 1;
3572 return value;
3575 static void opentype_layout_apply_gpos_value(struct scriptshaping_context *context, unsigned int table_offset,
3576 WORD value_format, const WORD *values, unsigned int glyph)
3578 const struct scriptshaping_cache *cache = context->cache;
3579 DWRITE_GLYPH_OFFSET *offset = &context->offsets[glyph];
3580 float *advance = &context->advances[glyph];
3582 if (!value_format)
3583 return;
3585 if (value_format & GPOS_VALUE_X_PLACEMENT)
3587 offset->advanceOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3588 values++;
3590 if (value_format & GPOS_VALUE_Y_PLACEMENT)
3592 offset->ascenderOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3593 values++;
3595 if (value_format & GPOS_VALUE_X_ADVANCE)
3597 *advance += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
3598 values++;
3600 if (value_format & GPOS_VALUE_Y_ADVANCE)
3602 values++;
3604 if (value_format & GPOS_VALUE_X_PLACEMENT_DEVICE)
3606 offset->advanceOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3607 values++;
3609 if (value_format & GPOS_VALUE_Y_PLACEMENT_DEVICE)
3611 offset->ascenderOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3612 values++;
3614 if (value_format & GPOS_VALUE_X_ADVANCE_DEVICE)
3616 *advance += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
3617 values++;
3619 if (value_format & GPOS_VALUE_Y_ADVANCE_DEVICE)
3621 values++;
3625 struct lookup
3627 unsigned short index;
3628 unsigned short type;
3629 unsigned short subtable_count;
3631 unsigned int mask;
3632 unsigned int flags;
3633 unsigned int offset;
3634 unsigned int auto_zwnj : 1;
3635 unsigned int auto_zwj : 1;
3638 static unsigned int opentype_layout_is_subst_context(const struct scriptshaping_context *context)
3640 return context->table == &context->cache->gsub;
3643 static unsigned int opentype_layout_is_pos_context(const struct scriptshaping_context *context)
3645 return context->table == &context->cache->gpos;
3648 static unsigned int opentype_layout_get_gsubgpos_subtable(const struct scriptshaping_context *context,
3649 const struct lookup *lookup, unsigned int subtable, unsigned int *lookup_type)
3651 unsigned int subtable_offset = table_read_be_word(&context->table->table, lookup->offset +
3652 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
3653 const struct ot_gsubgpos_extension_format1 *format1;
3655 subtable_offset += lookup->offset;
3657 if ((opentype_layout_is_subst_context(context) && lookup->type != GSUB_LOOKUP_EXTENSION_SUBST) ||
3658 (opentype_layout_is_pos_context(context) && lookup->type != GPOS_LOOKUP_EXTENSION_POSITION))
3660 *lookup_type = lookup->type;
3661 return subtable_offset;
3664 *lookup_type = 0;
3666 if (!(format1 = table_read_ensure(&context->table->table, subtable_offset, sizeof(*format1))))
3667 return 0;
3669 if (GET_BE_WORD(format1->format) != 1)
3671 WARN("Unexpected extension table format %#x.\n", format1->format);
3672 return 0;
3675 *lookup_type = GET_BE_WORD(format1->lookup_type);
3676 return subtable_offset + GET_BE_DWORD(format1->extension_offset);
3679 struct ot_lookup
3681 unsigned int offset;
3682 unsigned int subtable_count;
3683 unsigned int flags;
3686 enum iterator_match
3688 /* First two to fit matching callback result. */
3689 ITER_NO = 0,
3690 ITER_YES = 1,
3691 ITER_MAYBE,
3694 struct match_context;
3695 struct match_data
3697 const struct match_context *mc;
3698 unsigned int subtable_offset;
3701 typedef BOOL (*p_match_func)(UINT16 glyph, UINT16 glyph_data, const struct match_data *match_data);
3703 struct match_context
3705 struct scriptshaping_context *context;
3706 unsigned int backtrack_offset;
3707 unsigned int input_offset;
3708 unsigned int lookahead_offset;
3709 p_match_func match_func;
3710 const struct lookup *lookup;
3713 struct glyph_iterator
3715 struct scriptshaping_context *context;
3716 unsigned int flags;
3717 unsigned int pos;
3718 unsigned int len;
3719 unsigned int mask;
3720 p_match_func match_func;
3721 const UINT16 *glyph_data;
3722 const struct match_data *match_data;
3723 unsigned int ignore_zwnj;
3724 unsigned int ignore_zwj;
3727 static void glyph_iterator_init(struct scriptshaping_context *context, unsigned int flags, unsigned int pos,
3728 unsigned int len, struct glyph_iterator *iter)
3730 iter->context = context;
3731 iter->flags = flags;
3732 iter->pos = pos;
3733 iter->len = len;
3734 iter->mask = ~0u;
3735 iter->match_func = NULL;
3736 iter->match_data = NULL;
3737 iter->glyph_data = NULL;
3738 /* Context matching iterators will get these fixed up. */
3739 iter->ignore_zwnj = !!opentype_layout_is_pos_context(context);
3740 iter->ignore_zwj = context->auto_zwj;
3743 struct ot_gdef_mark_glyph_sets
3745 UINT16 format;
3746 UINT16 count;
3747 DWORD offsets[1];
3750 static BOOL opentype_match_glyph_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3752 return glyph == glyph_data;
3755 static BOOL opentype_match_class_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3757 const struct match_context *mc = data->mc;
3758 UINT16 glyph_class = opentype_layout_get_glyph_class(&mc->context->table->table, data->subtable_offset, glyph);
3759 return glyph_class == glyph_data;
3762 static BOOL opentype_match_coverage_func(UINT16 glyph, UINT16 glyph_data, const struct match_data *data)
3764 const struct match_context *mc = data->mc;
3765 return opentype_layout_is_glyph_covered(&mc->context->table->table, data->subtable_offset + glyph_data, glyph)
3766 != GLYPH_NOT_COVERED;
3769 static BOOL opentype_layout_mark_set_covers(const struct scriptshaping_cache *cache, unsigned int set_index,
3770 UINT16 glyph)
3772 unsigned int format, offset = cache->gdef.markglyphsetdef, coverage_offset, count;
3774 if (!offset)
3775 return FALSE;
3777 format = table_read_be_word(&cache->gdef.table, offset);
3779 if (format == 1)
3781 count = table_read_be_word(&cache->gdef.table, offset + FIELD_OFFSET(struct ot_gdef_markglyphsets, count));
3782 if (!count || set_index >= count)
3783 return FALSE;
3785 coverage_offset = table_read_be_dword(&cache->gdef.table, offset +
3786 FIELD_OFFSET(struct ot_gdef_markglyphsets, offsets[set_index]));
3787 return opentype_layout_is_glyph_covered(&cache->gdef.table, offset + coverage_offset, glyph) != GLYPH_NOT_COVERED;
3789 else
3790 WARN("Unexpected MarkGlyphSets format %#x.\n", format);
3792 return FALSE;
3795 static BOOL lookup_is_glyph_match(const struct scriptshaping_context *context, unsigned int idx, unsigned int match_props)
3797 unsigned int glyph_props = context->glyph_infos[idx].props;
3798 UINT16 glyph = context->u.buffer.glyphs[idx];
3800 if (glyph_props & match_props & LOOKUP_FLAG_IGNORE_MASK)
3801 return FALSE;
3803 if (!(glyph_props & GLYPH_PROP_MARK))
3804 return TRUE;
3806 if (match_props & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
3807 return opentype_layout_mark_set_covers(context->cache, match_props >> 16, glyph);
3809 if (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE)
3810 return (match_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE) == (glyph_props & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE);
3812 return TRUE;
3815 static enum iterator_match glyph_iterator_may_skip(const struct glyph_iterator *iter)
3817 unsigned int glyph_props = iter->context->glyph_infos[iter->pos].props & (GLYPH_PROP_IGNORABLE | GLYPH_PROP_HIDDEN);
3819 if (!lookup_is_glyph_match(iter->context, iter->pos, iter->flags))
3820 return ITER_YES;
3822 if (glyph_props == GLYPH_PROP_IGNORABLE && !iter->context->u.buffer.glyph_props[iter->pos].components &&
3823 (iter->ignore_zwnj || !(iter->context->glyph_infos[iter->pos].props & GLYPH_PROP_ZWNJ)) &&
3824 (iter->ignore_zwj || !(iter->context->glyph_infos[iter->pos].props & GLYPH_PROP_ZWJ)))
3826 return ITER_MAYBE;
3829 return ITER_NO;
3832 static enum iterator_match glyph_iterator_may_match(const struct glyph_iterator *iter)
3834 if (!(iter->mask & iter->context->glyph_infos[iter->pos].mask))
3835 return ITER_NO;
3837 /* Glyph data is used for input, backtrack, and lookahead arrays, swap it here instead of doing that
3838 in all matching functions. */
3839 if (iter->match_func)
3840 return !!iter->match_func(iter->context->u.buffer.glyphs[iter->pos], GET_BE_WORD(*iter->glyph_data), iter->match_data);
3842 return ITER_MAYBE;
3845 static BOOL glyph_iterator_next(struct glyph_iterator *iter)
3847 enum iterator_match skip, match;
3849 while (iter->pos + iter->len < iter->context->glyph_count)
3851 ++iter->pos;
3853 skip = glyph_iterator_may_skip(iter);
3854 if (skip == ITER_YES)
3855 continue;
3857 match = glyph_iterator_may_match(iter);
3858 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3860 --iter->len;
3861 if (iter->glyph_data)
3862 ++iter->glyph_data;
3863 return TRUE;
3866 if (skip == ITER_NO)
3867 return FALSE;
3870 return FALSE;
3873 static BOOL glyph_iterator_prev(struct glyph_iterator *iter)
3875 enum iterator_match skip, match;
3877 while (iter->pos > iter->len - 1)
3879 --iter->pos;
3881 skip = glyph_iterator_may_skip(iter);
3882 if (skip == ITER_YES)
3883 continue;
3885 match = glyph_iterator_may_match(iter);
3886 if (match == ITER_YES || (match == ITER_MAYBE && skip == ITER_NO))
3888 --iter->len;
3889 if (iter->glyph_data)
3890 ++iter->glyph_data;
3891 return TRUE;
3894 if (skip == ITER_NO)
3895 return FALSE;
3898 return FALSE;
3901 static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context,
3902 const struct lookup *lookup, unsigned int subtable_offset)
3904 const struct dwrite_fonttable *table = &context->table->table;
3905 UINT16 format, value_format, value_len, coverage, glyph;
3907 unsigned int coverage_index;
3909 format = table_read_be_word(table, subtable_offset);
3911 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage));
3912 value_format = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format));
3913 value_len = dwrite_popcount(value_format);
3915 glyph = context->u.pos.glyphs[context->cur];
3917 if (format == 1)
3919 const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(table, subtable_offset,
3920 FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len]));
3922 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3923 if (coverage_index == GLYPH_NOT_COVERED)
3924 return FALSE;
3926 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, context->cur);
3928 else if (format == 2)
3930 WORD value_count = table_read_be_word(table, subtable_offset +
3931 FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count));
3932 const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(table, subtable_offset,
3933 FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD));
3935 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
3936 if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count)
3937 return FALSE;
3939 opentype_layout_apply_gpos_value(context, subtable_offset, value_format, &format2->values[coverage_index * value_len],
3940 context->cur);
3942 else
3944 WARN("Unknown single adjustment format %u.\n", format);
3945 return FALSE;
3948 context->cur++;
3950 return TRUE;
3953 static int __cdecl gpos_pair_adjustment_compare_format1(const void *g, const void *r)
3955 const struct ot_gpos_pairvalue *pairvalue = r;
3956 UINT16 second_glyph = GET_BE_WORD(pairvalue->second_glyph);
3957 return *(UINT16 *)g - second_glyph;
3960 static BOOL opentype_layout_apply_gpos_pair_adjustment(struct scriptshaping_context *context,
3961 const struct lookup *lookup, unsigned int subtable_offset)
3963 const struct dwrite_fonttable *table = &context->table->table;
3964 unsigned int first_glyph, second_glyph;
3965 struct glyph_iterator iter_pair;
3966 WORD format, coverage;
3968 WORD value_format1, value_format2, value_len1, value_len2;
3969 unsigned int coverage_index;
3971 glyph_iterator_init(context, lookup->flags, context->cur, 1, &iter_pair);
3972 if (!glyph_iterator_next(&iter_pair))
3973 return FALSE;
3975 if (context->is_rtl)
3977 first_glyph = iter_pair.pos;
3978 second_glyph = context->cur;
3980 else
3982 first_glyph = context->cur;
3983 second_glyph = iter_pair.pos;
3986 format = table_read_be_word(table, subtable_offset);
3988 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1, coverage));
3989 if (!coverage)
3990 return FALSE;
3992 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, context->u.pos.glyphs[first_glyph]);
3993 if (coverage_index == GLYPH_NOT_COVERED)
3994 return FALSE;
3996 if (format == 1)
3998 const struct ot_gpos_pairpos_format1 *format1;
3999 WORD pairset_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format1,
4000 pairset_count));
4001 unsigned int pairvalue_len, pairset_offset;
4002 const struct ot_gpos_pairset *pairset;
4003 const WORD *pairvalue;
4004 WORD pairvalue_count;
4006 if (!pairset_count || coverage_index >= pairset_count)
4007 return FALSE;
4009 format1 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format1, pairsets[pairset_count]));
4010 if (!format1)
4011 return FALSE;
4013 /* Ordered paired values. */
4014 pairvalue_count = table_read_be_word(table, subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]));
4015 if (!pairvalue_count)
4016 return FALSE;
4018 /* Structure length is variable, but does not change across the subtable. */
4019 value_format1 = GET_BE_WORD(format1->value_format1) & 0xff;
4020 value_format2 = GET_BE_WORD(format1->value_format2) & 0xff;
4022 value_len1 = dwrite_popcount(value_format1);
4023 value_len2 = dwrite_popcount(value_format2);
4024 pairvalue_len = FIELD_OFFSET(struct ot_gpos_pairvalue, data) + value_len1 * sizeof(WORD) +
4025 value_len2 * sizeof(WORD);
4027 pairset_offset = subtable_offset + GET_BE_WORD(format1->pairsets[coverage_index]);
4028 pairset = table_read_ensure(table, pairset_offset, pairvalue_len * pairvalue_count);
4029 if (!pairset)
4030 return FALSE;
4032 pairvalue = bsearch(&context->u.pos.glyphs[second_glyph], pairset->pairvalues, pairvalue_count,
4033 pairvalue_len, gpos_pair_adjustment_compare_format1);
4034 if (!pairvalue)
4035 return FALSE;
4037 pairvalue += 1; /* Skip SecondGlyph. */
4038 opentype_layout_apply_gpos_value(context, pairset_offset, value_format1, pairvalue, first_glyph);
4039 opentype_layout_apply_gpos_value(context, pairset_offset, value_format2, pairvalue + value_len1,
4040 second_glyph);
4042 context->cur = iter_pair.pos;
4043 if (value_len2)
4044 context->cur++;
4046 else if (format == 2)
4048 const struct ot_gpos_pairpos_format2 *format2;
4049 WORD class1_count, class2_count;
4050 unsigned int class1, class2;
4051 const WCHAR *values;
4053 value_format1 = table_read_be_word(table, subtable_offset +
4054 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format1)) & 0xff;
4055 value_format2 = table_read_be_word(table, subtable_offset +
4056 FIELD_OFFSET(struct ot_gpos_pairpos_format2, value_format2)) & 0xff;
4058 class1_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class1_count));
4059 class2_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_pairpos_format2, class2_count));
4061 value_len1 = dwrite_popcount(value_format1);
4062 value_len2 = dwrite_popcount(value_format2);
4064 format2 = table_read_ensure(table, subtable_offset, FIELD_OFFSET(struct ot_gpos_pairpos_format2,
4065 values[class1_count * class2_count * (value_len1 + value_len2)]));
4066 if (!format2)
4067 return FALSE;
4069 class1 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def1),
4070 context->u.pos.glyphs[first_glyph]);
4071 class2 = opentype_layout_get_glyph_class(table, subtable_offset + GET_BE_WORD(format2->class_def2),
4072 context->u.pos.glyphs[second_glyph]);
4074 if (!(class1 < class1_count && class2 < class2_count))
4075 return FALSE;
4077 values = &format2->values[(class1 * class2_count + class2) * (value_len1 + value_len2)];
4078 opentype_layout_apply_gpos_value(context, subtable_offset, value_format1, values, first_glyph);
4079 opentype_layout_apply_gpos_value(context, subtable_offset, value_format2, values + value_len1,
4080 second_glyph);
4082 context->cur = iter_pair.pos;
4083 if (value_len2)
4084 context->cur++;
4086 else
4088 WARN("Unknown pair adjustment format %u.\n", format);
4089 return FALSE;
4092 return TRUE;
4095 static void opentype_layout_gpos_get_anchor(const struct scriptshaping_context *context, unsigned int anchor_offset,
4096 unsigned int glyph_index, float *x, float *y)
4098 const struct scriptshaping_cache *cache = context->cache;
4099 const struct dwrite_fonttable *table = &context->table->table;
4101 WORD format = table_read_be_word(table, anchor_offset);
4103 *x = *y = 0.0f;
4105 if (format == 1)
4107 const struct ot_gpos_anchor_format1 *format1 = table_read_ensure(table, anchor_offset, sizeof(*format1));
4109 if (format1)
4111 *x = opentype_scale_gpos_be_value(format1->x_coord, context->emsize, cache->upem);
4112 *y = opentype_scale_gpos_be_value(format1->y_coord, context->emsize, cache->upem);
4115 else if (format == 2)
4117 const struct ot_gpos_anchor_format2 *format2 = table_read_ensure(table, anchor_offset, sizeof(*format2));
4119 if (format2)
4121 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
4122 FIXME("Use outline anchor point for glyph %u.\n", context->u.pos.glyphs[glyph_index]);
4124 *x = opentype_scale_gpos_be_value(format2->x_coord, context->emsize, cache->upem);
4125 *y = opentype_scale_gpos_be_value(format2->y_coord, context->emsize, cache->upem);
4128 else if (format == 3)
4130 const struct ot_gpos_anchor_format3 *format3 = table_read_ensure(table, anchor_offset, sizeof(*format3));
4132 if (format3)
4134 *x = opentype_scale_gpos_be_value(format3->x_coord, context->emsize, cache->upem);
4135 *y = opentype_scale_gpos_be_value(format3->y_coord, context->emsize, cache->upem);
4137 if (context->measuring_mode != DWRITE_MEASURING_MODE_NATURAL)
4139 if (format3->x_dev_offset)
4140 *x += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->x_dev_offset));
4141 if (format3->y_dev_offset)
4142 *y += opentype_layout_gpos_get_dev_value(context, anchor_offset + GET_BE_WORD(format3->y_dev_offset));
4146 else
4147 WARN("Unknown anchor format %u.\n", format);
4150 static void opentype_set_glyph_attach_type(struct scriptshaping_context *context, unsigned int idx,
4151 enum attach_type attach_type)
4153 context->glyph_infos[idx].props &= ~GLYPH_PROP_ATTACH_TYPE_MASK;
4154 context->glyph_infos[idx].props |= attach_type << 16;
4157 static enum attach_type opentype_get_glyph_attach_type(const struct scriptshaping_context *context, unsigned int idx)
4159 return (context->glyph_infos[idx].props >> 16) & 0xff;
4162 static void opentype_reverse_cursive_offset(struct scriptshaping_context *context, unsigned int i,
4163 unsigned int new_parent)
4165 enum attach_type type = opentype_get_glyph_attach_type(context, i);
4166 int chain = context->glyph_infos[i].attach_chain;
4167 unsigned int j;
4169 if (!chain || type != GLYPH_ATTACH_CURSIVE)
4170 return;
4172 context->glyph_infos[i].attach_chain = 0;
4174 j = (int)i + chain;
4175 if (j == new_parent)
4176 return;
4178 opentype_reverse_cursive_offset(context, j, new_parent);
4180 /* FIXME: handle vertical flow direction */
4181 context->offsets[j].ascenderOffset = -context->offsets[i].ascenderOffset;
4183 context->glyph_infos[j].attach_chain = -chain;
4184 opentype_set_glyph_attach_type(context, j, type);
4187 static BOOL opentype_layout_apply_gpos_cursive_attachment(struct scriptshaping_context *context,
4188 const struct lookup *lookup, unsigned int subtable_offset)
4190 const struct dwrite_fonttable *table = &context->table->table;
4191 UINT16 format, glyph;
4193 format = table_read_be_word(table, subtable_offset);
4194 glyph = context->u.pos.glyphs[context->cur];
4196 if (format == 1)
4198 WORD coverage_offset = table_read_be_word(table, subtable_offset +
4199 FIELD_OFFSET(struct ot_gpos_cursive_format1, coverage));
4200 unsigned int glyph_index, entry_count, entry_anchor, exit_anchor, child, parent;
4201 float entry_x, entry_y, exit_x, exit_y, delta;
4202 struct glyph_iterator prev_iter;
4203 float y_offset;
4205 if (!coverage_offset)
4206 return FALSE;
4208 entry_count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gpos_cursive_format1, count));
4210 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset, glyph);
4211 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4212 return FALSE;
4214 entry_anchor = table_read_be_word(table, subtable_offset +
4215 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2]));
4216 if (!entry_anchor)
4217 return FALSE;
4219 glyph_iterator_init(context, lookup->flags, context->cur, 1, &prev_iter);
4220 if (!glyph_iterator_prev(&prev_iter))
4221 return FALSE;
4223 glyph_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage_offset,
4224 context->u.pos.glyphs[prev_iter.pos]);
4225 if (glyph_index == GLYPH_NOT_COVERED || glyph_index >= entry_count)
4226 return FALSE;
4228 exit_anchor = table_read_be_word(table, subtable_offset +
4229 FIELD_OFFSET(struct ot_gpos_cursive_format1, anchors[glyph_index * 2 + 1]));
4230 if (!exit_anchor)
4231 return FALSE;
4233 opentype_layout_gpos_get_anchor(context, subtable_offset + exit_anchor, prev_iter.pos, &exit_x, &exit_y);
4234 opentype_layout_gpos_get_anchor(context, subtable_offset + entry_anchor, context->cur, &entry_x, &entry_y);
4236 if (context->is_rtl)
4238 delta = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4239 context->advances[prev_iter.pos] -= delta;
4240 context->advances[context->cur] = entry_x + context->offsets[context->cur].advanceOffset;
4241 context->offsets[prev_iter.pos].advanceOffset -= delta;
4243 else
4245 delta = entry_x + context->offsets[context->cur].advanceOffset;
4246 context->advances[prev_iter.pos] = exit_x + context->offsets[prev_iter.pos].advanceOffset;
4247 context->advances[context->cur] -= delta;
4248 context->offsets[context->cur].advanceOffset -= delta;
4251 if (lookup->flags & LOOKUP_FLAG_RTL)
4253 y_offset = entry_y - exit_y;
4254 child = prev_iter.pos;
4255 parent = context->cur;
4257 else
4259 y_offset = exit_y - entry_y;
4260 child = context->cur;
4261 parent = prev_iter.pos;
4264 opentype_reverse_cursive_offset(context, child, parent);
4266 context->offsets[child].ascenderOffset = y_offset;
4268 opentype_set_glyph_attach_type(context, child, GLYPH_ATTACH_CURSIVE);
4269 context->glyph_infos[child].attach_chain = (int)parent - (int)child;
4270 context->has_gpos_attachment = 1;
4272 if (context->glyph_infos[parent].attach_chain == -context->glyph_infos[child].attach_chain)
4273 context->glyph_infos[parent].attach_chain = 0;
4275 context->cur++;
4277 else
4279 WARN("Unknown cursive attachment format %u.\n", format);
4280 return FALSE;
4283 return TRUE;
4286 static BOOL opentype_layout_apply_mark_array(struct scriptshaping_context *context, unsigned int subtable_offset,
4287 unsigned int mark_array, unsigned int mark_index, unsigned int glyph_index, unsigned int anchors_matrix,
4288 unsigned int class_count, unsigned int glyph_pos)
4290 const struct dwrite_fonttable *table = &context->table->table;
4291 unsigned int mark_class, mark_count, glyph_count;
4292 const struct ot_gpos_mark_record *record;
4293 float mark_x, mark_y, base_x, base_y;
4294 const UINT16 *anchors;
4296 mark_count = table_read_be_word(table, subtable_offset + mark_array);
4297 if (mark_index >= mark_count) return FALSE;
4299 if (!(record = table_read_ensure(table, subtable_offset + mark_array +
4300 FIELD_OFFSET(struct ot_gpos_mark_array, records[mark_index]), sizeof(*record))))
4302 return FALSE;
4305 mark_class = GET_BE_WORD(record->mark_class);
4306 if (mark_class >= class_count) return FALSE;
4308 glyph_count = table_read_be_word(table, subtable_offset + anchors_matrix);
4309 if (glyph_index >= glyph_count) return FALSE;
4311 /* Anchors data is stored as two dimensional array [glyph_count][class_count], starting with row count field. */
4312 anchors = table_read_ensure(table, subtable_offset + anchors_matrix + 2, glyph_count * class_count * sizeof(*anchors));
4313 if (!anchors) return FALSE;
4315 opentype_layout_gpos_get_anchor(context, subtable_offset + mark_array + GET_BE_WORD(record->mark_anchor),
4316 context->cur, &mark_x, &mark_y);
4317 opentype_layout_gpos_get_anchor(context, subtable_offset + anchors_matrix +
4318 GET_BE_WORD(anchors[glyph_index * class_count + mark_class]), glyph_pos, &base_x, &base_y);
4320 if (context->is_rtl)
4321 context->offsets[context->cur].advanceOffset = mark_x - base_x;
4322 else
4323 context->offsets[context->cur].advanceOffset = base_x - mark_x;
4324 context->offsets[context->cur].ascenderOffset = base_y - mark_y;
4325 opentype_set_glyph_attach_type(context, context->cur, GLYPH_ATTACH_MARK);
4326 context->glyph_infos[context->cur].attach_chain = (int)glyph_pos - (int)context->cur;
4327 context->has_gpos_attachment = 1;
4329 context->cur++;
4331 return TRUE;
4334 static BOOL opentype_layout_apply_gpos_mark_to_base_attachment(struct scriptshaping_context *context,
4335 const struct lookup *lookup, unsigned int subtable_offset)
4337 const struct dwrite_fonttable *table = &context->table->table;
4338 WORD format;
4340 format = table_read_be_word(table, subtable_offset);
4342 if (format == 1)
4344 const struct ot_gpos_mark_to_base_format1 *format1;
4345 unsigned int base_index, mark_index;
4346 struct glyph_iterator base_iter;
4348 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4350 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4351 context->u.pos.glyphs[context->cur]);
4352 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4354 /* Look back for first base glyph. */
4355 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &base_iter);
4356 if (!glyph_iterator_prev(&base_iter))
4357 return FALSE;
4359 base_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->base_coverage),
4360 context->u.pos.glyphs[base_iter.pos]);
4361 if (base_index == GLYPH_NOT_COVERED) return FALSE;
4363 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4364 base_index, GET_BE_WORD(format1->base_array), GET_BE_WORD(format1->mark_class_count), base_iter.pos);
4366 else
4368 WARN("Unknown mark-to-base format %u.\n", format);
4369 return FALSE;
4372 return TRUE;
4375 static const UINT16 * table_read_array_be_word(const struct dwrite_fonttable *table, unsigned int offset,
4376 unsigned int index, UINT16 *data)
4378 unsigned int count = table_read_be_word(table, offset);
4379 const UINT16 *array;
4381 if (index != ~0u && index >= count) return NULL;
4382 if (!(array = table_read_ensure(table, offset + 2, count * sizeof(*array)))) return FALSE;
4383 *data = index == ~0u ? count : GET_BE_WORD(array[index]);
4384 return array;
4387 static BOOL opentype_layout_apply_gpos_mark_to_lig_attachment(struct scriptshaping_context *context,
4388 const struct lookup *lookup, unsigned int subtable_offset)
4390 const struct dwrite_fonttable *table = &context->table->table;
4391 WORD format;
4393 format = table_read_be_word(table, subtable_offset);
4395 if (format == 1)
4397 unsigned int mark_index, lig_index, comp_index, class_count, comp_count;
4398 const struct ot_gpos_mark_to_lig_format1 *format1;
4399 struct glyph_iterator lig_iter;
4400 unsigned int lig_array;
4401 UINT16 lig_attach;
4403 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4405 mark_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark_coverage),
4406 context->u.pos.glyphs[context->cur]);
4407 if (mark_index == GLYPH_NOT_COVERED) return FALSE;
4409 glyph_iterator_init(context, LOOKUP_FLAG_IGNORE_MARKS, context->cur, 1, &lig_iter);
4410 if (!glyph_iterator_prev(&lig_iter))
4411 return FALSE;
4413 lig_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->lig_coverage),
4414 context->u.pos.glyphs[lig_iter.pos]);
4415 if (lig_index == GLYPH_NOT_COVERED) return FALSE;
4417 class_count = GET_BE_WORD(format1->mark_class_count);
4419 lig_array = GET_BE_WORD(format1->lig_array);
4421 if (!table_read_array_be_word(table, subtable_offset + lig_array, lig_index, &lig_attach)) return FALSE;
4423 comp_count = table_read_be_word(table, subtable_offset + lig_array + lig_attach);
4424 if (!comp_count) return FALSE;
4426 comp_index = context->u.buffer.glyph_props[lig_iter.pos].components -
4427 context->u.buffer.glyph_props[context->cur].lig_component - 1;
4428 if (comp_index >= comp_count) return FALSE;
4430 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark_array), mark_index,
4431 comp_index, lig_array + lig_attach, class_count, lig_iter.pos);
4433 else
4434 WARN("Unknown mark-to-ligature format %u.\n", format);
4436 return FALSE;
4439 static BOOL opentype_layout_apply_gpos_mark_to_mark_attachment(struct scriptshaping_context *context,
4440 const struct lookup *lookup, unsigned int subtable_offset)
4442 const struct dwrite_fonttable *table = &context->table->table;
4443 WORD format;
4445 format = table_read_be_word(table, subtable_offset);
4447 if (format == 1)
4449 const struct ot_gpos_mark_to_mark_format1 *format1;
4450 unsigned int mark1_index, mark2_index;
4451 struct glyph_iterator mark_iter;
4453 if (!(format1 = table_read_ensure(table, subtable_offset, sizeof(*format1)))) return FALSE;
4455 mark1_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark1_coverage),
4456 context->u.pos.glyphs[context->cur]);
4457 if (mark1_index == GLYPH_NOT_COVERED) return FALSE;
4459 glyph_iterator_init(context, lookup->flags & ~LOOKUP_FLAG_IGNORE_MASK, context->cur, 1, &mark_iter);
4460 if (!glyph_iterator_prev(&mark_iter))
4461 return FALSE;
4463 if (!context->u.pos.glyph_props[mark_iter.pos].isDiacritic)
4464 return FALSE;
4466 mark2_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(format1->mark2_coverage),
4467 context->u.pos.glyphs[mark_iter.pos]);
4468 if (mark2_index == GLYPH_NOT_COVERED) return FALSE;
4470 return opentype_layout_apply_mark_array(context, subtable_offset, GET_BE_WORD(format1->mark1_array), mark1_index,
4471 mark2_index, GET_BE_WORD(format1->mark2_array), GET_BE_WORD(format1->mark_class_count), mark_iter.pos);
4473 else
4475 WARN("Unknown mark-to-mark format %u.\n", format);
4476 return FALSE;
4479 return TRUE;
4482 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
4483 unsigned int subtable_offset);
4484 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
4485 unsigned int subtable_offset);
4487 static BOOL opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
4489 unsigned int i, lookup_type;
4490 BOOL ret = FALSE;
4492 for (i = 0; i < lookup->subtable_count; ++i)
4494 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
4496 switch (lookup_type)
4498 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
4499 ret = opentype_layout_apply_gpos_single_adjustment(context, lookup, subtable_offset);
4500 break;
4501 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
4502 ret = opentype_layout_apply_gpos_pair_adjustment(context, lookup, subtable_offset);
4503 break;
4504 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
4505 ret = opentype_layout_apply_gpos_cursive_attachment(context, lookup, subtable_offset);
4506 break;
4507 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
4508 ret = opentype_layout_apply_gpos_mark_to_base_attachment(context, lookup, subtable_offset);
4509 break;
4510 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
4511 ret = opentype_layout_apply_gpos_mark_to_lig_attachment(context, lookup, subtable_offset);
4512 break;
4513 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
4514 ret = opentype_layout_apply_gpos_mark_to_mark_attachment(context, lookup, subtable_offset);
4515 break;
4516 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
4517 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
4518 break;
4519 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
4520 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
4521 break;
4522 case GPOS_LOOKUP_EXTENSION_POSITION:
4523 WARN("Recursive extension lookup.\n");
4524 break;
4525 default:
4526 WARN("Unknown lookup type %u.\n", lookup_type);
4529 if (ret)
4530 break;
4533 return ret;
4536 struct lookups
4538 struct lookup *lookups;
4539 size_t capacity;
4540 size_t count;
4543 static int __cdecl lookups_sorting_compare(const void *a, const void *b)
4545 const struct lookup *left = (const struct lookup *)a;
4546 const struct lookup *right = (const struct lookup *)b;
4547 return left->index < right->index ? -1 : left->index > right->index ? 1 : 0;
4550 static BOOL opentype_layout_init_lookup(const struct ot_gsubgpos_table *table, unsigned short lookup_index,
4551 const struct shaping_feature *feature, struct lookup *lookup)
4553 unsigned short subtable_count, lookup_type, mark_filtering_set;
4554 const struct ot_lookup_table *lookup_table;
4555 unsigned int offset, flags;
4557 if (!(offset = table_read_be_word(&table->table, table->lookup_list +
4558 FIELD_OFFSET(struct ot_lookup_list, lookup[lookup_index]))))
4560 return FALSE;
4563 offset += table->lookup_list;
4565 if (!(lookup_table = table_read_ensure(&table->table, offset, sizeof(*lookup_table))))
4566 return FALSE;
4568 if (!(subtable_count = GET_BE_WORD(lookup_table->subtable_count)))
4569 return FALSE;
4571 lookup_type = GET_BE_WORD(lookup_table->lookup_type);
4572 flags = GET_BE_WORD(lookup_table->flags);
4574 if (flags & LOOKUP_FLAG_USE_MARK_FILTERING_SET)
4576 mark_filtering_set = table_read_be_word(&table->table, offset +
4577 FIELD_OFFSET(struct ot_lookup_table, subtable[subtable_count]));
4578 flags |= mark_filtering_set << 16;
4581 lookup->index = lookup_index;
4582 lookup->type = lookup_type;
4583 lookup->flags = flags;
4584 lookup->subtable_count = subtable_count;
4585 lookup->offset = offset;
4586 if (feature)
4588 lookup->mask = feature->mask;
4589 lookup->auto_zwnj = !(feature->flags & FEATURE_MANUAL_ZWNJ);
4590 lookup->auto_zwj = !(feature->flags & FEATURE_MANUAL_ZWJ);
4593 return TRUE;
4596 static void opentype_layout_add_lookups(const struct ot_feature_list *feature_list, UINT16 total_lookup_count,
4597 const struct ot_gsubgpos_table *table, struct shaping_feature *feature, struct lookups *lookups)
4599 UINT16 feature_offset, lookup_count;
4600 unsigned int i;
4602 /* Feature wasn't found */
4603 if (feature->index == 0xffff)
4604 return;
4606 feature_offset = GET_BE_WORD(feature_list->features[feature->index].offset);
4608 lookup_count = table_read_be_word(&table->table, table->feature_list + feature_offset +
4609 FIELD_OFFSET(struct ot_feature, lookup_count));
4610 if (!lookup_count)
4611 return;
4613 if (!dwrite_array_reserve((void **)&lookups->lookups, &lookups->capacity, lookups->count + lookup_count,
4614 sizeof(*lookups->lookups)))
4616 return;
4619 for (i = 0; i < lookup_count; ++i)
4621 UINT16 lookup_index = table_read_be_word(&table->table, table->feature_list + feature_offset +
4622 FIELD_OFFSET(struct ot_feature, lookuplist_index[i]));
4624 if (lookup_index >= total_lookup_count)
4625 continue;
4627 if (opentype_layout_init_lookup(table, lookup_index, feature, &lookups->lookups[lookups->count]))
4628 lookups->count++;
4632 static void opentype_layout_collect_lookups(struct scriptshaping_context *context, unsigned int script_index,
4633 unsigned int language_index, struct shaping_features *features, const struct ot_gsubgpos_table *table,
4634 struct lookups *lookups)
4636 unsigned int last_num_lookups = 0, stage, script_feature_count = 0;
4637 UINT16 total_feature_count, total_lookup_count;
4638 struct shaping_feature required_feature = { 0 };
4639 const struct ot_feature_list *feature_list;
4640 const struct ot_langsys *langsys = NULL;
4641 struct shaping_feature *feature;
4642 unsigned int i, j, next_bit;
4643 unsigned int global_bit_shift = 1;
4644 unsigned int global_bit_mask = 2;
4645 UINT16 feature_index;
4647 if (!table->table.data)
4648 return;
4650 if (script_index != ~0u)
4652 unsigned int table_offset, langsys_offset;
4654 /* ScriptTable offset. */
4655 table_offset = table_read_be_word(&table->table, table->script_list + FIELD_OFFSET(struct ot_script_list, scripts) +
4656 script_index * sizeof(struct ot_script_record) + FIELD_OFFSET(struct ot_script_record, script));
4657 if (!table_offset)
4658 return;
4660 if (language_index == ~0u)
4661 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset);
4662 else
4663 langsys_offset = table_read_be_word(&table->table, table->script_list + table_offset +
4664 FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) +
4665 FIELD_OFFSET(struct ot_langsys_record, langsys));
4666 langsys_offset += table->script_list + table_offset;
4668 script_feature_count = table_read_be_word(&table->table, langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count));
4669 if (script_feature_count)
4670 langsys = table_read_ensure(&table->table, langsys_offset,
4671 FIELD_OFFSET(struct ot_langsys, feature_index[script_feature_count]));
4672 if (!langsys)
4673 script_feature_count = 0;
4676 total_feature_count = table_read_be_word(&table->table, table->feature_list);
4677 if (!total_feature_count)
4678 return;
4680 total_lookup_count = table_read_be_word(&table->table, table->lookup_list);
4681 if (!total_lookup_count)
4682 return;
4684 feature_list = table_read_ensure(&table->table, table->feature_list,
4685 FIELD_OFFSET(struct ot_feature_list, features[total_feature_count]));
4686 if (!feature_list)
4687 return;
4689 /* Required feature. */
4690 required_feature.index = langsys ? GET_BE_WORD(langsys->required_feature_index) : 0xffff;
4691 if (required_feature.index < total_feature_count)
4692 required_feature.tag = feature_list->features[required_feature.index].tag;
4693 required_feature.mask = global_bit_mask;
4695 context->global_mask = global_bit_mask;
4696 next_bit = global_bit_shift + 1;
4697 for (i = 0; i < features->count; ++i)
4699 BOOL found = FALSE;
4700 DWORD bits_needed;
4702 feature = &features->features[i];
4704 feature->index = 0xffff;
4706 if ((feature->flags & FEATURE_GLOBAL) && feature->max_value == 1)
4707 bits_needed = 0;
4708 else
4710 BitScanReverse(&bits_needed, min(feature->max_value, 256));
4711 bits_needed++;
4714 if (!feature->max_value || next_bit + bits_needed > 8 * sizeof (feature->mask))
4715 continue;
4717 if (required_feature.tag == feature->tag)
4718 required_feature.stage = feature->stage;
4720 for (j = 0; j < script_feature_count; ++j)
4722 feature_index = GET_BE_WORD(langsys->feature_index[j]);
4723 if (feature_index >= total_feature_count)
4724 continue;
4725 if ((found = feature_list->features[feature_index].tag == feature->tag))
4727 feature->index = feature_index;
4728 break;
4732 if (!found && (features->features[i].flags & FEATURE_GLOBAL_SEARCH))
4734 for (j = 0; j < total_feature_count; ++j)
4736 if ((found = (feature_list->features[j].tag == feature->tag)))
4738 feature->index = j;
4739 break;
4744 if (!found && !(features->features[i].flags & FEATURE_HAS_FALLBACK))
4745 continue;
4747 if (feature->flags & FEATURE_GLOBAL && feature->max_value == 1)
4749 feature->shift = global_bit_shift;
4750 feature->mask = global_bit_mask;
4752 else
4754 feature->shift = next_bit;
4755 feature->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
4756 next_bit += bits_needed;
4757 context->global_mask |= (feature->default_value << feature->shift) & feature->mask;
4759 if (!found)
4760 feature->flags |= FEATURE_NEEDS_FALLBACK;
4763 for (stage = 0; stage <= features->stage; ++stage)
4765 if (required_feature.index != 0xffff && required_feature.stage == stage)
4766 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &required_feature, lookups);
4768 for (i = 0; i < features->count; ++i)
4770 if (features->features[i].stage == stage)
4771 opentype_layout_add_lookups(feature_list, total_lookup_count, table, &features->features[i], lookups);
4774 /* Sort and merge lookups for current stage. */
4775 if (last_num_lookups < lookups->count)
4777 qsort(lookups->lookups + last_num_lookups, lookups->count - last_num_lookups, sizeof(*lookups->lookups),
4778 lookups_sorting_compare);
4780 j = last_num_lookups;
4781 for (i = j + 1; i < lookups->count; ++i)
4783 if (lookups->lookups[i].index != lookups->lookups[j].index)
4785 lookups->lookups[++j] = lookups->lookups[i];
4787 else
4789 lookups->lookups[j].mask |= lookups->lookups[i].mask;
4790 lookups->lookups[j].auto_zwnj &= lookups->lookups[i].auto_zwnj;
4791 lookups->lookups[j].auto_zwj &= lookups->lookups[i].auto_zwj;
4794 lookups->count = j + 1;
4797 last_num_lookups = lookups->count;
4798 features->stages[stage].last_lookup = last_num_lookups;
4802 static int __cdecl feature_search_compare(const void *a, const void* b)
4804 unsigned int tag = *(unsigned int *)a;
4805 const struct shaping_feature *feature = b;
4807 return tag < feature->tag ? -1 : tag > feature->tag ? 1 : 0;
4810 static unsigned int shaping_features_get_mask(const struct shaping_features *features, unsigned int tag, unsigned int *shift)
4812 struct shaping_feature *feature;
4814 feature = bsearch(&tag, features->features, features->count, sizeof(*features->features), feature_search_compare);
4816 if (!feature || feature->index == 0xffff)
4817 return 0;
4819 if (shift) *shift = feature->shift;
4820 return feature->mask;
4823 unsigned int shape_get_feature_1_mask(const struct shaping_features *features, unsigned int tag)
4825 unsigned int shift, mask = shaping_features_get_mask(features, tag, &shift);
4826 return (1 << shift) & mask;
4829 static void opentype_layout_get_glyph_range_for_text(struct scriptshaping_context *context, unsigned int start_char,
4830 unsigned int end_char, unsigned int *start_glyph, unsigned int *end_glyph)
4832 *start_glyph = context->u.buffer.clustermap[start_char];
4833 if (end_char >= context->length - 1)
4834 *end_glyph = context->glyph_count - 1;
4835 else
4836 *end_glyph = context->u.buffer.clustermap[end_char] - 1;
4839 static void opentype_layout_set_glyph_masks(struct scriptshaping_context *context, const struct shaping_features *features)
4841 const DWRITE_TYPOGRAPHIC_FEATURES **user_features = context->user_features.features;
4842 unsigned int f, r, g, start_char, mask, shift, value;
4844 for (g = 0; g < context->glyph_count; ++g)
4845 context->glyph_infos[g].mask = context->global_mask;
4847 if (opentype_layout_is_subst_context(context) && context->shaper->setup_masks)
4848 context->shaper->setup_masks(context, features);
4850 for (r = 0, start_char = 0; r < context->user_features.range_count; ++r)
4852 unsigned int start_glyph, end_glyph;
4854 if (start_char >= context->length)
4855 break;
4857 if (!context->user_features.range_lengths[r])
4858 continue;
4860 opentype_layout_get_glyph_range_for_text(context, start_char, start_char + context->user_features.range_lengths[r],
4861 &start_glyph, &end_glyph);
4862 start_char += context->user_features.range_lengths[r];
4864 if (start_glyph > end_glyph || end_glyph >= context->glyph_count)
4865 continue;
4867 for (f = 0; f < user_features[r]->featureCount; ++f)
4869 mask = shaping_features_get_mask(features, user_features[r]->features[f].nameTag, &shift);
4870 if (!mask)
4871 continue;
4873 value = (user_features[r]->features[f].parameter << shift) & mask;
4875 for (g = start_glyph; g <= end_glyph; ++g)
4876 context->glyph_infos[g].mask = (context->glyph_infos[g].mask & ~mask) | value;
4881 static void opentype_layout_apply_gpos_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
4883 struct lookup lookup = { 0 };
4884 if (opentype_layout_init_lookup(context->table, lookup_index, NULL, &lookup))
4885 opentype_layout_apply_gpos_lookup(context, &lookup);
4888 static void opentype_propagate_attachment_offsets(struct scriptshaping_context *context, unsigned int i)
4890 enum attach_type type = opentype_get_glyph_attach_type(context, i);
4891 int chain = context->glyph_infos[i].attach_chain;
4892 unsigned int j, k;
4894 if (!chain)
4895 return;
4897 context->glyph_infos[i].attach_chain = 0;
4899 j = (int)i + chain;
4900 if (j >= context->glyph_count)
4901 return;
4903 opentype_propagate_attachment_offsets(context, j);
4905 if (type == GLYPH_ATTACH_CURSIVE)
4907 /* FIXME: handle vertical direction. */
4908 context->offsets[i].ascenderOffset += context->offsets[j].ascenderOffset;
4910 else if (type == GLYPH_ATTACH_MARK)
4912 context->offsets[i].advanceOffset += context->offsets[j].advanceOffset;
4913 context->offsets[i].ascenderOffset += context->offsets[j].ascenderOffset;
4915 /* FIXME: handle vertical adjustment. */
4916 if (context->is_rtl)
4918 for (k = j + 1; k < i + 1; ++k)
4920 context->offsets[i].advanceOffset += context->advances[k];
4923 else
4925 for (k = j; k < i; k++)
4927 context->offsets[i].advanceOffset -= context->advances[k];
4933 void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index,
4934 unsigned int language_index, struct shaping_features *features)
4936 struct lookups lookups = { 0 };
4937 unsigned int i;
4938 BOOL ret;
4940 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
4941 context->u.buffer.apply_context_lookup = opentype_layout_apply_gpos_context_lookup;
4942 opentype_layout_collect_lookups(context, script_index, language_index, features, &context->cache->gpos, &lookups);
4944 for (i = 0; i < context->glyph_count; ++i)
4945 opentype_set_glyph_props(context, i);
4946 opentype_layout_set_glyph_masks(context, features);
4948 for (i = 0; i < lookups.count; ++i)
4950 const struct lookup *lookup = &lookups.lookups[i];
4952 context->cur = 0;
4953 context->lookup_mask = lookup->mask;
4954 context->auto_zwnj = lookup->auto_zwnj;
4955 context->auto_zwj = lookup->auto_zwj;
4957 while (context->cur < context->glyph_count)
4959 ret = FALSE;
4961 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
4962 lookup_is_glyph_match(context, context->cur, lookup->flags))
4964 ret = opentype_layout_apply_gpos_lookup(context, lookup);
4967 if (!ret)
4968 context->cur++;
4972 free(lookups.lookups);
4974 if (context->has_gpos_attachment)
4976 for (i = 0; i < context->glyph_count; ++i)
4977 opentype_propagate_attachment_offsets(context, i);
4981 static void opentype_layout_replace_glyph(struct scriptshaping_context *context, UINT16 glyph)
4983 UINT16 orig_glyph = context->u.subst.glyphs[context->cur];
4984 if (glyph != orig_glyph)
4986 context->u.subst.glyphs[context->cur] = glyph;
4987 opentype_set_subst_glyph_props(context, context->cur);
4991 static BOOL opentype_layout_apply_gsub_single_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
4992 unsigned int subtable_offset)
4994 const struct dwrite_fonttable *table = &context->table->table;
4995 UINT16 format, coverage, orig_glyph, glyph;
4996 unsigned int coverage_index;
4998 orig_glyph = glyph = context->u.subst.glyphs[context->cur];
5000 format = table_read_be_word(table, subtable_offset);
5002 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, coverage));
5004 if (format == 1)
5006 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5007 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5009 glyph = orig_glyph + table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format1, delta));
5011 else if (format == 2)
5013 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5014 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5016 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count),
5017 coverage_index, &glyph))
5019 return FALSE;
5022 else
5024 WARN("Unknown single substitution format %u.\n", format);
5025 return FALSE;
5028 opentype_layout_replace_glyph(context, glyph);
5029 context->cur++;
5031 return TRUE;
5034 static BOOL opentype_layout_gsub_ensure_buffer(struct scriptshaping_context *context, unsigned int count)
5036 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
5037 struct shaping_glyph_info *glyph_infos;
5038 unsigned int new_capacity;
5039 UINT16 *glyphs;
5040 BOOL ret;
5042 if (context->u.subst.capacity >= count)
5043 return TRUE;
5045 new_capacity = context->u.subst.capacity * 2;
5047 if ((glyphs = realloc(context->u.subst.glyphs, new_capacity * sizeof(*glyphs))))
5048 context->u.subst.glyphs = glyphs;
5049 if ((glyph_props = realloc(context->u.subst.glyph_props, new_capacity * sizeof(*glyph_props))))
5050 context->u.subst.glyph_props = glyph_props;
5051 if ((glyph_infos = realloc(context->glyph_infos, new_capacity * sizeof(*glyph_infos))))
5052 context->glyph_infos = glyph_infos;
5054 if ((ret = (glyphs && glyph_props && glyph_infos)))
5055 context->u.subst.capacity = new_capacity;
5057 return ret;
5060 static BOOL opentype_layout_apply_gsub_mult_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5061 unsigned int subtable_offset)
5063 const struct dwrite_fonttable *table = &context->table->table;
5064 UINT16 format, coverage, glyph, glyph_count;
5065 unsigned int i, idx, coverage_index;
5066 const UINT16 *glyphs;
5068 idx = context->cur;
5069 glyph = context->u.subst.glyphs[idx];
5071 format = table_read_be_word(table, subtable_offset);
5073 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, coverage));
5075 if (format == 1)
5077 UINT16 seq_offset;
5079 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5080 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5082 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_multsubst_format1, seq_count),
5083 coverage_index, &seq_offset))
5085 return FALSE;
5088 if (!(glyphs = table_read_array_be_word(table, subtable_offset + seq_offset, ~0u, &glyph_count))) return FALSE;
5090 if (glyph_count == 1)
5092 /* Equivalent of single substitution. */
5093 opentype_layout_replace_glyph(context, GET_BE_WORD(glyphs[0]));
5094 context->cur++;
5096 else if (glyph_count == 0)
5098 context->cur++;
5100 else
5102 unsigned int shift_len, src_idx, dest_idx, mask;
5104 /* Current glyph is also replaced. */
5105 glyph_count--;
5107 if (!(opentype_layout_gsub_ensure_buffer(context, context->glyph_count + glyph_count)))
5108 return FALSE;
5110 shift_len = context->cur + 1 < context->glyph_count ? context->glyph_count - context->cur - 1 : 0;
5112 if (shift_len)
5114 src_idx = context->cur + 1;
5115 dest_idx = src_idx + glyph_count;
5117 memmove(&context->u.subst.glyphs[dest_idx], &context->u.subst.glyphs[src_idx],
5118 shift_len * sizeof(*context->u.subst.glyphs));
5119 memmove(&context->u.subst.glyph_props[dest_idx], &context->u.subst.glyph_props[src_idx],
5120 shift_len * sizeof(*context->u.subst.glyph_props));
5121 memmove(&context->glyph_infos[dest_idx], &context->glyph_infos[src_idx],
5122 shift_len * sizeof(*context->glyph_infos));
5125 mask = context->glyph_infos[context->cur].mask;
5126 for (i = 0, idx = context->cur; i <= glyph_count; ++i)
5128 glyph = GET_BE_WORD(glyphs[i]);
5129 context->u.subst.glyphs[idx + i] = glyph;
5130 if (i)
5132 context->u.subst.glyph_props[idx + i].isClusterStart = 0;
5133 context->u.buffer.glyph_props[idx + i].components = 0;
5134 context->glyph_infos[idx + i].start_text_idx = 0;
5136 opentype_set_subst_glyph_props(context, idx + i);
5137 /* Inherit feature mask from original matched glyph. */
5138 context->glyph_infos[idx + i].mask = mask;
5141 context->cur += glyph_count + 1;
5142 context->glyph_count += glyph_count;
5145 else
5147 WARN("Unknown multiple substitution format %u.\n", format);
5148 return FALSE;
5151 return TRUE;
5154 static BOOL opentype_layout_apply_gsub_alt_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5155 unsigned int subtable_offset)
5157 const struct dwrite_fonttable *table = &context->table->table;
5158 unsigned int idx, coverage_index;
5159 UINT16 format, coverage, glyph;
5161 idx = context->cur;
5162 glyph = context->u.subst.glyphs[idx];
5164 format = table_read_be_word(table, subtable_offset);
5166 if (format == 1)
5168 const struct ot_gsub_altsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
5169 DWORD shift;
5170 unsigned int alt_index;
5171 UINT16 set_offset;
5173 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, coverage));
5175 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5176 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5178 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_altsubst_format1, count),
5179 coverage_index, &set_offset))
5180 return FALSE;
5182 /* Argument is 1-based. */
5183 BitScanForward(&shift, context->lookup_mask);
5184 alt_index = (context->lookup_mask & context->glyph_infos[idx].mask) >> shift;
5185 if (!alt_index) return FALSE;
5187 if (!table_read_array_be_word(table, subtable_offset + set_offset, alt_index - 1, &glyph)) return FALSE;
5189 else
5191 WARN("Unexpected alternate substitution format %d.\n", format);
5192 return FALSE;
5195 opentype_layout_replace_glyph(context, glyph);
5196 context->cur++;
5198 return TRUE;
5201 static BOOL opentype_layout_context_match_input(const struct match_context *mc, unsigned int count, const UINT16 *input,
5202 unsigned int *end_offset, unsigned int *match_positions)
5204 struct match_data match_data = { .mc = mc, .subtable_offset = mc->input_offset };
5205 struct scriptshaping_context *context = mc->context;
5206 struct glyph_iterator iter;
5207 unsigned int i;
5209 if (count > GLYPH_CONTEXT_MAX_LENGTH)
5210 return FALSE;
5212 match_positions[0] = context->cur;
5214 glyph_iterator_init(context, mc->lookup->flags, context->cur, count - 1, &iter);
5215 iter.mask = context->lookup_mask;
5216 iter.match_func = mc->match_func;
5217 iter.match_data = &match_data;
5218 iter.glyph_data = input;
5220 for (i = 1; i < count; ++i)
5222 if (!glyph_iterator_next(&iter))
5223 return FALSE;
5225 match_positions[i] = iter.pos;
5228 *end_offset = iter.pos - context->cur + 1;
5230 return TRUE;
5233 /* Marks text segment as unsafe to break between [start, end) glyphs. */
5234 void opentype_layout_unsafe_to_break(struct scriptshaping_context *context, unsigned int start,
5235 unsigned int end)
5237 unsigned int i;
5239 while (start && !context->u.buffer.glyph_props[start].isClusterStart)
5240 --start;
5242 while (--end && !context->u.buffer.glyph_props[end].isClusterStart)
5245 if (start == end)
5247 context->u.buffer.text_props[context->glyph_infos[start].start_text_idx].canBreakShapingAfter = 0;
5248 return;
5251 for (i = context->glyph_infos[start].start_text_idx; i < context->glyph_infos[end].start_text_idx; ++i)
5253 context->u.buffer.text_props[i].canBreakShapingAfter = 0;
5257 static void opentype_layout_delete_glyph(struct scriptshaping_context *context, unsigned int idx)
5259 unsigned int shift_len;
5261 shift_len = context->glyph_count - context->cur - 1;
5263 if (shift_len)
5265 memmove(&context->u.buffer.glyphs[idx], &context->u.buffer.glyphs[idx + 1],
5266 shift_len * sizeof(*context->u.buffer.glyphs));
5267 memmove(&context->u.buffer.glyph_props[idx], &context->u.buffer.glyph_props[idx + 1],
5268 shift_len * sizeof(*context->u.buffer.glyph_props));
5269 memmove(&context->glyph_infos[idx], &context->glyph_infos[idx + 1], shift_len * sizeof(*context->glyph_infos));
5272 context->glyph_count--;
5275 static BOOL opentype_layout_apply_ligature(struct scriptshaping_context *context, unsigned int offset,
5276 const struct lookup *lookup)
5278 struct match_context mc = { .context = context, .lookup = lookup, .match_func = opentype_match_glyph_func };
5279 const struct dwrite_fonttable *gsub = &context->table->table;
5280 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5281 unsigned int i, j, comp_count, match_length = 0;
5282 const struct ot_gsub_lig *lig;
5283 UINT16 lig_glyph;
5285 comp_count = table_read_be_word(gsub, offset + FIELD_OFFSET(struct ot_gsub_lig, comp_count));
5287 if (!comp_count)
5288 return FALSE;
5290 lig = table_read_ensure(gsub, offset, FIELD_OFFSET(struct ot_gsub_lig, components[comp_count-1]));
5291 if (!lig)
5292 return FALSE;
5294 lig_glyph = GET_BE_WORD(lig->lig_glyph);
5296 if (comp_count == 1)
5298 opentype_layout_replace_glyph(context, lig_glyph);
5299 context->cur++;
5300 return TRUE;
5303 if (!opentype_layout_context_match_input(&mc, comp_count, lig->components, &match_length, match_positions))
5304 return FALSE;
5306 opentype_layout_replace_glyph(context, lig_glyph);
5307 context->u.buffer.glyph_props[context->cur].components = comp_count;
5309 /* Positioning against a ligature implies keeping track of ligature component
5310 glyph should be attached to. Update per-glyph property for interleaving glyphs,
5311 0 means attaching to last component, n - attaching to n-th glyph before last. */
5312 for (i = 1; i < comp_count; ++i)
5314 j = match_positions[i - 1] + 1;
5315 while (j < match_positions[i])
5317 context->u.buffer.glyph_props[j++].lig_component = comp_count - i;
5320 opentype_layout_unsafe_to_break(context, match_positions[0], match_positions[comp_count - 1] + 1);
5322 /* Delete ligated glyphs, backwards to preserve index. */
5323 for (i = 1; i < comp_count; ++i)
5325 opentype_layout_delete_glyph(context, match_positions[comp_count - i]);
5328 /* Skip whole matched sequence, accounting for deleted glyphs. */
5329 context->cur += match_length - (comp_count - 1);
5331 return TRUE;
5334 static BOOL opentype_layout_apply_gsub_lig_substitution(struct scriptshaping_context *context, const struct lookup *lookup,
5335 unsigned int subtable_offset)
5337 const struct dwrite_fonttable *table = &context->table->table;
5338 UINT16 format, coverage, glyph, lig_set_offset;
5339 unsigned int coverage_index;
5341 glyph = context->u.subst.glyphs[context->cur];
5343 format = table_read_be_word(table, subtable_offset);
5345 if (format == 1)
5347 const struct ot_gsub_ligsubst_format1 *format1 = table_read_ensure(table, subtable_offset, sizeof(*format1));
5348 unsigned int i;
5349 const UINT16 *offsets;
5350 UINT16 lig_count;
5352 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, coverage));
5354 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5355 if (coverage_index == GLYPH_NOT_COVERED) return FALSE;
5357 if (!table_read_array_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_ligsubst_format1, lig_set_count),
5358 coverage_index, &lig_set_offset))
5359 return FALSE;
5361 if (!(offsets = table_read_array_be_word(table, subtable_offset + lig_set_offset, ~0u, &lig_count)))
5362 return FALSE;
5364 /* First applicable ligature is used. */
5365 for (i = 0; i < lig_count; ++i)
5367 if (opentype_layout_apply_ligature(context, subtable_offset + lig_set_offset + GET_BE_WORD(offsets[i]), lookup))
5368 return TRUE;
5371 else
5372 WARN("Unexpected ligature substitution format %d.\n", format);
5374 return FALSE;
5377 static BOOL opentype_layout_context_match_backtrack(const struct match_context *mc, unsigned int count,
5378 const UINT16 *backtrack, unsigned int *match_start)
5380 struct match_data match_data = { .mc = mc, .subtable_offset = mc->backtrack_offset };
5381 struct scriptshaping_context *context = mc->context;
5382 struct glyph_iterator iter;
5383 unsigned int i;
5385 glyph_iterator_init(context, mc->lookup->flags, context->cur, count, &iter);
5386 iter.match_func = mc->match_func;
5387 iter.match_data = &match_data;
5388 iter.glyph_data = backtrack;
5389 iter.ignore_zwnj |= context->auto_zwnj;
5390 iter.ignore_zwj = 1;
5392 for (i = 0; i < count; ++i)
5394 if (!glyph_iterator_prev(&iter))
5395 return FALSE;
5398 *match_start = iter.pos;
5400 return TRUE;
5403 static BOOL opentype_layout_context_match_lookahead(const struct match_context *mc, unsigned int count,
5404 const UINT16 *lookahead, unsigned int offset, unsigned int *end_index)
5406 struct match_data match_data = { .mc = mc, .subtable_offset = mc->lookahead_offset };
5407 struct scriptshaping_context *context = mc->context;
5408 struct glyph_iterator iter;
5409 unsigned int i;
5411 glyph_iterator_init(context, mc->lookup->flags, context->cur + offset - 1, count, &iter);
5412 iter.match_func = mc->match_func;
5413 iter.match_data = &match_data;
5414 iter.glyph_data = lookahead;
5415 iter.ignore_zwnj |= context->auto_zwnj;
5416 iter.ignore_zwj = 1;
5418 for (i = 0; i < count; ++i)
5420 if (!glyph_iterator_next(&iter))
5421 return FALSE;
5424 *end_index = iter.pos;
5426 return TRUE;
5429 static BOOL opentype_layout_context_apply_lookup(struct scriptshaping_context *context, unsigned int count,
5430 unsigned int *match_positions, unsigned int lookup_count, const UINT16 *lookup_records, unsigned int match_length)
5432 unsigned int i, j;
5433 int end, delta;
5435 if (!context->nesting_level_left)
5436 return TRUE;
5438 end = context->cur + match_length;
5440 for (i = 0; i < lookup_count; ++i)
5442 unsigned int idx = GET_BE_WORD(lookup_records[i]);
5443 unsigned int orig_len, lookup_index, next;
5445 if (idx >= count)
5446 continue;
5448 context->cur = match_positions[idx];
5450 orig_len = context->glyph_count;
5452 lookup_index = GET_BE_WORD(lookup_records[i+1]);
5454 --context->nesting_level_left;
5455 context->u.buffer.apply_context_lookup(context, lookup_index);
5456 ++context->nesting_level_left;
5458 delta = context->glyph_count - orig_len;
5459 if (!delta)
5460 continue;
5462 end += delta;
5463 if (end <= (int)match_positions[idx])
5465 end = match_positions[idx];
5466 break;
5469 next = idx + 1;
5471 if (delta > 0)
5473 if (delta + count > GLYPH_CONTEXT_MAX_LENGTH)
5474 break;
5476 else
5478 delta = max(delta, (int)next - (int)count);
5479 next -= delta;
5482 memmove(match_positions + next + delta, match_positions + next,
5483 (count - next) * sizeof (*match_positions));
5484 next += delta;
5485 count += delta;
5487 for (j = idx + 1; j < next; j++)
5488 match_positions[j] = match_positions[j - 1] + 1;
5490 for (; next < count; next++)
5491 match_positions[next] += delta;
5494 context->cur = end;
5496 return TRUE;
5499 static BOOL opentype_layout_apply_chain_context_match(unsigned int backtrack_count, const UINT16 *backtrack,
5500 unsigned int input_count, const UINT16 *input, unsigned int lookahead_count, const UINT16 *lookahead,
5501 unsigned int lookup_count, const UINT16 *lookup_records, const struct match_context *mc)
5503 unsigned int start_index = 0, match_length = 0, end_index = 0;
5504 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5506 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5507 opentype_layout_context_match_backtrack(mc, backtrack_count, backtrack, &start_index) &&
5508 opentype_layout_context_match_lookahead(mc, lookahead_count, lookahead, input_count, &end_index) &&
5509 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count, lookup_records, match_length);
5512 static BOOL opentype_layout_apply_chain_rule_set(const struct match_context *mc, unsigned int offset)
5514 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5515 const struct dwrite_fonttable *table = &mc->context->table->table;
5516 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5517 const struct ot_gsubgpos_ruleset *ruleset;
5518 unsigned int i, count;
5520 count = table_read_be_word(table, offset);
5521 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5523 for (i = 0; i < count; ++i)
5525 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5527 backtrack_count = table_read_be_word(table, rule_offset);
5528 rule_offset += 2;
5529 backtrack = table_read_ensure(table, rule_offset, backtrack_count * sizeof(*backtrack));
5530 rule_offset += backtrack_count * sizeof(*backtrack);
5532 if (!(input_count = table_read_be_word(table, rule_offset)))
5533 continue;
5535 rule_offset += 2;
5536 input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input));
5537 rule_offset += (input_count - 1) * sizeof(*input);
5539 lookahead_count = table_read_be_word(table, rule_offset);
5540 rule_offset += 2;
5541 lookahead = table_read_ensure(table, rule_offset, lookahead_count * sizeof(*lookahead));
5542 rule_offset += lookahead_count * sizeof(*lookahead);
5544 lookup_count = table_read_be_word(table, rule_offset);
5545 rule_offset += 2;
5546 lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records));
5548 /* First applicable rule is used. */
5549 if (opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input, lookahead_count,
5550 lookahead, lookup_count, lookup_records, mc))
5552 return TRUE;
5556 return FALSE;
5559 static BOOL opentype_layout_apply_context_match(unsigned int input_count, const UINT16 *input, unsigned int lookup_count,
5560 const UINT16 *lookup_records, const struct match_context *mc)
5562 unsigned int match_positions[GLYPH_CONTEXT_MAX_LENGTH];
5563 unsigned int match_length = 0;
5565 return opentype_layout_context_match_input(mc, input_count, input, &match_length, match_positions) &&
5566 opentype_layout_context_apply_lookup(mc->context, input_count, match_positions, lookup_count,
5567 lookup_records, match_length);
5570 static BOOL opentype_layout_apply_rule_set(const struct match_context *mc, unsigned int offset)
5572 unsigned int input_count, lookup_count;
5573 const struct dwrite_fonttable *table = &mc->context->table->table;
5574 const UINT16 *input, *lookup_records;
5575 const struct ot_gsubgpos_ruleset *ruleset;
5576 unsigned int i, count;
5578 count = table_read_be_word(table, offset);
5579 ruleset = table_read_ensure(table, offset, count * sizeof(ruleset->offsets));
5581 for (i = 0; i < count; ++i)
5583 unsigned int rule_offset = offset + GET_BE_WORD(ruleset->offsets[i]);
5585 if (!(input_count = table_read_be_word(table, rule_offset)))
5586 continue;
5587 rule_offset += 2;
5589 if (!(lookup_count = table_read_be_word(table, rule_offset)))
5590 continue;
5591 rule_offset += 2;
5593 if (!(input = table_read_ensure(table, rule_offset, (input_count - 1) * sizeof(*input))))
5594 continue;
5595 rule_offset += (input_count - 1) * sizeof(*input);
5597 if (!(lookup_records = table_read_ensure(table, rule_offset, lookup_count * 2 * sizeof(*lookup_records))))
5598 continue;
5600 /* First applicable rule is used. */
5601 if (opentype_layout_apply_context_match(input_count, input, lookup_count, lookup_records, mc))
5602 return TRUE;
5605 return FALSE;
5608 static BOOL opentype_layout_apply_context(struct scriptshaping_context *context, const struct lookup *lookup,
5609 unsigned int subtable_offset)
5611 struct match_context mc = { .context = context, .lookup = lookup };
5612 const struct dwrite_fonttable *table = &context->table->table;
5613 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5614 UINT16 glyph, format, coverage;
5615 BOOL ret = FALSE;
5617 glyph = context->u.subst.glyphs[context->cur];
5619 format = table_read_be_word(table, subtable_offset);
5621 if (format == 1)
5623 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5625 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5626 if (coverage_index == GLYPH_NOT_COVERED)
5627 return FALSE;
5629 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5630 if (coverage_index >= count)
5631 return FALSE;
5633 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5634 rulesets[coverage_index]));
5635 offset += subtable_offset;
5637 mc.match_func = opentype_match_glyph_func;
5639 ret = opentype_layout_apply_rule_set(&mc, offset);
5641 else if (format == 2)
5643 unsigned int input_classdef, rule_set_idx;
5645 offset = subtable_offset + 2 /* format */;
5647 coverage = table_read_be_word(table, offset);
5648 offset += 2;
5650 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5651 if (coverage_index == GLYPH_NOT_COVERED)
5652 return FALSE;
5654 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5655 offset += 2;
5657 count = table_read_be_word(table, offset);
5658 offset+= 2;
5660 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5661 if (rule_set_idx >= count)
5662 return FALSE;
5664 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5665 offset += subtable_offset;
5667 mc.input_offset = input_classdef;
5668 mc.match_func = opentype_match_class_func;
5670 ret = opentype_layout_apply_rule_set(&mc, offset);
5672 else if (format == 3)
5674 unsigned int input_count, lookup_count;
5675 const UINT16 *input, *lookup_records;
5677 offset = subtable_offset + 2 /* format */;
5679 input_count = table_read_be_word(table, offset);
5680 offset += 2;
5682 if (!input_count)
5683 return FALSE;
5685 lookup_count = table_read_be_word(table, offset);
5686 offset += 2;
5688 if (!(input = table_read_ensure(table, offset, sizeof(*input) * input_count)))
5689 return FALSE;
5690 offset += sizeof(*input) * input_count;
5692 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5693 if (coverage_index == GLYPH_NOT_COVERED)
5694 return FALSE;
5696 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5698 mc.input_offset = subtable_offset;
5699 mc.match_func = opentype_match_coverage_func;
5701 ret = opentype_layout_apply_context_match(input_count, input + 1, lookup_count, lookup_records, &mc);
5703 else
5704 WARN("Unknown contextual substitution format %u.\n", format);
5706 return ret;
5709 static BOOL opentype_layout_apply_chain_context(struct scriptshaping_context *context, const struct lookup *lookup,
5710 unsigned int subtable_offset)
5712 struct match_context mc = { .context = context, .lookup = lookup };
5713 const struct dwrite_fonttable *table = &context->table->table;
5714 unsigned int coverage_index = GLYPH_NOT_COVERED, count, offset;
5715 UINT16 glyph, format, coverage;
5716 BOOL ret = FALSE;
5718 glyph = context->u.subst.glyphs[context->cur];
5720 format = table_read_be_word(table, subtable_offset);
5722 if (format == 1)
5724 coverage = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, coverage));
5726 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5727 if (coverage_index == GLYPH_NOT_COVERED)
5728 return FALSE;
5730 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1, ruleset_count));
5731 if (coverage_index >= count)
5732 return FALSE;
5734 offset = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsubgpos_context_format1,
5735 rulesets[coverage_index]));
5736 offset += subtable_offset;
5738 mc.match_func = opentype_match_glyph_func;
5740 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5742 else if (format == 2)
5744 unsigned int backtrack_classdef, input_classdef, lookahead_classdef, rule_set_idx;
5746 offset = subtable_offset + 2 /* format */;
5748 coverage = table_read_be_word(table, offset);
5749 offset += 2;
5751 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5752 if (coverage_index == GLYPH_NOT_COVERED)
5753 return FALSE;
5755 backtrack_classdef = table_read_be_word(table, offset) + subtable_offset;
5756 offset += 2;
5758 input_classdef = table_read_be_word(table, offset) + subtable_offset;
5759 offset += 2;
5761 lookahead_classdef = table_read_be_word(table, offset) + subtable_offset;
5762 offset += 2;
5764 count = table_read_be_word(table, offset);
5765 offset+= 2;
5767 rule_set_idx = opentype_layout_get_glyph_class(table, input_classdef, glyph);
5768 if (rule_set_idx >= count)
5769 return FALSE;
5771 offset = table_read_be_word(table, offset + rule_set_idx * 2);
5772 offset += subtable_offset;
5774 mc.backtrack_offset = backtrack_classdef;
5775 mc.input_offset = input_classdef;
5776 mc.lookahead_offset = lookahead_classdef;
5777 mc.match_func = opentype_match_class_func;
5779 ret = opentype_layout_apply_chain_rule_set(&mc, offset);
5781 else if (format == 3)
5783 unsigned int backtrack_count, input_count, lookahead_count, lookup_count;
5784 const UINT16 *backtrack, *lookahead, *input, *lookup_records;
5786 offset = subtable_offset + 2 /* format */;
5788 backtrack_count = table_read_be_word(table, offset);
5789 offset += 2;
5790 backtrack = table_read_ensure(table, offset, backtrack_count * sizeof(*backtrack));
5791 offset += backtrack_count * sizeof(*backtrack);
5793 input_count = table_read_be_word(table, offset);
5794 offset += 2;
5795 input = table_read_ensure(table, offset, input_count * sizeof(*input));
5796 offset += input_count * sizeof(*input);
5798 lookahead_count = table_read_be_word(table, offset);
5799 offset += 2;
5800 lookahead = table_read_ensure(table, offset, lookahead_count * sizeof(*lookahead));
5801 offset += lookahead_count * sizeof(*lookahead);
5803 lookup_count = table_read_be_word(table, offset);
5804 offset += 2;
5805 lookup_records = table_read_ensure(table, offset, lookup_count * 2 * sizeof(*lookup_records));
5807 if (input)
5808 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(input[0]), glyph);
5810 if (coverage_index == GLYPH_NOT_COVERED)
5811 return FALSE;
5813 mc.backtrack_offset = subtable_offset;
5814 mc.input_offset = subtable_offset;
5815 mc.lookahead_offset = subtable_offset;
5816 mc.match_func = opentype_match_coverage_func;
5818 ret = opentype_layout_apply_chain_context_match(backtrack_count, backtrack, input_count, input + 1, lookahead_count,
5819 lookahead, lookup_count, lookup_records, &mc);
5821 else
5822 WARN("Unknown chaining contextual substitution format %u.\n", format);
5824 return ret;
5827 static BOOL opentype_layout_apply_gsub_reverse_chain_context_substitution(struct scriptshaping_context *context,
5828 const struct lookup *lookup, unsigned int subtable_offset)
5830 const struct dwrite_fonttable *table = &context->table->table;
5831 unsigned int offset = subtable_offset;
5832 UINT16 glyph, format;
5834 if (context->nesting_level_left != SHAPE_MAX_NESTING_LEVEL)
5835 return FALSE;
5837 glyph = context->u.subst.glyphs[context->cur];
5839 format = table_read_be_word(table, offset);
5840 offset += 2;
5842 if (format == 1)
5844 struct match_context mc = { .context = context, .lookup = lookup };
5845 unsigned int start_index = 0, end_index = 0, backtrack_count, lookahead_count;
5846 unsigned int coverage, coverage_index;
5847 const UINT16 *backtrack, *lookahead;
5849 coverage = table_read_be_word(table, offset);
5850 offset += 2;
5852 coverage_index = opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph);
5853 if (coverage_index == GLYPH_NOT_COVERED)
5854 return FALSE;
5856 backtrack_count = table_read_be_word(table, offset);
5857 offset += 2;
5859 backtrack = table_read_ensure(table, offset, sizeof(*backtrack) * backtrack_count);
5860 offset += sizeof(*backtrack) * backtrack_count;
5862 lookahead_count = table_read_be_word(table, offset);
5863 offset += 2;
5865 lookahead = table_read_ensure(table, offset, sizeof(*lookahead) * lookahead_count);
5866 offset += sizeof(*lookahead) * lookahead_count;
5868 mc.match_func = opentype_match_coverage_func;
5869 mc.backtrack_offset = subtable_offset;
5870 mc.lookahead_offset = subtable_offset;
5872 if (opentype_layout_context_match_backtrack(&mc, backtrack_count, backtrack, &start_index) &&
5873 opentype_layout_context_match_lookahead(&mc, lookahead_count, lookahead, 1, &end_index))
5875 unsigned int glyph_count = table_read_be_word(table, offset);
5876 if (coverage_index >= glyph_count)
5877 return FALSE;
5878 offset += 2;
5880 glyph = table_read_be_word(table, offset + coverage_index * sizeof(glyph));
5881 opentype_layout_replace_glyph(context, glyph);
5883 return TRUE;
5886 else
5887 WARN("Unknown reverse chaining contextual substitution format %u.\n", format);
5889 return FALSE;
5892 static BOOL opentype_layout_apply_gsub_lookup(struct scriptshaping_context *context, const struct lookup *lookup)
5894 unsigned int i, lookup_type;
5895 BOOL ret = FALSE;
5897 for (i = 0; i < lookup->subtable_count; ++i)
5899 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
5901 switch (lookup_type)
5903 case GSUB_LOOKUP_SINGLE_SUBST:
5904 ret = opentype_layout_apply_gsub_single_substitution(context, lookup, subtable_offset);
5905 break;
5906 case GSUB_LOOKUP_MULTIPLE_SUBST:
5907 ret = opentype_layout_apply_gsub_mult_substitution(context, lookup, subtable_offset);
5908 break;
5909 case GSUB_LOOKUP_ALTERNATE_SUBST:
5910 ret = opentype_layout_apply_gsub_alt_substitution(context, lookup, subtable_offset);
5911 break;
5912 case GSUB_LOOKUP_LIGATURE_SUBST:
5913 ret = opentype_layout_apply_gsub_lig_substitution(context, lookup, subtable_offset);
5914 break;
5915 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
5916 ret = opentype_layout_apply_context(context, lookup, subtable_offset);
5917 break;
5918 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
5919 ret = opentype_layout_apply_chain_context(context, lookup, subtable_offset);
5920 break;
5921 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
5922 ret = opentype_layout_apply_gsub_reverse_chain_context_substitution(context, lookup, subtable_offset);
5923 break;
5924 case GSUB_LOOKUP_EXTENSION_SUBST:
5925 WARN("Invalid lookup type for extension substitution %#x.\n", lookup_type);
5926 break;
5927 default:
5928 WARN("Unknown lookup type %u.\n", lookup_type);
5931 if (ret)
5932 break;
5935 return ret;
5938 static unsigned int unicode_get_mirrored_char(unsigned int codepoint)
5940 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
5941 WCHAR mirror;
5942 /* TODO: check if mirroring for higher planes makes sense at all */
5943 if (codepoint > 0xffff) return codepoint;
5944 mirror = get_table_entry_16(wine_mirror_map, codepoint);
5945 return mirror ? mirror : codepoint;
5949 * 034F # Mn COMBINING GRAPHEME JOINER
5950 * 061C # Cf ARABIC LETTER MARK
5951 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5952 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5953 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5954 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5956 static unsigned int opentype_is_zero_width(unsigned int codepoint)
5958 return codepoint == 0x34f || codepoint == 0x61c || codepoint == 0xfeff ||
5959 (codepoint >= 0x180b && codepoint <= 0x180e) || (codepoint >= 0x200b && codepoint <= 0x200f);
5963 * 00AD # Cf SOFT HYPHEN
5964 * 034F # Mn COMBINING GRAPHEME JOINER
5965 * 061C # Cf ARABIC LETTER MARK
5966 * 115F..1160 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER
5967 * 17B4..17B5 # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
5968 * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
5969 * 180E # Cf MONGOLIAN VOWEL SEPARATOR
5970 * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
5971 * 202A..202E # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
5972 * 2060..2064 # Cf [5] WORD JOINER..INVISIBLE PLUS
5973 * 2065 # Cn <reserved-2065>
5974 * 2066..206F # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
5975 * 3164 # Lo HANGUL FILLER
5976 * FE00..FE0F # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
5977 * FEFF # Cf ZERO WIDTH NO-BREAK SPACE
5978 * FFA0 # Lo HALFWIDTH HANGUL FILLER
5979 * FFF0..FFF8 # Cn [9] <reserved-FFF0>..<reserved-FFF8>
5980 * 1BCA0..1BCA3 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
5981 * 1D173..1D17A # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
5982 * E0000 # Cn <reserved-E0000>
5983 * E0001 # Cf LANGUAGE TAG
5984 * E0002..E001F # Cn [30] <reserved-E0002>..<reserved-E001F>
5985 * E0020..E007F # Cf [96] TAG SPACE..CANCEL TAG
5986 * E0080..E00FF # Cn [128] <reserved-E0080>..<reserved-E00FF>
5987 * E0100..E01EF # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
5988 * E01F0..E0FFF # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
5990 static unsigned int opentype_is_default_ignorable(unsigned int codepoint)
5992 if (codepoint < 0x80) return 0;
5993 return codepoint == 0xad ||
5994 codepoint == 0x34f ||
5995 codepoint == 0x61c ||
5996 (codepoint >= 0x17b4 && codepoint <= 0x17b5) ||
5997 (codepoint >= 0x180b && codepoint <= 0x180e) ||
5998 (codepoint >= 0x200b && codepoint <= 0x200f) ||
5999 (codepoint >= 0x202a && codepoint <= 0x202e) ||
6000 (codepoint >= 0x2060 && codepoint <= 0x206f) ||
6001 (codepoint >= 0xfe00 && codepoint <= 0xfe0f) ||
6002 codepoint == 0xfeff ||
6003 (codepoint >= 0xfff0 && codepoint <= 0xfff8) ||
6004 (codepoint >= 0x1d173 && codepoint <= 0x1d17a) ||
6005 (codepoint >= 0xe0000 && codepoint <= 0xe0fff);
6008 static unsigned int opentype_is_diacritic(unsigned int codepoint)
6010 WCHAR ch = codepoint;
6011 WORD type = 0;
6012 /* Ignore higher planes for now. */
6013 if (codepoint > 0xffff) return 0;
6014 GetStringTypeW(CT_CTYPE3, &ch, 1, &type);
6015 return !!(type & C3_DIACRITIC);
6018 static void opentype_get_nominal_glyphs(struct scriptshaping_context *context, const struct shaping_features *features)
6020 unsigned int rtlm_mask = shaping_features_get_mask(features, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), NULL);
6021 const struct shaping_font_ops *font = context->cache->font;
6022 unsigned int i, g, c, codepoint, cluster_start_idx = 0;
6023 UINT16 *clustermap = context->u.subst.clustermap;
6024 const WCHAR *text = context->text;
6025 BOOL bmp;
6027 memset(context->u.subst.glyph_props, 0, context->u.subst.max_glyph_count * sizeof(*context->u.subst.glyph_props));
6028 memset(context->u.buffer.text_props, 0, context->length * sizeof(*context->u.buffer.text_props));
6030 for (i = 0; i < context->length; ++i)
6032 g = context->glyph_count;
6034 if ((bmp = !(IS_HIGH_SURROGATE(text[i]) && (i < context->length - 1) && IS_LOW_SURROGATE(text[i + 1]))))
6036 codepoint = text[i];
6038 else
6040 codepoint = 0x10000 + ((text[i] - 0xd800) << 10) + (text[i + 1] - 0xdc00);
6043 if (context->is_rtl)
6045 c = unicode_get_mirrored_char(codepoint);
6046 if (c != codepoint && font->has_glyph(context->cache->context, c))
6047 codepoint = c;
6048 else
6049 context->glyph_infos[i].mask |= rtlm_mask;
6052 /* Glyph availability is not tested for a replacement digit. */
6053 if (*context->u.subst.digits && codepoint >= '0' && codepoint <= '9')
6054 codepoint = context->u.subst.digits[codepoint - '0'];
6056 context->glyph_infos[g].codepoint = codepoint;
6057 context->u.buffer.glyphs[g] = font->get_glyph(context->cache->context, codepoint);
6058 context->u.buffer.glyph_props[g].justification = SCRIPT_JUSTIFY_CHARACTER;
6059 opentype_set_subst_glyph_props(context, g);
6060 if (opentype_is_default_ignorable(codepoint))
6062 context->glyph_infos[g].props |= GLYPH_PROP_IGNORABLE;
6063 if (codepoint == 0x200d)
6064 context->glyph_infos[g].props |= GLYPH_PROP_ZWJ;
6065 else if (codepoint == 0x200c)
6066 context->glyph_infos[g].props |= GLYPH_PROP_ZWNJ;
6067 /* Mongolian FVSs, TAGs, COMBINING GRAPHEME JOINER */
6068 else if ((codepoint >= 0x180b && codepoint <= 0x180d) ||
6069 (codepoint >= 0xe0020 && codepoint <= 0xe007f) ||
6070 codepoint == 0x34f)
6072 context->glyph_infos[g].props |= GLYPH_PROP_HIDDEN;
6076 /* Group diacritics with preceding base. Glyph class is ignored here. */
6077 if (!g || !opentype_is_diacritic(codepoint))
6079 context->u.buffer.glyph_props[g].isClusterStart = 1;
6080 context->glyph_infos[g].start_text_idx = i;
6081 cluster_start_idx = g;
6083 if (opentype_is_zero_width(codepoint))
6084 context->u.buffer.glyph_props[g].isZeroWidthSpace = 1;
6086 context->u.buffer.glyph_props[g].components = 1;
6087 context->glyph_count++;
6089 /* Set initial cluster map here, it's used for setting user features masks. */
6090 clustermap[i] = cluster_start_idx;
6091 if (bmp)
6092 context->u.buffer.text_props[i].canBreakShapingAfter = 1;
6093 else
6095 clustermap[i + 1] = cluster_start_idx;
6096 context->u.buffer.text_props[i + 1].canBreakShapingAfter = 1;
6097 ++i;
6102 static BOOL opentype_is_gsub_lookup_reversed(const struct scriptshaping_context *context, const struct lookup *lookup)
6104 unsigned int lookup_type;
6106 opentype_layout_get_gsubgpos_subtable(context, lookup, 0, &lookup_type);
6107 return lookup_type == GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST;
6110 static void opentype_layout_apply_gsub_context_lookup(struct scriptshaping_context *context, unsigned int lookup_index)
6112 struct lookup lookup = { 0 };
6113 if (opentype_layout_init_lookup(context->table, lookup_index, NULL, &lookup))
6114 opentype_layout_apply_gsub_lookup(context, &lookup);
6117 void opentype_layout_apply_gsub_features(struct scriptshaping_context *context, unsigned int script_index,
6118 unsigned int language_index, struct shaping_features *features)
6120 struct lookups lookups = { 0 };
6121 unsigned int i = 0, j, start_idx;
6122 BOOL ret;
6124 context->nesting_level_left = SHAPE_MAX_NESTING_LEVEL;
6125 context->u.buffer.apply_context_lookup = opentype_layout_apply_gsub_context_lookup;
6126 opentype_layout_collect_lookups(context, script_index, language_index, features, context->table, &lookups);
6128 opentype_get_nominal_glyphs(context, features);
6129 opentype_layout_set_glyph_masks(context, features);
6131 for (j = 0; j <= features->stage; ++j)
6133 for (; i < features->stages[j].last_lookup; ++i)
6135 const struct lookup *lookup = &lookups.lookups[i];
6137 context->lookup_mask = lookup->mask;
6138 context->auto_zwnj = lookup->auto_zwnj;
6139 context->auto_zwj = lookup->auto_zwj;
6141 if (!opentype_is_gsub_lookup_reversed(context, lookup))
6143 context->cur = 0;
6144 while (context->cur < context->glyph_count)
6146 ret = FALSE;
6148 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
6149 lookup_is_glyph_match(context, context->cur, lookup->flags))
6151 ret = opentype_layout_apply_gsub_lookup(context, lookup);
6154 if (!ret)
6155 context->cur++;
6158 else
6160 context->cur = context->glyph_count - 1;
6162 for (;;)
6164 if ((context->glyph_infos[context->cur].mask & lookup->mask) &&
6165 lookup_is_glyph_match(context, context->cur, lookup->flags))
6167 opentype_layout_apply_gsub_lookup(context, lookup);
6170 if (context->cur == 0) break;
6171 --context->cur;
6176 if (features->stages[j].func)
6177 features->stages[j].func(context, features);
6180 /* For every glyph range of [<last>.isClusterStart, <next>.isClusterStart) set corresponding
6181 text span to start_idx. */
6182 start_idx = 0;
6183 for (i = 1; i < context->glyph_count; ++i)
6185 if (context->u.buffer.glyph_props[i].isClusterStart)
6187 unsigned int start_text, end_text;
6189 start_text = context->glyph_infos[start_idx].start_text_idx;
6190 end_text = context->glyph_infos[i].start_text_idx;
6192 for (j = start_text; j < end_text; ++j)
6193 context->u.buffer.clustermap[j] = start_idx;
6195 start_idx = i;
6199 /* Fill the tail. */
6200 for (j = context->glyph_infos[start_idx].start_text_idx; j < context->length; ++j)
6201 context->u.buffer.clustermap[j] = start_idx;
6203 free(lookups.lookups);
6206 static BOOL opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6207 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
6209 const struct dwrite_fonttable *table = &context->table->table;
6210 const UINT16 *offsets;
6211 unsigned int count;
6213 if (format == 1 || format == 2)
6215 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6216 return TRUE;
6218 else if (format == 3)
6220 count = table_read_be_word(table, subtable_offset + 2);
6221 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6, count * sizeof(*offsets))))
6222 return FALSE;
6224 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
6225 return TRUE;
6228 return FALSE;
6231 static BOOL opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6232 unsigned int subtable_offset, unsigned int coverage, unsigned int format)
6234 const struct dwrite_fonttable *table = &context->table->table;
6235 unsigned int count, backtrack_count;
6236 const UINT16 *offsets;
6238 if (format == 1 || format == 2)
6240 if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6241 return TRUE;
6243 else if (format == 3)
6245 backtrack_count = table_read_be_word(table, subtable_offset + 2);
6247 count = table_read_be_word(table, subtable_offset + 4 + backtrack_count * sizeof(*offsets));
6249 if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6 + backtrack_count * sizeof(*offsets),
6250 count * sizeof(*offsets))))
6251 return FALSE;
6253 if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
6254 return TRUE;
6257 return FALSE;
6260 static BOOL opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6261 const struct lookup *lookup)
6263 const struct dwrite_fonttable *gsub = &context->table->table;
6264 static const unsigned short gsub_formats[] =
6266 0, /* Unused */
6267 1, /* SingleSubst */
6268 1, /* MultipleSubst */
6269 1, /* AlternateSubst */
6270 1, /* LigatureSubst */
6271 3, /* ContextSubst */
6272 3, /* ChainContextSubst */
6273 0, /* Extension, unused */
6274 1, /* ReverseChainSubst */
6276 unsigned int i, coverage, lookup_type, format;
6278 for (i = 0; i < lookup->subtable_count; ++i)
6280 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
6282 format = table_read_be_word(gsub, subtable_offset);
6284 if (!format || format > ARRAY_SIZE(gsub_formats) || format > gsub_formats[lookup_type])
6285 break;
6287 coverage = table_read_be_word(gsub, subtable_offset + 2);
6289 switch (lookup_type)
6291 case GSUB_LOOKUP_SINGLE_SUBST:
6292 case GSUB_LOOKUP_MULTIPLE_SUBST:
6293 case GSUB_LOOKUP_ALTERNATE_SUBST:
6294 case GSUB_LOOKUP_LIGATURE_SUBST:
6295 case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
6297 if (opentype_layout_is_glyph_covered(gsub, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6298 return TRUE;
6300 break;
6302 case GSUB_LOOKUP_CONTEXTUAL_SUBST:
6304 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6305 return TRUE;
6307 break;
6309 case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
6311 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6312 return TRUE;
6314 break;
6316 default:
6317 WARN("Unknown lookup type %u.\n", lookup_type);
6321 return FALSE;
6324 static BOOL opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
6325 const struct lookup *lookup)
6327 const struct dwrite_fonttable *gpos = &context->table->table;
6328 static const unsigned short gpos_formats[] =
6330 0, /* Unused */
6331 2, /* SinglePos */
6332 2, /* PairPos */
6333 1, /* CursivePos */
6334 1, /* MarkBasePos */
6335 1, /* MarkLigPos */
6336 1, /* MarkMarkPos */
6337 3, /* ContextPos */
6338 3, /* ChainContextPos */
6339 0, /* Extension, unused */
6341 unsigned int i, coverage, lookup_type, format;
6343 for (i = 0; i < lookup->subtable_count; ++i)
6345 unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup, i, &lookup_type);
6347 format = table_read_be_word(gpos, subtable_offset);
6349 if (!format || format > ARRAY_SIZE(gpos_formats) || format > gpos_formats[lookup_type])
6350 break;
6352 coverage = table_read_be_word(gpos, subtable_offset + 2);
6354 switch (lookup_type)
6356 case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
6357 case GPOS_LOOKUP_PAIR_ADJUSTMENT:
6358 case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
6359 case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
6360 case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
6361 case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
6363 if (opentype_layout_is_glyph_covered(gpos, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
6364 return TRUE;
6366 break;
6368 case GPOS_LOOKUP_CONTEXTUAL_POSITION:
6370 if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6371 return TRUE;
6373 break;
6375 case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
6377 if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
6378 return TRUE;
6380 break;
6382 default:
6383 WARN("Unknown lookup type %u.\n", lookup_type);
6387 return FALSE;
6390 typedef BOOL (*p_lookup_is_glyph_covered_func)(struct scriptshaping_context *context, UINT16 glyph, const struct lookup *lookup);
6392 BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index,
6393 unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count,
6394 const UINT16 *glyphs, UINT8 *feature_applies)
6396 p_lookup_is_glyph_covered_func func_is_covered;
6397 struct shaping_features features = { 0 };
6398 struct lookups lookups = { 0 };
6399 BOOL ret = FALSE, is_covered;
6400 unsigned int i, j, applies;
6402 features.features = feature;
6403 features.count = 1;
6405 for (i = 0; i < context->glyph_count; ++i)
6406 opentype_set_glyph_props(context, i);
6408 opentype_layout_collect_lookups(context, script_index, language_index, &features, context->table, &lookups);
6410 func_is_covered = opentype_layout_is_subst_context(context) ? opentype_layout_gsub_lookup_is_glyph_covered :
6411 opentype_layout_gpos_lookup_is_glyph_covered;
6413 for (i = 0; i < lookups.count; ++i)
6415 struct lookup *lookup = &lookups.lookups[i];
6417 applies = 0;
6418 for (j = 0; j < context->glyph_count; ++j)
6420 if (lookup_is_glyph_match(context, j, lookup->flags))
6422 if ((is_covered = func_is_covered(context, glyphs[i], lookup)))
6423 ++applies;
6424 feature_applies[j] |= is_covered;
6428 if ((ret = (applies == context->glyph_count)))
6429 break;
6432 free(lookups.lookups);
6434 return ret;
6437 BOOL opentype_has_vertical_variants(struct dwrite_fontface *fontface)
6439 unsigned int i, j, count = 0, lookup_type, subtable_offset;
6440 struct shaping_features features = { 0 };
6441 struct shaping_feature vert_feature = { 0 };
6442 struct scriptshaping_context context = { 0 };
6443 struct lookups lookups = { 0 };
6444 UINT16 format;
6446 if (fontface->flags & (FONTFACE_VERTICAL_VARIANTS | FONTFACE_NO_VERTICAL_VARIANTS))
6447 return !!(fontface->flags & FONTFACE_VERTICAL_VARIANTS);
6449 context.cache = fontface_get_shaping_cache(fontface);
6450 context.table = &context.cache->gsub;
6452 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6453 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6454 vert_feature.max_value = 1;
6455 vert_feature.default_value = 1;
6457 features.features = &vert_feature;
6458 features.count = features.capacity = 1;
6460 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6462 for (i = 0; i < lookups.count && !count; ++i)
6464 const struct dwrite_fonttable *table = &context.table->table;
6465 const struct lookup *lookup = &lookups.lookups[i];
6467 for (j = 0; j < lookup->subtable_count && !count; ++j)
6469 subtable_offset = opentype_layout_get_gsubgpos_subtable(&context, lookup, j, &lookup_type);
6471 if (lookup_type != GSUB_LOOKUP_SINGLE_SUBST)
6472 continue;
6474 format = table_read_be_word(table, subtable_offset);
6476 if (format == 1)
6478 count = 1;
6480 else if (format == 2)
6482 count = table_read_be_word(table, subtable_offset + FIELD_OFFSET(struct ot_gsub_singlesubst_format2, count));
6484 else
6485 WARN("Unrecognized single substitution format %u.\n", format);
6489 free(lookups.lookups);
6491 if (count)
6492 fontface->flags |= FONTFACE_VERTICAL_VARIANTS;
6493 else
6494 fontface->flags |= FONTFACE_NO_VERTICAL_VARIANTS;
6496 return !!(fontface->flags & FONTFACE_VERTICAL_VARIANTS);
6499 HRESULT opentype_get_vertical_glyph_variants(struct dwrite_fontface *fontface, unsigned int glyph_count,
6500 const UINT16 *nominal_glyphs, UINT16 *glyphs)
6502 struct shaping_features features = { 0 };
6503 struct shaping_feature vert_feature = { 0 };
6504 struct scriptshaping_context context = { 0 };
6505 struct lookups lookups = { 0 };
6506 unsigned int i;
6508 memcpy(glyphs, nominal_glyphs, glyph_count * sizeof(*glyphs));
6510 if (!opentype_has_vertical_variants(fontface))
6511 return S_OK;
6513 context.cache = fontface_get_shaping_cache(fontface);
6514 context.u.subst.glyphs = glyphs;
6515 context.u.subst.glyph_props = calloc(glyph_count, sizeof(*context.u.subst.glyph_props));
6516 context.u.subst.max_glyph_count = glyph_count;
6517 context.u.subst.capacity = glyph_count;
6518 context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos));
6519 context.table = &context.cache->gsub;
6521 vert_feature.tag = DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t');
6522 vert_feature.flags = FEATURE_GLOBAL | FEATURE_GLOBAL_SEARCH;
6523 vert_feature.max_value = 1;
6524 vert_feature.default_value = 1;
6526 features.features = &vert_feature;
6527 features.count = features.capacity = 1;
6529 opentype_layout_collect_lookups(&context, ~0u, ~0u, &features, context.table, &lookups);
6530 opentype_layout_set_glyph_masks(&context, &features);
6532 for (i = 0; i < lookups.count; ++i)
6534 const struct lookup *lookup = &lookups.lookups[i];
6536 context.cur = 0;
6537 while (context.cur < context.glyph_count)
6539 BOOL ret = FALSE;
6541 if (lookup_is_glyph_match(&context, context.cur, lookup->flags))
6542 ret = opentype_layout_apply_gsub_lookup(&context, lookup);
6544 if (!ret)
6545 context.cur++;
6549 free(context.u.subst.glyph_props);
6550 free(context.glyph_infos);
6551 free(lookups.lookups);
6553 return S_OK;
6556 BOOL opentype_has_kerning_pairs(struct dwrite_fontface *fontface)
6558 const struct kern_subtable_header *subtable;
6559 struct file_stream_desc stream_desc;
6560 const struct kern_header *header;
6561 unsigned int offset, count, i;
6563 if (fontface->flags & (FONTFACE_KERNING_PAIRS | FONTFACE_NO_KERNING_PAIRS))
6564 return !!(fontface->flags & FONTFACE_KERNING_PAIRS);
6566 fontface->flags |= FONTFACE_NO_KERNING_PAIRS;
6568 stream_desc.stream = fontface->stream;
6569 stream_desc.face_type = fontface->type;
6570 stream_desc.face_index = fontface->index;
6572 opentype_get_font_table(&stream_desc, MS_KERN_TAG, &fontface->kern);
6573 if (fontface->kern.exists)
6575 if ((header = table_read_ensure(&fontface->kern, 0, sizeof(*header))))
6577 count = GET_BE_WORD(header->table_count);
6578 offset = sizeof(*header);
6580 /* FreeType limits table count this way. */
6581 count = min(count, 32);
6583 /* Check for presence of format 0 subtable with horizontal coverage. */
6584 for (i = 0; i < count; ++i)
6586 if (!(subtable = table_read_ensure(&fontface->kern, offset, sizeof(*subtable))))
6587 break;
6589 if (subtable->version == 0 && GET_BE_WORD(subtable->coverage) & 1)
6591 fontface->flags &= ~FONTFACE_NO_KERNING_PAIRS;
6592 fontface->flags |= FONTFACE_KERNING_PAIRS;
6593 break;
6596 offset += GET_BE_WORD(subtable->length);
6601 if (fontface->flags & FONTFACE_NO_KERNING_PAIRS && fontface->kern.data)
6602 IDWriteFontFileStream_ReleaseFileFragment(stream_desc.stream, fontface->kern.context);
6604 return !!(fontface->flags & FONTFACE_KERNING_PAIRS);
6607 struct kern_format0_compare_key
6609 UINT16 left;
6610 UINT16 right;
6613 static int __cdecl kern_format0_compare(const void *a, const void *b)
6615 const struct kern_format0_compare_key *key = a;
6616 const WORD *data = b;
6617 UINT16 left = GET_BE_WORD(data[0]), right = GET_BE_WORD(data[1]);
6618 int ret;
6620 if ((ret = (int)key->left - (int)left)) return ret;
6621 if ((ret = (int)key->right - (int)right)) return ret;
6622 return 0;
6625 HRESULT opentype_get_kerning_pairs(struct dwrite_fontface *fontface, unsigned int count,
6626 const UINT16 *glyphs, INT32 *values)
6628 const struct kern_subtable_header *subtable;
6629 unsigned int i, s, offset, pair_count, subtable_count;
6630 struct kern_format0_compare_key key;
6631 const struct kern_header *header;
6632 const WORD *data;
6634 if (!opentype_has_kerning_pairs(fontface))
6636 memset(values, 0, count * sizeof(*values));
6637 return S_OK;
6640 subtable_count = table_read_be_word(&fontface->kern, 2);
6641 subtable_count = min(subtable_count, 32);
6643 for (i = 0; i < count - 1; ++i)
6645 offset = sizeof(*header);
6647 key.left = glyphs[i];
6648 key.right = glyphs[i + 1];
6649 values[i] = 0;
6651 for (s = 0; s < subtable_count; ++s)
6653 if (!(subtable = table_read_ensure(&fontface->kern, offset, sizeof(*subtable))))
6654 break;
6656 if (subtable->version == 0 && GET_BE_WORD(subtable->coverage) & 1)
6658 if ((data = table_read_ensure(&fontface->kern, offset, GET_BE_WORD(subtable->length))))
6660 /* Skip subtable header */
6661 data += 3;
6662 pair_count = GET_BE_WORD(*data);
6663 data += 4;
6664 /* Move to pair data */
6665 if ((data = table_read_ensure(&fontface->kern, offset + 7 * sizeof(*data),
6666 pair_count * 3 * sizeof(*data))))
6668 if ((data = bsearch(&key, data, pair_count, 3 * sizeof(*data), kern_format0_compare)))
6670 values[i] = (short)GET_BE_WORD(data[2]);
6671 break;
6677 offset += GET_BE_WORD(subtable->length);
6680 values[count - 1] = 0;
6682 return S_OK;
6685 static void opentype_font_var_add_static_axis(struct dwrite_var_axis **axis, unsigned int *axis_count,
6686 unsigned int tag, float value)
6688 struct dwrite_var_axis *entry = &(*axis)[(*axis_count)++];
6689 entry->tag = tag;
6690 entry->min_value = entry->max_value = entry->default_value = value;
6691 entry->attributes = 0;
6694 HRESULT opentype_get_font_var_axis(const struct file_stream_desc *stream_desc, struct dwrite_var_axis **axis,
6695 unsigned int *axis_count)
6697 static const float width_axis_values[] =
6699 0.0f, /* DWRITE_FONT_STRETCH_UNDEFINED */
6700 50.0f, /* DWRITE_FONT_STRETCH_ULTRA_CONDENSED */
6701 62.5f, /* DWRITE_FONT_STRETCH_EXTRA_CONDENSED */
6702 75.0f, /* DWRITE_FONT_STRETCH_CONDENSED */
6703 87.5f, /* DWRITE_FONT_STRETCH_SEMI_CONDENSED */
6704 100.0f, /* DWRITE_FONT_STRETCH_NORMAL */
6705 112.5f, /* DWRITE_FONT_STRETCH_SEMI_EXPANDED */
6706 125.0f, /* DWRITE_FONT_STRETCH_EXPANDED */
6707 150.0f, /* DWRITE_FONT_STRETCH_EXTRA_EXPANDED */
6708 200.0f, /* DWRITE_FONT_STRETCH_ULTRA_EXPANDED */
6710 BOOL has_wght = FALSE, has_wdth = FALSE, has_slnt = FALSE, has_ital = FALSE;
6711 const struct var_axis_record *records;
6712 const struct fvar_header *header;
6713 unsigned int i, count, tag, size;
6714 struct dwrite_font_props props;
6715 struct dwrite_fonttable fvar;
6716 HRESULT hr = S_OK;
6718 *axis = NULL;
6719 *axis_count = 0;
6721 opentype_get_font_table(stream_desc, MS_FVAR_TAG, &fvar);
6723 if (!(header = table_read_ensure(&fvar, 0, sizeof(*header)))) goto done;
6724 if (!(GET_BE_WORD(header->major_version) == 1 && GET_BE_WORD(header->minor_version) == 0))
6726 WARN("Unexpected fvar version.\n");
6727 goto done;
6730 count = GET_BE_WORD(header->axis_count);
6731 size = GET_BE_WORD(header->axis_size);
6733 if (!count || size != sizeof(*records)) goto done;
6734 if (!(records = table_read_ensure(&fvar, GET_BE_WORD(header->axes_array_offset), size * count))) goto done;
6736 if (!(*axis = calloc(count + 4, sizeof(**axis))))
6738 hr = E_OUTOFMEMORY;
6739 goto done;
6742 for (i = 0; i < count; ++i)
6744 (*axis)[i].tag = tag = records[i].tag;
6745 (*axis)[i].default_value = GET_BE_FIXED(records[i].default_value);
6746 (*axis)[i].min_value = GET_BE_FIXED(records[i].min_value);
6747 (*axis)[i].max_value = GET_BE_FIXED(records[i].max_value);
6748 if (GET_BE_WORD(records[i].flags & 0x1))
6749 (*axis)[i].attributes |= DWRITE_FONT_AXIS_ATTRIBUTES_HIDDEN;
6750 /* FIXME: set DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE */
6752 if (tag == DWRITE_FONT_AXIS_TAG_WEIGHT) has_wght = TRUE;
6753 if (tag == DWRITE_FONT_AXIS_TAG_WIDTH) has_wdth = TRUE;
6754 if (tag == DWRITE_FONT_AXIS_TAG_SLANT) has_slnt = TRUE;
6755 if (tag == DWRITE_FONT_AXIS_TAG_ITALIC) has_ital = TRUE;
6758 if (!has_wght || !has_wdth || !has_slnt || !has_ital)
6760 opentype_get_font_properties(stream_desc, &props);
6761 if (!has_wght) opentype_font_var_add_static_axis(axis, &count, DWRITE_FONT_AXIS_TAG_WEIGHT, props.weight);
6762 if (!has_ital) opentype_font_var_add_static_axis(axis, &count, DWRITE_FONT_AXIS_TAG_ITALIC,
6763 props.style == DWRITE_FONT_STYLE_ITALIC ? 1.0f : 0.0f);
6764 if (!has_wdth) opentype_font_var_add_static_axis(axis, &count, DWRITE_FONT_AXIS_TAG_WIDTH,
6765 width_axis_values[props.stretch]);
6766 if (!has_slnt) opentype_font_var_add_static_axis(axis, &count, DWRITE_FONT_AXIS_TAG_SLANT, props.slant_angle);
6769 *axis_count = count;
6771 done:
6772 if (fvar.context)
6773 IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, fvar.context);
6775 return hr;