dwrite: Implement GetRecommendedRenderingMode().
[wine.git] / dlls / dwrite / opentype.c
blob3cb8afba8ea35d0d479bca885c6d73af8ce54b76
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
23 #include "dwrite_private.h"
24 #include "winternl.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
28 #define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d')
29 #define MS_HHEA_TAG DWRITE_MAKE_OPENTYPE_TAG('h','h','e','a')
30 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
31 #define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2')
32 #define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t')
33 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
34 #define MS_GPOS_TAG DWRITE_MAKE_OPENTYPE_TAG('G','P','O','S')
35 #define MS_GSUB_TAG DWRITE_MAKE_OPENTYPE_TAG('G','S','U','B')
37 #ifdef WORDS_BIGENDIAN
38 #define GET_BE_WORD(x) (x)
39 #define GET_BE_DWORD(x) (x)
40 #else
41 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
42 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
43 #endif
45 typedef struct {
46 CHAR TTCTag[4];
47 DWORD Version;
48 DWORD numFonts;
49 DWORD OffsetTable[1];
50 } TTC_Header_V1;
52 typedef struct {
53 DWORD version;
54 WORD numTables;
55 WORD searchRange;
56 WORD entrySelector;
57 WORD rangeShift;
58 } TTC_SFNT_V1;
60 typedef struct {
61 CHAR tag[4];
62 DWORD checkSum;
63 DWORD offset;
64 DWORD length;
65 } TT_TableRecord;
67 typedef struct {
68 WORD platformID;
69 WORD encodingID;
70 DWORD offset;
71 } CMAP_EncodingRecord;
73 typedef struct {
74 WORD version;
75 WORD numTables;
76 CMAP_EncodingRecord tables[1];
77 } CMAP_Header;
79 typedef struct {
80 DWORD startCharCode;
81 DWORD endCharCode;
82 DWORD startGlyphID;
83 } CMAP_SegmentedCoverage_group;
85 typedef struct {
86 WORD format;
87 WORD reserved;
88 DWORD length;
89 DWORD language;
90 DWORD nGroups;
91 CMAP_SegmentedCoverage_group groups[1];
92 } CMAP_SegmentedCoverage;
94 typedef struct {
95 WORD format;
96 WORD length;
97 WORD language;
98 WORD segCountX2;
99 WORD searchRange;
100 WORD entrySelector;
101 WORD rangeShift;
102 WORD endCode[1];
103 } CMAP_SegmentMapping_0;
105 enum OPENTYPE_CMAP_TABLE_FORMAT
107 OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING = 4,
108 OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE = 12
111 /* PANOSE is 10 bytes in size, need to pack the structure properly */
112 #include "pshpack2.h"
113 typedef struct
115 ULONG version;
116 ULONG revision;
117 ULONG checksumadj;
118 ULONG magic;
119 USHORT flags;
120 USHORT unitsPerEm;
121 ULONGLONG created;
122 ULONGLONG modified;
123 SHORT xMin;
124 SHORT yMin;
125 SHORT xMax;
126 SHORT yMax;
127 USHORT macStyle;
128 USHORT lowestRecPPEM;
129 SHORT direction_hint;
130 SHORT index_format;
131 SHORT glyphdata_format;
132 } TT_HEAD;
134 typedef struct
136 ULONG Version;
137 ULONG italicAngle;
138 SHORT underlinePosition;
139 SHORT underlineThickness;
140 ULONG fixed_pitch;
141 ULONG minmemType42;
142 ULONG maxmemType42;
143 ULONG minmemType1;
144 ULONG maxmemType1;
145 } TT_POST;
147 typedef struct
149 USHORT version;
150 SHORT xAvgCharWidth;
151 USHORT usWeightClass;
152 USHORT usWidthClass;
153 SHORT fsType;
154 SHORT ySubscriptXSize;
155 SHORT ySubscriptYSize;
156 SHORT ySubscriptXOffset;
157 SHORT ySubscriptYOffset;
158 SHORT ySuperscriptXSize;
159 SHORT ySuperscriptYSize;
160 SHORT ySuperscriptXOffset;
161 SHORT ySuperscriptYOffset;
162 SHORT yStrikeoutSize;
163 SHORT yStrikeoutPosition;
164 SHORT sFamilyClass;
165 PANOSE panose;
166 ULONG ulUnicodeRange1;
167 ULONG ulUnicodeRange2;
168 ULONG ulUnicodeRange3;
169 ULONG ulUnicodeRange4;
170 CHAR achVendID[4];
171 USHORT fsSelection;
172 USHORT usFirstCharIndex;
173 USHORT usLastCharIndex;
174 /* According to the Apple spec, original version didn't have the below fields,
175 * version numbers were taken from the OpenType spec.
177 /* version 0 (TrueType 1.5) */
178 USHORT sTypoAscender;
179 USHORT sTypoDescender;
180 USHORT sTypoLineGap;
181 USHORT usWinAscent;
182 USHORT usWinDescent;
183 /* version 1 (TrueType 1.66) */
184 ULONG ulCodePageRange1;
185 ULONG ulCodePageRange2;
186 /* version 2 (OpenType 1.2) */
187 SHORT sxHeight;
188 SHORT sCapHeight;
189 USHORT usDefaultChar;
190 USHORT usBreakChar;
191 USHORT usMaxContext;
192 } TT_OS2_V2;
194 typedef struct {
195 ULONG version;
196 SHORT ascender;
197 SHORT descender;
198 SHORT linegap;
199 USHORT advanceWidthMax;
200 SHORT minLeftSideBearing;
201 SHORT minRightSideBearing;
202 SHORT xMaxExtent;
203 SHORT caretSlopeRise;
204 SHORT caretSlopeRun;
205 SHORT caretOffset;
206 SHORT reserved[4];
207 SHORT metricDataFormat;
208 USHORT numberOfHMetrics;
209 } TT_HHEA;
211 #include "poppack.h"
213 enum OS2_FSSELECTION {
214 OS2_FSSELECTION_ITALIC = 1 << 0,
215 OS2_FSSELECTION_UNDERSCORE = 1 << 1,
216 OS2_FSSELECTION_NEGATIVE = 1 << 2,
217 OS2_FSSELECTION_OUTLINED = 1 << 3,
218 OS2_FSSELECTION_STRIKEOUT = 1 << 4,
219 OS2_FSSELECTION_BOLD = 1 << 5,
220 OS2_FSSELECTION_REGULAR = 1 << 6,
221 OS2_FSSELECTION_USE_TYPO_METRICS = 1 << 7,
222 OS2_FSSELECTION_WWS = 1 << 8,
223 OS2_FSSELECTION_OBLIQUE = 1 << 9
226 typedef struct {
227 WORD platformID;
228 WORD encodingID;
229 WORD languageID;
230 WORD nameID;
231 WORD length;
232 WORD offset;
233 } TT_NameRecord;
235 typedef struct {
236 WORD format;
237 WORD count;
238 WORD stringOffset;
239 TT_NameRecord nameRecord[1];
240 } TT_NAME_V0;
242 struct VDMX_Header
244 WORD version;
245 WORD numRecs;
246 WORD numRatios;
249 struct VDMX_Ratio
251 BYTE bCharSet;
252 BYTE xRatio;
253 BYTE yStartRatio;
254 BYTE yEndRatio;
257 struct VDMX_group
259 WORD recs;
260 BYTE startsz;
261 BYTE endsz;
264 struct VDMX_vTable
266 WORD yPelHeight;
267 SHORT yMax;
268 SHORT yMin;
271 typedef struct {
272 CHAR FeatureTag[4];
273 WORD Feature;
274 } OT_FeatureRecord;
276 typedef struct {
277 WORD FeatureCount;
278 OT_FeatureRecord FeatureRecord[1];
279 } OT_FeatureList;
281 typedef struct {
282 WORD LookupOrder; /* Reserved */
283 WORD ReqFeatureIndex;
284 WORD FeatureCount;
285 WORD FeatureIndex[1];
286 } OT_LangSys;
288 typedef struct {
289 CHAR LangSysTag[4];
290 WORD LangSys;
291 } OT_LangSysRecord;
293 typedef struct {
294 WORD DefaultLangSys;
295 WORD LangSysCount;
296 OT_LangSysRecord LangSysRecord[1];
297 } OT_Script;
299 typedef struct {
300 CHAR ScriptTag[4];
301 WORD Script;
302 } OT_ScriptRecord;
304 typedef struct {
305 WORD ScriptCount;
306 OT_ScriptRecord ScriptRecord[1];
307 } OT_ScriptList;
309 typedef struct {
310 DWORD version;
311 WORD ScriptList;
312 WORD FeatureList;
313 WORD LookupList;
314 } GPOS_GSUB_Header;
316 enum OPENTYPE_PLATFORM_ID
318 OPENTYPE_PLATFORM_UNICODE = 0,
319 OPENTYPE_PLATFORM_MAC,
320 OPENTYPE_PLATFORM_ISO,
321 OPENTYPE_PLATFORM_WIN,
322 OPENTYPE_PLATFORM_CUSTOM
325 enum TT_NAME_WINDOWS_ENCODING_ID
327 TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
328 TT_NAME_WINDOWS_ENCODING_UCS2,
329 TT_NAME_WINDOWS_ENCODING_SJIS,
330 TT_NAME_WINDOWS_ENCODING_PRC,
331 TT_NAME_WINDOWS_ENCODING_BIG5,
332 TT_NAME_WINDOWS_ENCODING_WANSUNG,
333 TT_NAME_WINDOWS_ENCODING_JOHAB,
334 TT_NAME_WINDOWS_ENCODING_RESERVED1,
335 TT_NAME_WINDOWS_ENCODING_RESERVED2,
336 TT_NAME_WINDOWS_ENCODING_RESERVED3,
337 TT_NAME_WINDOWS_ENCODING_UCS4
340 enum TT_NAME_MAC_ENCODING_ID
342 TT_NAME_MAC_ENCODING_ROMAN = 0,
343 TT_NAME_MAC_ENCODING_JAPANESE,
344 TT_NAME_MAC_ENCODING_TRAD_CHINESE,
345 TT_NAME_MAC_ENCODING_KOREAN,
346 TT_NAME_MAC_ENCODING_ARABIC,
347 TT_NAME_MAC_ENCODING_HEBREW,
348 TT_NAME_MAC_ENCODING_GREEK,
349 TT_NAME_MAC_ENCODING_RUSSIAN,
350 TT_NAME_MAC_ENCODING_RSYMBOL,
351 TT_NAME_MAC_ENCODING_DEVANAGARI,
352 TT_NAME_MAC_ENCODING_GURMUKHI,
353 TT_NAME_MAC_ENCODING_GUJARATI,
354 TT_NAME_MAC_ENCODING_ORIYA,
355 TT_NAME_MAC_ENCODING_BENGALI,
356 TT_NAME_MAC_ENCODING_TAMIL,
357 TT_NAME_MAC_ENCODING_TELUGU,
358 TT_NAME_MAC_ENCODING_KANNADA,
359 TT_NAME_MAC_ENCODING_MALAYALAM,
360 TT_NAME_MAC_ENCODING_SINHALESE,
361 TT_NAME_MAC_ENCODING_BURMESE,
362 TT_NAME_MAC_ENCODING_KHMER,
363 TT_NAME_MAC_ENCODING_THAI,
364 TT_NAME_MAC_ENCODING_LAOTIAN,
365 TT_NAME_MAC_ENCODING_GEORGIAN,
366 TT_NAME_MAC_ENCODING_ARMENIAN,
367 TT_NAME_MAC_ENCODING_SIMPL_CHINESE,
368 TT_NAME_MAC_ENCODING_TIBETAN,
369 TT_NAME_MAC_ENCODING_MONGOLIAN,
370 TT_NAME_MAC_ENCODING_GEEZ,
371 TT_NAME_MAC_ENCODING_SLAVIC,
372 TT_NAME_MAC_ENCODING_VIETNAMESE,
373 TT_NAME_MAC_ENCODING_SINDHI,
374 TT_NAME_MAC_ENCODING_UNINTERPRETED
377 enum TT_NAME_MAC_LANGUAGE_ID
379 TT_NAME_MAC_LANGID_ENGLISH = 0,
380 TT_NAME_MAC_LANGID_FRENCH,
381 TT_NAME_MAC_LANGID_GERMAN,
382 TT_NAME_MAC_LANGID_ITALIAN,
383 TT_NAME_MAC_LANGID_DUTCH,
384 TT_NAME_MAC_LANGID_SWEDISH,
385 TT_NAME_MAC_LANGID_SPANISH,
386 TT_NAME_MAC_LANGID_DANISH,
387 TT_NAME_MAC_LANGID_PORTUGUESE,
388 TT_NAME_MAC_LANGID_NORWEGIAN,
389 TT_NAME_MAC_LANGID_HEBREW,
390 TT_NAME_MAC_LANGID_JAPANESE,
391 TT_NAME_MAC_LANGID_ARABIC,
392 TT_NAME_MAC_LANGID_FINNISH,
393 TT_NAME_MAC_LANGID_GREEK,
394 TT_NAME_MAC_LANGID_ICELANDIC,
395 TT_NAME_MAC_LANGID_MALTESE,
396 TT_NAME_MAC_LANGID_TURKISH,
397 TT_NAME_MAC_LANGID_CROATIAN,
398 TT_NAME_MAC_LANGID_TRAD_CHINESE,
399 TT_NAME_MAC_LANGID_URDU,
400 TT_NAME_MAC_LANGID_HINDI,
401 TT_NAME_MAC_LANGID_THAI,
402 TT_NAME_MAC_LANGID_KOREAN,
403 TT_NAME_MAC_LANGID_LITHUANIAN,
404 TT_NAME_MAC_LANGID_POLISH,
405 TT_NAME_MAC_LANGID_HUNGARIAN,
406 TT_NAME_MAC_LANGID_ESTONIAN,
407 TT_NAME_MAC_LANGID_LATVIAN,
408 TT_NAME_MAC_LANGID_SAMI,
409 TT_NAME_MAC_LANGID_FAROESE,
410 TT_NAME_MAC_LANGID_FARSI,
411 TT_NAME_MAC_LANGID_RUSSIAN,
412 TT_NAME_MAC_LANGID_SIMPL_CHINESE,
413 TT_NAME_MAC_LANGID_FLEMISH,
414 TT_NAME_MAC_LANGID_GAELIC,
415 TT_NAME_MAC_LANGID_ALBANIAN,
416 TT_NAME_MAC_LANGID_ROMANIAN,
417 TT_NAME_MAC_LANGID_CZECH,
418 TT_NAME_MAC_LANGID_SLOVAK,
419 TT_NAME_MAC_LANGID_SLOVENIAN,
420 TT_NAME_MAC_LANGID_YIDDISH,
421 TT_NAME_MAC_LANGID_SERBIAN,
422 TT_NAME_MAC_LANGID_MACEDONIAN,
423 TT_NAME_MAC_LANGID_BULGARIAN,
424 TT_NAME_MAC_LANGID_UKRAINIAN,
425 TT_NAME_MAC_LANGID_BYELORUSSIAN,
426 TT_NAME_MAC_LANGID_UZBEK,
427 TT_NAME_MAC_LANGID_KAZAKH,
428 TT_NAME_MAC_LANGID_AZERB_CYR,
429 TT_NAME_MAC_LANGID_AZERB_ARABIC,
430 TT_NAME_MAC_LANGID_ARMENIAN,
431 TT_NAME_MAC_LANGID_GEORGIAN,
432 TT_NAME_MAC_LANGID_MOLDAVIAN,
433 TT_NAME_MAC_LANGID_KIRGHIZ,
434 TT_NAME_MAC_LANGID_TAJIKI,
435 TT_NAME_MAC_LANGID_TURKMEN,
436 TT_NAME_MAC_LANGID_MONGOLIAN,
437 TT_NAME_MAC_LANGID_MONGOLIAN_CYR,
438 TT_NAME_MAC_LANGID_PASHTO,
439 TT_NAME_MAC_LANGID_KURDISH,
440 TT_NAME_MAC_LANGID_KASHMIRI,
441 TT_NAME_MAC_LANGID_SINDHI,
442 TT_NAME_MAC_LANGID_TIBETAN,
443 TT_NAME_MAC_LANGID_NEPALI,
444 TT_NAME_MAC_LANGID_SANSKRIT,
445 TT_NAME_MAC_LANGID_MARATHI,
446 TT_NAME_MAC_LANGID_BENGALI,
447 TT_NAME_MAC_LANGID_ASSAMESE,
448 TT_NAME_MAC_LANGID_GUJARATI,
449 TT_NAME_MAC_LANGID_PUNJABI,
450 TT_NAME_MAC_LANGID_ORIYA,
451 TT_NAME_MAC_LANGID_MALAYALAM,
452 TT_NAME_MAC_LANGID_KANNADA,
453 TT_NAME_MAC_LANGID_TAMIL,
454 TT_NAME_MAC_LANGID_TELUGU,
455 TT_NAME_MAC_LANGID_SINHALESE,
456 TT_NAME_MAC_LANGID_BURMESE,
457 TT_NAME_MAC_LANGID_KHMER,
458 TT_NAME_MAC_LANGID_LAO,
459 TT_NAME_MAC_LANGID_VIETNAMESE,
460 TT_NAME_MAC_LANGID_INDONESIAN,
461 TT_NAME_MAC_LANGID_TAGALONG,
462 TT_NAME_MAC_LANGID_MALAY_ROMAN,
463 TT_NAME_MAC_LANGID_MALAY_ARABIC,
464 TT_NAME_MAC_LANGID_AMHARIC,
465 TT_NAME_MAC_LANGID_TIGRINYA,
466 TT_NAME_MAC_LANGID_GALLA,
467 TT_NAME_MAC_LANGID_SOMALI,
468 TT_NAME_MAC_LANGID_SWAHILI,
469 TT_NAME_MAC_LANGID_KINYARWANDA,
470 TT_NAME_MAC_LANGID_RUNDI,
471 TT_NAME_MAC_LANGID_NYANJA,
472 TT_NAME_MAC_LANGID_MALAGASY,
473 TT_NAME_MAC_LANGID_ESPERANTO,
474 TT_NAME_MAC_LANGID_WELSH,
475 TT_NAME_MAC_LANGID_BASQUE,
476 TT_NAME_MAC_LANGID_CATALAN,
477 TT_NAME_MAC_LANGID_LATIN,
478 TT_NAME_MAC_LANGID_QUENCHUA,
479 TT_NAME_MAC_LANGID_GUARANI,
480 TT_NAME_MAC_LANGID_AYMARA,
481 TT_NAME_MAC_LANGID_TATAR,
482 TT_NAME_MAC_LANGID_UIGHUR,
483 TT_NAME_MAC_LANGID_DZONGKHA,
484 TT_NAME_MAC_LANGID_JAVANESE,
485 TT_NAME_MAC_LANGID_SUNDANESE,
486 TT_NAME_MAC_LANGID_GALICIAN,
487 TT_NAME_MAC_LANGID_AFRIKAANS,
488 TT_NAME_MAC_LANGID_BRETON,
489 TT_NAME_MAC_LANGID_INUKTITUT,
490 TT_NAME_MAC_LANGID_SCOTTISH_GAELIC,
491 TT_NAME_MAC_LANGID_MANX_GAELIC,
492 TT_NAME_MAC_LANGID_IRISH_GAELIC,
493 TT_NAME_MAC_LANGID_TONGAN,
494 TT_NAME_MAC_LANGID_GREEK_POLYTONIC,
495 TT_NAME_MAC_LANGID_GREENLANDIC,
496 TT_NAME_MAC_LANGID_AZER_ROMAN
499 /* Names are indexed with TT_NAME_MAC_LANGUAGE_ID values */
500 static const char name_mac_langid_to_locale[][10] = {
501 "en-US",
502 "fr-FR",
503 "de-DE",
504 "it-IT",
505 "nl-NL",
506 "sv-SE",
507 "es-ES",
508 "da-DA",
509 "pt-PT",
510 "no-NO",
511 "he-IL",
512 "ja-JP",
513 "ar-AR",
514 "fi-FI",
515 "el-GR",
516 "is-IS",
517 "mt-MT",
518 "tr-TR",
519 "hr-HR",
520 "zh-HK",
521 "ur-PK",
522 "hi-IN",
523 "th-TH",
524 "ko-KR",
525 "lt-LT",
526 "pl-PL",
527 "hu-HU",
528 "et-EE",
529 "lv-LV",
530 "se-NO",
531 "fo-FO",
532 "fa-IR",
533 "ru-RU",
534 "zh-CN",
535 "nl-BE",
536 "gd-GB",
537 "sq-AL",
538 "ro-RO",
539 "cs-CZ",
540 "sk-SK",
541 "sl-SI",
543 "sr-Latn",
544 "mk-MK",
545 "bg-BG",
546 "uk-UA",
547 "be-BY",
548 "uz-Latn",
549 "kk-KZ",
550 "az-Cyrl-AZ",
551 "az-AZ",
552 "hy-AM",
553 "ka-GE",
556 "tg-TJ",
557 "tk-TM",
558 "mn-Mong",
559 "mn-MN",
560 "ps-AF",
561 "ku-Arab",
563 "sd-Arab",
564 "bo-CN",
565 "ne-NP",
566 "sa-IN",
567 "mr-IN",
568 "bn-IN",
569 "as-IN",
570 "gu-IN",
571 "pa-Arab",
572 "or-IN",
573 "ml-IN",
574 "kn-IN",
575 "ta-LK",
576 "te-IN",
577 "si-LK",
579 "km-KH",
580 "lo-LA",
581 "vi-VN",
582 "id-ID",
584 "ms-MY",
585 "ms-Arab",
586 "am-ET",
587 "ti-ET",
590 "sw-KE",
591 "rw-RW",
596 "cy-GB",
597 "eu-ES",
598 "ca-ES",
603 "tt-RU",
604 "ug-CN",
608 "gl-ES",
609 "af-ZA",
610 "br-FR",
611 "iu-Latn-CA",
612 "gd-GB",
614 "ga-IE",
617 "kl-GL",
618 "az-Latn"
621 enum OPENTYPE_STRING_ID
623 OPENTYPE_STRING_COPYRIGHT_NOTICE = 0,
624 OPENTYPE_STRING_FAMILY_NAME,
625 OPENTYPE_STRING_SUBFAMILY_NAME,
626 OPENTYPE_STRING_UNIQUE_IDENTIFIER,
627 OPENTYPE_STRING_FULL_FONTNAME,
628 OPENTYPE_STRING_VERSION_STRING,
629 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
630 OPENTYPE_STRING_TRADEMARK,
631 OPENTYPE_STRING_MANUFACTURER,
632 OPENTYPE_STRING_DESIGNER,
633 OPENTYPE_STRING_DESCRIPTION,
634 OPENTYPE_STRING_VENDOR_URL,
635 OPENTYPE_STRING_DESIGNER_URL,
636 OPENTYPE_STRING_LICENSE_DESCRIPTION,
637 OPENTYPE_STRING_LICENSE_INFO_URL,
638 OPENTYPE_STRING_RESERVED_ID15,
639 OPENTYPE_STRING_PREFERRED_FAMILY_NAME,
640 OPENTYPE_STRING_PREFERRED_SUBFAMILY_NAME,
641 OPENTYPE_STRING_COMPATIBLE_FULLNAME,
642 OPENTYPE_STRING_SAMPLE_TEXT,
643 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
644 OPENTYPE_STRING_WWS_FAMILY_NAME,
645 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
648 static const UINT16 dwriteid_to_opentypeid[DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME+1] =
650 (UINT16)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
651 OPENTYPE_STRING_COPYRIGHT_NOTICE,
652 OPENTYPE_STRING_VERSION_STRING,
653 OPENTYPE_STRING_TRADEMARK,
654 OPENTYPE_STRING_MANUFACTURER,
655 OPENTYPE_STRING_DESIGNER,
656 OPENTYPE_STRING_DESIGNER_URL,
657 OPENTYPE_STRING_DESCRIPTION,
658 OPENTYPE_STRING_VENDOR_URL,
659 OPENTYPE_STRING_LICENSE_DESCRIPTION,
660 OPENTYPE_STRING_LICENSE_INFO_URL,
661 OPENTYPE_STRING_FAMILY_NAME,
662 OPENTYPE_STRING_SUBFAMILY_NAME,
663 OPENTYPE_STRING_PREFERRED_FAMILY_NAME,
664 OPENTYPE_STRING_PREFERRED_SUBFAMILY_NAME,
665 OPENTYPE_STRING_SAMPLE_TEXT,
666 OPENTYPE_STRING_FULL_FONTNAME,
667 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
668 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
671 BOOL is_face_type_supported(DWRITE_FONT_FACE_TYPE type)
673 return (type == DWRITE_FONT_FACE_TYPE_CFF) ||
674 (type == DWRITE_FONT_FACE_TYPE_TRUETYPE) ||
675 (type == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) ||
676 (type == DWRITE_FONT_FACE_TYPE_RAW_CFF);
679 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, UINT32* font_count, DWRITE_FONT_FILE_TYPE *file_type, DWRITE_FONT_FACE_TYPE *face_type, BOOL *supported)
681 /* TODO: Do font validation */
682 DWRITE_FONT_FACE_TYPE face;
683 const void *font_data;
684 const char* tag;
685 void *context;
686 HRESULT hr;
688 hr = IDWriteFontFileStream_ReadFileFragment(stream, &font_data, 0, sizeof(TTC_Header_V1), &context);
689 if (FAILED(hr))
690 return hr;
692 tag = font_data;
693 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
694 face = DWRITE_FONT_FACE_TYPE_UNKNOWN;
695 *font_count = 0;
697 if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_TTCF_TAG)
699 const TTC_Header_V1 *header = font_data;
700 *font_count = GET_BE_DWORD(header->numFonts);
701 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE_COLLECTION;
702 face = DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION;
704 else if (GET_BE_DWORD(*(DWORD*)font_data) == 0x10000)
706 *font_count = 1;
707 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
708 face = DWRITE_FONT_FACE_TYPE_TRUETYPE;
710 else if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_OTTO_TAG)
712 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
713 face = DWRITE_FONT_FACE_TYPE_CFF;
716 if (face_type)
717 *face_type = face;
719 *supported = is_face_type_supported(face);
721 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
722 return S_OK;
725 HRESULT opentype_get_font_table(IDWriteFontFileStream *stream, DWRITE_FONT_FACE_TYPE type, UINT32 font_index, UINT32 tag,
726 const void **table_data, void **table_context, UINT32 *table_size, BOOL *found)
728 HRESULT hr;
729 TTC_SFNT_V1 *font_header = NULL;
730 void *sfnt_context;
731 TT_TableRecord *table_record = NULL;
732 void *table_record_context;
733 int table_count, table_offset = 0;
734 int i;
736 if (found) *found = FALSE;
737 if (table_size) *table_size = 0;
739 if (type == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) {
740 const TTC_Header_V1 *ttc_header;
741 void * ttc_context;
742 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
743 if (SUCCEEDED(hr)) {
744 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[0]);
745 if (font_index >= GET_BE_DWORD(ttc_header->numFonts))
746 hr = E_INVALIDARG;
747 else
748 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
749 IDWriteFontFileStream_ReleaseFileFragment(stream, ttc_context);
752 else
753 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
755 if (FAILED(hr))
756 return hr;
758 table_count = GET_BE_WORD(font_header->numTables);
759 table_offset += sizeof(*font_header);
760 for (i = 0; i < table_count; i++)
762 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&table_record, table_offset, sizeof(*table_record), &table_record_context);
763 if (FAILED(hr))
764 break;
765 if (DWRITE_MAKE_OPENTYPE_TAG(table_record->tag[0], table_record->tag[1], table_record->tag[2], table_record->tag[3]) == tag)
766 break;
767 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
768 table_offset += sizeof(*table_record);
771 IDWriteFontFileStream_ReleaseFileFragment(stream, sfnt_context);
772 if (SUCCEEDED(hr) && i < table_count)
774 int offset = GET_BE_DWORD(table_record->offset);
775 int length = GET_BE_DWORD(table_record->length);
776 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
778 if (found) *found = TRUE;
779 if (table_size) *table_size = length;
780 hr = IDWriteFontFileStream_ReadFileFragment(stream, table_data, offset, length, table_context);
783 return hr;
786 /**********
787 * CMAP
788 **********/
790 static UINT32 opentype_cmap_get_unicode_ranges_count(const CMAP_Header *CMAP_Table)
792 UINT32 count = 0;
793 int i;
795 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++) {
796 WORD type;
797 WORD *table;
799 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
800 continue;
802 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
803 type = GET_BE_WORD(*table);
804 TRACE("table type %i\n", type);
806 switch (type)
808 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
810 CMAP_SegmentMapping_0 *format = (CMAP_SegmentMapping_0*)table;
811 count += GET_BE_WORD(format->segCountX2)/2;
812 break;
814 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
816 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)table;
817 count += GET_BE_DWORD(format->nGroups);
818 break;
820 default:
821 FIXME("table type %i unhandled.\n", type);
825 return count;
828 HRESULT opentype_cmap_get_unicode_ranges(void *data, UINT32 max_count, DWRITE_UNICODE_RANGE *ranges, UINT32 *count)
830 CMAP_Header *CMAP_Table = data;
831 int i, k = 0;
833 if (!CMAP_Table)
834 return E_FAIL;
836 *count = opentype_cmap_get_unicode_ranges_count(CMAP_Table);
838 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables) && k < max_count; i++)
840 WORD type;
841 WORD *table;
842 int j;
844 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
845 continue;
847 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
848 type = GET_BE_WORD(*table);
849 TRACE("table type %i\n", type);
851 switch (type)
853 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
855 CMAP_SegmentMapping_0 *format = (CMAP_SegmentMapping_0*)table;
856 UINT16 segment_count = GET_BE_WORD(format->segCountX2)/2;
857 UINT16 *startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count));
859 for (j = 0; j < segment_count && GET_BE_WORD(format->endCode[j]) < 0xffff && k < max_count; j++, k++) {
860 ranges[k].first = GET_BE_WORD(startCode[j]);
861 ranges[k].last = GET_BE_WORD(format->endCode[j]);
863 break;
865 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
867 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)table;
868 for (j = 0; j < GET_BE_DWORD(format->nGroups) && k < max_count; j++, k++) {
869 ranges[k].first = GET_BE_DWORD(format->groups[j].startCharCode);
870 ranges[k].last = GET_BE_DWORD(format->groups[j].endCharCode);
872 break;
874 default:
875 FIXME("table type %i unhandled.\n", type);
879 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
882 void opentype_get_font_metrics(IDWriteFontFileStream *stream, DWRITE_FONT_FACE_TYPE face_type, UINT32 face_index,
883 DWRITE_FONT_METRICS1 *metrics, DWRITE_CARET_METRICS *caret)
885 void *os2_context, *head_context, *post_context, *hhea_context;
886 const TT_OS2_V2 *tt_os2;
887 const TT_HEAD *tt_head;
888 const TT_POST *tt_post;
889 const TT_HHEA *tt_hhea;
891 memset(metrics, 0, sizeof(*metrics));
893 opentype_get_font_table(stream, face_type, face_index, MS_OS2_TAG, (const void**)&tt_os2, &os2_context, NULL, NULL);
894 opentype_get_font_table(stream, face_type, face_index, MS_HEAD_TAG, (const void**)&tt_head, &head_context, NULL, NULL);
895 opentype_get_font_table(stream, face_type, face_index, MS_POST_TAG, (const void**)&tt_post, &post_context, NULL, NULL);
896 opentype_get_font_table(stream, face_type, face_index, MS_HHEA_TAG, (const void**)&tt_hhea, &hhea_context, NULL, NULL);
898 if (tt_head) {
899 metrics->designUnitsPerEm = GET_BE_WORD(tt_head->unitsPerEm);
900 metrics->glyphBoxLeft = GET_BE_WORD(tt_head->xMin);
901 metrics->glyphBoxTop = GET_BE_WORD(tt_head->yMax);
902 metrics->glyphBoxRight = GET_BE_WORD(tt_head->xMax);
903 metrics->glyphBoxBottom = GET_BE_WORD(tt_head->yMin);
906 if (caret) {
907 if (tt_hhea) {
908 caret->slopeRise = GET_BE_WORD(tt_hhea->caretSlopeRise);
909 caret->slopeRun = GET_BE_WORD(tt_hhea->caretSlopeRun);
910 caret->offset = GET_BE_WORD(tt_hhea->caretOffset);
912 else {
913 caret->slopeRise = 0;
914 caret->slopeRun = 0;
915 caret->offset = 0;
919 if (tt_os2) {
920 USHORT version = GET_BE_WORD(tt_os2->version);
922 metrics->ascent = GET_BE_WORD(tt_os2->usWinAscent);
923 metrics->descent = GET_BE_WORD(tt_os2->usWinDescent);
925 /* line gap is estimated using two sets of ascender/descender values and 'hhea' line gap */
926 if (tt_hhea) {
927 SHORT descender = (SHORT)GET_BE_WORD(tt_hhea->descender);
928 INT32 linegap;
930 linegap = GET_BE_WORD(tt_hhea->ascender) + abs(descender) + GET_BE_WORD(tt_hhea->linegap) -
931 metrics->ascent - metrics->descent;
932 metrics->lineGap = linegap > 0 ? linegap : 0;
935 metrics->strikethroughPosition = GET_BE_WORD(tt_os2->yStrikeoutPosition);
936 metrics->strikethroughThickness = GET_BE_WORD(tt_os2->yStrikeoutSize);
937 metrics->subscriptPositionX = GET_BE_WORD(tt_os2->ySubscriptXOffset);
938 /* Y offset is stored as positive offset below baseline */
939 metrics->subscriptPositionY = -GET_BE_WORD(tt_os2->ySubscriptYOffset);
940 metrics->subscriptSizeX = GET_BE_WORD(tt_os2->ySubscriptXSize);
941 metrics->subscriptSizeY = GET_BE_WORD(tt_os2->ySubscriptYSize);
942 metrics->superscriptPositionX = GET_BE_WORD(tt_os2->ySuperscriptXOffset);
943 metrics->superscriptPositionY = GET_BE_WORD(tt_os2->ySuperscriptYOffset);
944 metrics->superscriptSizeX = GET_BE_WORD(tt_os2->ySuperscriptXSize);
945 metrics->superscriptSizeY = GET_BE_WORD(tt_os2->ySuperscriptYSize);
947 /* version 2 fields */
948 if (version >= 2) {
949 metrics->capHeight = GET_BE_WORD(tt_os2->sCapHeight);
950 metrics->xHeight = GET_BE_WORD(tt_os2->sxHeight);
953 /* version 4 fields */
954 if (version >= 4) {
955 if (GET_BE_WORD(tt_os2->fsSelection) & OS2_FSSELECTION_USE_TYPO_METRICS) {
956 SHORT descent = GET_BE_WORD(tt_os2->sTypoDescender);
957 metrics->ascent = GET_BE_WORD(tt_os2->sTypoAscender);
958 metrics->descent = descent < 0 ? -descent : 0;
959 metrics->lineGap = GET_BE_WORD(tt_os2->sTypoLineGap);
960 metrics->hasTypographicMetrics = TRUE;
965 if (tt_post) {
966 metrics->underlinePosition = GET_BE_WORD(tt_post->underlinePosition);
967 metrics->underlineThickness = GET_BE_WORD(tt_post->underlineThickness);
970 /* estimate missing metrics */
971 if (metrics->xHeight == 0)
972 metrics->xHeight = metrics->designUnitsPerEm / 2;
973 if (metrics->capHeight == 0)
974 metrics->capHeight = metrics->designUnitsPerEm * 7 / 10;
976 if (tt_os2)
977 IDWriteFontFileStream_ReleaseFileFragment(stream, os2_context);
978 if (tt_head)
979 IDWriteFontFileStream_ReleaseFileFragment(stream, head_context);
980 if (tt_post)
981 IDWriteFontFileStream_ReleaseFileFragment(stream, post_context);
982 if (tt_hhea)
983 IDWriteFontFileStream_ReleaseFileFragment(stream, hhea_context);
986 void opentype_get_font_properties(IDWriteFontFileStream *stream, DWRITE_FONT_FACE_TYPE type, UINT32 index,
987 struct dwrite_font_props *props)
989 void *os2_context, *head_context;
990 const TT_OS2_V2 *tt_os2;
991 const TT_HEAD *tt_head;
993 opentype_get_font_table(stream, type, index, MS_OS2_TAG, (const void**)&tt_os2, &os2_context, NULL, NULL);
994 opentype_get_font_table(stream, type, index, MS_HEAD_TAG, (const void**)&tt_head, &head_context, NULL, NULL);
996 /* default stretch, weight and style to normal */
997 props->stretch = DWRITE_FONT_STRETCH_NORMAL;
998 props->weight = DWRITE_FONT_WEIGHT_NORMAL;
999 props->style = DWRITE_FONT_STYLE_NORMAL;
1000 memset(&props->panose, 0, sizeof(props->panose));
1002 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
1003 if (tt_os2) {
1004 if (GET_BE_WORD(tt_os2->usWidthClass) <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
1005 props->stretch = GET_BE_WORD(tt_os2->usWidthClass);
1007 props->weight = GET_BE_WORD(tt_os2->usWeightClass);
1008 memcpy(&props->panose, &tt_os2->panose, sizeof(props->panose));
1010 TRACE("stretch=%d, weight=%d\n", props->stretch, props->weight);
1013 if (tt_head) {
1014 USHORT macStyle = GET_BE_WORD(tt_head->macStyle);
1015 if (macStyle & 0x0002)
1016 props->style = DWRITE_FONT_STYLE_ITALIC;
1019 if (tt_os2)
1020 IDWriteFontFileStream_ReleaseFileFragment(stream, os2_context);
1021 if (tt_head)
1022 IDWriteFontFileStream_ReleaseFileFragment(stream, head_context);
1025 static UINT get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform, USHORT encoding)
1027 UINT codepage = 0;
1029 switch (platform) {
1030 case OPENTYPE_PLATFORM_UNICODE:
1031 break;
1032 case OPENTYPE_PLATFORM_MAC:
1033 switch (encoding)
1035 case TT_NAME_MAC_ENCODING_ROMAN:
1036 codepage = 10000;
1037 break;
1038 case TT_NAME_MAC_ENCODING_JAPANESE:
1039 codepage = 10001;
1040 break;
1041 case TT_NAME_MAC_ENCODING_TRAD_CHINESE:
1042 codepage = 10002;
1043 break;
1044 case TT_NAME_MAC_ENCODING_KOREAN:
1045 codepage = 10003;
1046 break;
1047 case TT_NAME_MAC_ENCODING_ARABIC:
1048 codepage = 10004;
1049 break;
1050 case TT_NAME_MAC_ENCODING_HEBREW:
1051 codepage = 10005;
1052 break;
1053 case TT_NAME_MAC_ENCODING_GREEK:
1054 codepage = 10006;
1055 break;
1056 case TT_NAME_MAC_ENCODING_RUSSIAN:
1057 codepage = 10007;
1058 break;
1059 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE:
1060 codepage = 10008;
1061 break;
1062 case TT_NAME_MAC_ENCODING_THAI:
1063 codepage = 10021;
1064 break;
1065 default:
1066 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
1067 break;
1069 break;
1070 case OPENTYPE_PLATFORM_WIN:
1071 switch (encoding)
1073 case TT_NAME_WINDOWS_ENCODING_SYMBOL:
1074 case TT_NAME_WINDOWS_ENCODING_UCS2:
1075 break;
1076 case TT_NAME_WINDOWS_ENCODING_SJIS:
1077 codepage = 932;
1078 break;
1079 case TT_NAME_WINDOWS_ENCODING_PRC:
1080 codepage = 936;
1081 break;
1082 case TT_NAME_WINDOWS_ENCODING_BIG5:
1083 codepage = 950;
1084 break;
1085 case TT_NAME_WINDOWS_ENCODING_WANSUNG:
1086 codepage = 20949;
1087 break;
1088 case TT_NAME_WINDOWS_ENCODING_JOHAB:
1089 codepage = 1361;
1090 break;
1091 default:
1092 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
1093 break;
1095 break;
1096 default:
1097 FIXME("unknown platform %d\n", platform);
1100 return codepage;
1103 static void get_name_record_locale(enum OPENTYPE_PLATFORM_ID platform, USHORT lang_id, WCHAR *locale, USHORT locale_len)
1105 static const WCHAR enusW[] = {'e','n','-','U','S',0};
1107 switch (platform) {
1108 case OPENTYPE_PLATFORM_MAC:
1110 const char *locale_name = NULL;
1112 if (lang_id > TT_NAME_MAC_LANGID_AZER_ROMAN)
1113 ERR("invalid mac lang id %d\n", lang_id);
1114 else if (!name_mac_langid_to_locale[lang_id][0])
1115 FIXME("failed to map mac lang id %d to locale name\n", lang_id);
1116 else
1117 locale_name = name_mac_langid_to_locale[lang_id];
1119 if (locale_name)
1120 MultiByteToWideChar(CP_ACP, 0, name_mac_langid_to_locale[lang_id], -1, locale, locale_len);
1121 else
1122 strcpyW(locale, enusW);
1123 break;
1125 case OPENTYPE_PLATFORM_WIN:
1126 if (!LCIDToLocaleName(MAKELCID(lang_id, SORT_DEFAULT), locale, locale_len, 0)) {
1127 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id, SORT_DEFAULT));
1128 strcpyW(locale, enusW);
1130 break;
1131 default:
1132 FIXME("unknown platform %d\n", platform);
1136 HRESULT opentype_get_font_strings_from_id(const void *table_data, DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **strings)
1138 const TT_NAME_V0 *header;
1139 BYTE *storage_area = 0;
1140 USHORT count = 0;
1141 UINT16 name_id;
1142 BOOL exists;
1143 HRESULT hr;
1144 int i;
1146 if (!table_data)
1147 return E_FAIL;
1149 hr = create_localizedstrings(strings);
1150 if (FAILED(hr)) return hr;
1152 header = table_data;
1154 switch (header->format) {
1155 case 0:
1156 break;
1157 default:
1158 FIXME("unsupported NAME format %d\n", header->format);
1161 storage_area = (LPBYTE)table_data + GET_BE_WORD(header->stringOffset);
1162 count = GET_BE_WORD(header->count);
1164 name_id = dwriteid_to_opentypeid[id];
1166 exists = FALSE;
1167 for (i = 0; i < count; i++) {
1168 const TT_NameRecord *record = &header->nameRecord[i];
1169 USHORT lang_id, length, offset, encoding, platform;
1171 if (GET_BE_WORD(record->nameID) != name_id)
1172 continue;
1174 exists = TRUE;
1176 /* Right now only accept unicode and windows encoded fonts */
1177 platform = GET_BE_WORD(record->platformID);
1178 if (platform != OPENTYPE_PLATFORM_UNICODE &&
1179 platform != OPENTYPE_PLATFORM_MAC &&
1180 platform != OPENTYPE_PLATFORM_WIN)
1182 FIXME("platform %i not supported\n", platform);
1183 continue;
1186 /* Skip such entries for now, as it's not clear which locale is implied when
1187 unicode platform is used. Also fonts tend to duplicate those strings as
1188 WIN platform entries. */
1189 if (platform == OPENTYPE_PLATFORM_UNICODE)
1190 continue;
1192 lang_id = GET_BE_WORD(record->languageID);
1193 length = GET_BE_WORD(record->length);
1194 offset = GET_BE_WORD(record->offset);
1195 encoding = GET_BE_WORD(record->encodingID);
1197 if (lang_id < 0x8000) {
1198 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
1199 WCHAR *name_string;
1200 UINT codepage;
1202 codepage = get_name_record_codepage(platform, encoding);
1203 get_name_record_locale(platform, lang_id, locale, sizeof(locale)/sizeof(WCHAR));
1205 if (codepage) {
1206 DWORD len = MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, NULL, 0);
1207 name_string = heap_alloc(sizeof(WCHAR) * (len+1));
1208 MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, name_string, len);
1209 name_string[len] = 0;
1211 else {
1212 int i;
1214 length /= sizeof(WCHAR);
1215 name_string = heap_strdupnW((LPWSTR)(storage_area + offset), length);
1216 for (i = 0; i < length; i++)
1217 name_string[i] = GET_BE_WORD(name_string[i]);
1220 TRACE("string %s for locale %s found\n", debugstr_w(name_string), debugstr_w(locale));
1221 add_localizedstring(*strings, locale, name_string);
1222 heap_free(name_string);
1224 else {
1225 FIXME("handle NAME format 1\n");
1226 continue;
1230 if (!exists) {
1231 IDWriteLocalizedStrings_Release(*strings);
1232 *strings = NULL;
1235 return hr;
1238 static inline const OT_Script *opentype_get_script(const OT_ScriptList *scriptlist, UINT32 scripttag)
1240 UINT16 j;
1242 for (j = 0; j < GET_BE_WORD(scriptlist->ScriptCount); j++) {
1243 const char *tag = scriptlist->ScriptRecord[j].ScriptTag;
1244 if (scripttag == DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]))
1245 return (OT_Script*)((BYTE*)scriptlist + GET_BE_WORD(scriptlist->ScriptRecord[j].Script));
1248 return NULL;
1251 static inline const OT_LangSys *opentype_get_langsys(const OT_Script *script, UINT32 languagetag)
1253 UINT16 j;
1255 for (j = 0; j < GET_BE_WORD(script->LangSysCount); j++) {
1256 const char *tag = script->LangSysRecord[j].LangSysTag;
1257 if (languagetag == DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]))
1258 return (OT_LangSys*)((BYTE*)script + GET_BE_WORD(script->LangSysRecord[j].LangSys));
1261 return NULL;
1264 static void opentype_add_font_features(const GPOS_GSUB_Header *header, const OT_LangSys *langsys,
1265 UINT32 max_tagcount, UINT32 *count, DWRITE_FONT_FEATURE_TAG *tags)
1267 const OT_FeatureList *features = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
1268 UINT16 j;
1270 for (j = 0; j < GET_BE_WORD(langsys->FeatureCount); j++) {
1271 const OT_FeatureRecord *feature = &features->FeatureRecord[langsys->FeatureIndex[j]];
1272 const char *tag = feature->FeatureTag;
1274 if (*count < max_tagcount)
1275 tags[*count] = DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]);
1277 (*count)++;
1281 HRESULT opentype_get_typographic_features(IDWriteFontFace *fontface, UINT32 scripttag, UINT32 languagetag, UINT32 max_tagcount,
1282 UINT32 *count, DWRITE_FONT_FEATURE_TAG *tags)
1284 UINT32 tables[2] = { MS_GSUB_TAG, MS_GPOS_TAG };
1285 HRESULT hr;
1286 UINT8 i;
1288 *count = 0;
1289 for (i = 0; i < sizeof(tables)/sizeof(tables[0]); i++) {
1290 const OT_ScriptList *scriptlist;
1291 const GPOS_GSUB_Header *header;
1292 const OT_Script *script;
1293 const void *ptr;
1294 void *context;
1295 UINT32 size;
1296 BOOL exists;
1298 exists = FALSE;
1299 hr = IDWriteFontFace_TryGetFontTable(fontface, tables[i], &ptr, &size, &context, &exists);
1300 if (FAILED(hr))
1301 return hr;
1303 if (!exists)
1304 continue;
1306 header = (const GPOS_GSUB_Header*)ptr;
1307 scriptlist = (const OT_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
1309 script = opentype_get_script(scriptlist, scripttag);
1310 if (script) {
1311 const OT_LangSys *langsys = opentype_get_langsys(script, languagetag);
1312 if (langsys)
1313 opentype_add_font_features(header, langsys, max_tagcount, count, tags);
1316 IDWriteFontFace_ReleaseFontTable(fontface, context);
1319 return *count > max_tagcount ? E_NOT_SUFFICIENT_BUFFER : S_OK;
1322 static const struct VDMX_group *find_vdmx_group(const struct VDMX_Header *hdr)
1324 WORD num_ratios, i, group_offset = 0;
1325 struct VDMX_Ratio *ratios = (struct VDMX_Ratio*)(hdr + 1);
1326 BYTE dev_x_ratio = 1, dev_y_ratio = 1;
1328 num_ratios = GET_BE_WORD(hdr->numRatios);
1330 for (i = 0; i < num_ratios; i++) {
1332 if (!ratios[i].bCharSet) continue;
1334 if ((ratios[i].xRatio == 0 && ratios[i].yStartRatio == 0 &&
1335 ratios[i].yEndRatio == 0) ||
1336 (ratios[i].xRatio == dev_x_ratio && ratios[i].yStartRatio <= dev_y_ratio &&
1337 ratios[i].yEndRatio >= dev_y_ratio))
1339 group_offset = GET_BE_WORD(*((WORD *)(ratios + num_ratios) + i));
1340 break;
1343 if (group_offset)
1344 return (const struct VDMX_group *)((BYTE *)hdr + group_offset);
1345 return NULL;
1348 BOOL opentype_get_vdmx_size(const void *data, INT emsize, UINT16 *ascent, UINT16 *descent)
1350 const struct VDMX_Header *hdr = (const struct VDMX_Header*)data;
1351 const struct VDMX_group *group;
1352 const struct VDMX_vTable *tables;
1353 WORD recs, i;
1355 if (!data)
1356 return FALSE;
1358 group = find_vdmx_group(hdr);
1359 if (!group)
1360 return FALSE;
1362 recs = GET_BE_WORD(group->recs);
1363 if (emsize < group->startsz || emsize >= group->endsz) return FALSE;
1365 tables = (const struct VDMX_vTable *)(group + 1);
1366 for (i = 0; i < recs; i++) {
1367 WORD ppem = GET_BE_WORD(tables[i].yPelHeight);
1368 if (ppem > emsize) {
1369 FIXME("interpolate %d\n", emsize);
1370 return FALSE;
1373 if (ppem == emsize) {
1374 *ascent = (SHORT)GET_BE_WORD(tables[i].yMax);
1375 *descent = -(SHORT)GET_BE_WORD(tables[i].yMin);
1376 return TRUE;
1379 return FALSE;
1382 WORD opentype_get_gasp_flags(const WORD *ptr, UINT32 size, INT emsize)
1384 WORD num_recs, version;
1385 WORD flags = 0;
1387 if (!ptr)
1388 return 0;
1390 version = GET_BE_WORD( *ptr++ );
1391 num_recs = GET_BE_WORD( *ptr++ );
1392 if (version > 1 || size < (num_recs * 2 + 2) * sizeof(WORD)) {
1393 ERR("unsupported gasp table: ver %d size %d recs %d\n", version, size, num_recs);
1394 goto done;
1397 while (num_recs--) {
1398 flags = GET_BE_WORD( *(ptr + 1) );
1399 if (emsize <= GET_BE_WORD( *ptr )) break;
1400 ptr += 2;
1403 done:
1404 return flags;