msvcrt: Avoid locking the file in _fclose_nolock.
[wine.git] / dlls / dwrite / opentype.c
blob6d48f9016b34848e42c8e3ad3624affa7042762f
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"
25 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
27 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
28 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
30 #ifdef WORDS_BIGENDIAN
31 #define GET_BE_WORD(x) (x)
32 #define GET_BE_DWORD(x) (x)
33 #else
34 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
35 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)))
36 #endif
38 typedef struct {
39 CHAR TTCTag[4];
40 DWORD Version;
41 DWORD numFonts;
42 DWORD OffsetTable[1];
43 } TTC_Header_V1;
45 typedef struct {
46 DWORD version;
47 WORD numTables;
48 WORD searchRange;
49 WORD entrySelector;
50 WORD rangeShift;
51 } TTC_SFNT_V1;
53 typedef struct {
54 CHAR tag[4];
55 DWORD checkSum;
56 DWORD offset;
57 DWORD length;
58 } TT_TableRecord;
60 typedef struct {
61 WORD platformID;
62 WORD encodingID;
63 DWORD offset;
64 } CMAP_EncodingRecord;
66 typedef struct {
67 WORD version;
68 WORD numTables;
69 CMAP_EncodingRecord tables[1];
70 } CMAP_Header;
72 typedef struct {
73 DWORD startCharCode;
74 DWORD endCharCode;
75 DWORD startGlyphID;
76 } CMAP_SegmentedCoverage_group;
78 typedef struct {
79 WORD format;
80 WORD reserved;
81 DWORD length;
82 DWORD language;
83 DWORD nGroups;
84 CMAP_SegmentedCoverage_group groups[1];
85 } CMAP_SegmentedCoverage;
87 typedef struct {
88 WORD format;
89 WORD length;
90 WORD language;
91 WORD segCountX2;
92 WORD searchRange;
93 WORD entrySelector;
94 WORD rangeShift;
95 WORD endCode[1];
96 } CMAP_SegmentMapping_0;
98 enum OPENTYPE_CMAP_TABLE_FORMAT
100 OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING = 4,
101 OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE = 12
104 /* PANOSE is 10 bytes in size, need to pack the structure properly */
105 #include "pshpack2.h"
106 typedef struct
108 ULONG version;
109 ULONG revision;
110 ULONG checksumadj;
111 ULONG magic;
112 USHORT flags;
113 USHORT unitsPerEm;
114 ULONGLONG created;
115 ULONGLONG modified;
116 SHORT xMin;
117 SHORT yMin;
118 SHORT xMax;
119 SHORT yMax;
120 USHORT macStyle;
121 USHORT lowestRecPPEM;
122 SHORT direction_hint;
123 SHORT index_format;
124 SHORT glyphdata_format;
125 } TT_HEAD;
127 typedef struct
129 ULONG Version;
130 ULONG italicAngle;
131 SHORT underlinePosition;
132 SHORT underlineThickness;
133 ULONG fixed_pitch;
134 ULONG minmemType42;
135 ULONG maxmemType42;
136 ULONG minmemType1;
137 ULONG maxmemType1;
138 } TT_POST;
140 typedef struct
142 USHORT version;
143 SHORT xAvgCharWidth;
144 USHORT usWeightClass;
145 USHORT usWidthClass;
146 SHORT fsType;
147 SHORT ySubscriptXSize;
148 SHORT ySubscriptYSize;
149 SHORT ySubscriptXOffset;
150 SHORT ySubscriptYOffset;
151 SHORT ySuperscriptXSize;
152 SHORT ySuperscriptYSize;
153 SHORT ySuperscriptXOffset;
154 SHORT ySuperscriptYOffset;
155 SHORT yStrikeoutSize;
156 SHORT yStrikeoutPosition;
157 SHORT sFamilyClass;
158 PANOSE panose;
159 ULONG ulUnicodeRange1;
160 ULONG ulUnicodeRange2;
161 ULONG ulUnicodeRange3;
162 ULONG ulUnicodeRange4;
163 CHAR achVendID[4];
164 USHORT fsSelection;
165 USHORT usFirstCharIndex;
166 USHORT usLastCharIndex;
167 /* According to the Apple spec, original version didn't have the below fields,
168 * version numbers were taken from the OpenType spec.
170 /* version 0 (TrueType 1.5) */
171 USHORT sTypoAscender;
172 USHORT sTypoDescender;
173 USHORT sTypoLineGap;
174 USHORT usWinAscent;
175 USHORT usWinDescent;
176 /* version 1 (TrueType 1.66) */
177 ULONG ulCodePageRange1;
178 ULONG ulCodePageRange2;
179 /* version 2 (OpenType 1.2) */
180 SHORT sxHeight;
181 SHORT sCapHeight;
182 USHORT usDefaultChar;
183 USHORT usBreakChar;
184 USHORT usMaxContext;
185 } TT_OS2_V2;
186 #include "poppack.h"
188 typedef struct {
189 WORD platformID;
190 WORD encodingID;
191 WORD languageID;
192 WORD nameID;
193 WORD length;
194 WORD offset;
195 } TT_NameRecord;
197 typedef struct {
198 WORD format;
199 WORD count;
200 WORD stringOffset;
201 TT_NameRecord nameRecord[1];
202 } TT_NAME_V0;
204 enum OPENTYPE_PLATFORM_ID
206 OPENTYPE_PLATFORM_UNICODE = 0,
207 OPENTYPE_PLATFORM_MAC,
208 OPENTYPE_PLATFORM_ISO,
209 OPENTYPE_PLATFORM_WIN,
210 OPENTYPE_PLATFORM_CUSTOM
213 enum TT_NAME_WINDOWS_ENCODING_ID
215 TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
216 TT_NAME_WINDOWS_ENCODING_UCS2,
217 TT_NAME_WINDOWS_ENCODING_SJIS,
218 TT_NAME_WINDOWS_ENCODING_PRC,
219 TT_NAME_WINDOWS_ENCODING_BIG5,
220 TT_NAME_WINDOWS_ENCODING_WANSUNG,
221 TT_NAME_WINDOWS_ENCODING_JOHAB,
222 TT_NAME_WINDOWS_ENCODING_RESERVED1,
223 TT_NAME_WINDOWS_ENCODING_RESERVED2,
224 TT_NAME_WINDOWS_ENCODING_RESERVED3,
225 TT_NAME_WINDOWS_ENCODING_UCS4
228 enum TT_NAME_MAC_ENCODING_ID
230 TT_NAME_MAC_ENCODING_ROMAN = 0,
231 TT_NAME_MAC_ENCODING_JAPANESE,
232 TT_NAME_MAC_ENCODING_TRAD_CHINESE,
233 TT_NAME_MAC_ENCODING_KOREAN,
234 TT_NAME_MAC_ENCODING_ARABIC,
235 TT_NAME_MAC_ENCODING_HEBREW,
236 TT_NAME_MAC_ENCODING_GREEK,
237 TT_NAME_MAC_ENCODING_RUSSIAN,
238 TT_NAME_MAC_ENCODING_RSYMBOL,
239 TT_NAME_MAC_ENCODING_DEVANAGARI,
240 TT_NAME_MAC_ENCODING_GURMUKHI,
241 TT_NAME_MAC_ENCODING_GUJARATI,
242 TT_NAME_MAC_ENCODING_ORIYA,
243 TT_NAME_MAC_ENCODING_BENGALI,
244 TT_NAME_MAC_ENCODING_TAMIL,
245 TT_NAME_MAC_ENCODING_TELUGU,
246 TT_NAME_MAC_ENCODING_KANNADA,
247 TT_NAME_MAC_ENCODING_MALAYALAM,
248 TT_NAME_MAC_ENCODING_SINHALESE,
249 TT_NAME_MAC_ENCODING_BURMESE,
250 TT_NAME_MAC_ENCODING_KHMER,
251 TT_NAME_MAC_ENCODING_THAI,
252 TT_NAME_MAC_ENCODING_LAOTIAN,
253 TT_NAME_MAC_ENCODING_GEORGIAN,
254 TT_NAME_MAC_ENCODING_ARMENIAN,
255 TT_NAME_MAC_ENCODING_SIMPL_CHINESE,
256 TT_NAME_MAC_ENCODING_TIBETAN,
257 TT_NAME_MAC_ENCODING_MONGOLIAN,
258 TT_NAME_MAC_ENCODING_GEEZ,
259 TT_NAME_MAC_ENCODING_SLAVIC,
260 TT_NAME_MAC_ENCODING_VIETNAMESE,
261 TT_NAME_MAC_ENCODING_SINDHI,
262 TT_NAME_MAC_ENCODING_UNINTERPRETED
265 enum OPENTYPE_STRING_ID
267 OPENTYPE_STRING_COPYRIGHT_NOTICE = 0,
268 OPENTYPE_STRING_FAMILY_NAME,
269 OPENTYPE_STRING_SUBFAMILY_NAME,
270 OPENTYPE_STRING_UNIQUE_IDENTIFIER,
271 OPENTYPE_STRING_FULL_FONTNAME,
272 OPENTYPE_STRING_VERSION_STRING,
273 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
274 OPENTYPE_STRING_TRADEMARK,
275 OPENTYPE_STRING_MANUFACTURER,
276 OPENTYPE_STRING_DESIGNER,
277 OPENTYPE_STRING_DESCRIPTION,
278 OPENTYPE_STRING_VENDOR_URL,
279 OPENTYPE_STRING_DESIGNER_URL,
280 OPENTYPE_STRING_LICENSE_DESCRIPTION,
281 OPENTYPE_STRING_LICENSE_INFO_URL,
282 OPENTYPE_STRING_RESERVED_ID15,
283 OPENTYPE_STRING_PREFERRED_FAMILY_NAME,
284 OPENTYPE_STRING_PREFERRED_SUBFAMILY_NAME,
285 OPENTYPE_STRING_COMPATIBLE_FULLNAME,
286 OPENTYPE_STRING_SAMPLE_TEXT,
287 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
288 OPENTYPE_STRING_WWS_FAMILY_NAME,
289 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
292 static const UINT16 dwriteid_to_opentypeid[DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME+1] =
294 (UINT16)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
295 OPENTYPE_STRING_COPYRIGHT_NOTICE,
296 OPENTYPE_STRING_VERSION_STRING,
297 OPENTYPE_STRING_TRADEMARK,
298 OPENTYPE_STRING_MANUFACTURER,
299 OPENTYPE_STRING_DESIGNER,
300 OPENTYPE_STRING_DESIGNER_URL,
301 OPENTYPE_STRING_DESCRIPTION,
302 OPENTYPE_STRING_VENDOR_URL,
303 OPENTYPE_STRING_LICENSE_DESCRIPTION,
304 OPENTYPE_STRING_LICENSE_INFO_URL,
305 OPENTYPE_STRING_FAMILY_NAME,
306 OPENTYPE_STRING_SUBFAMILY_NAME,
307 OPENTYPE_STRING_PREFERRED_FAMILY_NAME,
308 OPENTYPE_STRING_PREFERRED_SUBFAMILY_NAME,
309 OPENTYPE_STRING_SAMPLE_TEXT,
310 OPENTYPE_STRING_FULL_FONTNAME,
311 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
312 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
315 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, UINT32* font_count, DWRITE_FONT_FILE_TYPE *file_type, DWRITE_FONT_FACE_TYPE *face_type, BOOL *supported)
317 /* TODO: Do font validation */
318 const void *font_data;
319 const char* tag;
320 void *context;
321 HRESULT hr;
323 hr = IDWriteFontFileStream_ReadFileFragment(stream, &font_data, 0, sizeof(TTC_Header_V1), &context);
324 if (FAILED(hr))
325 return hr;
327 tag = font_data;
328 *supported = FALSE;
329 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
330 if (face_type)
331 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
332 *font_count = 0;
334 if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_TTCF_TAG)
336 const TTC_Header_V1 *header = font_data;
337 *font_count = GET_BE_DWORD(header->numFonts);
338 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE_COLLECTION;
339 if (face_type)
340 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION;
341 *supported = TRUE;
343 else if (GET_BE_DWORD(*(DWORD*)font_data) == 0x10000)
345 *font_count = 1;
346 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
347 if (face_type)
348 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
349 *supported = TRUE;
351 else if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_OTTO_TAG)
353 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
356 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
357 return S_OK;
360 HRESULT opentype_get_font_table(IDWriteFontFileStream *stream, DWRITE_FONT_FACE_TYPE type, UINT32 font_index, UINT32 tag,
361 const void **table_data, void **table_context, UINT32 *table_size, BOOL *found)
363 HRESULT hr;
364 TTC_SFNT_V1 *font_header = NULL;
365 void *sfnt_context;
366 TT_TableRecord *table_record = NULL;
367 void *table_record_context;
368 int table_count, table_offset = 0;
369 int i;
371 if (found) *found = FALSE;
372 if (table_size) *table_size = 0;
374 if (type == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) {
375 const TTC_Header_V1 *ttc_header;
376 void * ttc_context;
377 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
378 if (SUCCEEDED(hr)) {
379 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[0]);
380 if (font_index >= GET_BE_DWORD(ttc_header->numFonts))
381 hr = E_INVALIDARG;
382 else
383 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
384 IDWriteFontFileStream_ReleaseFileFragment(stream, ttc_context);
387 else
388 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
390 if (FAILED(hr))
391 return hr;
393 table_count = GET_BE_WORD(font_header->numTables);
394 table_offset += sizeof(*font_header);
395 for (i = 0; i < table_count; i++)
397 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&table_record, table_offset, sizeof(*table_record), &table_record_context);
398 if (FAILED(hr))
399 break;
400 if (DWRITE_MAKE_OPENTYPE_TAG(table_record->tag[0], table_record->tag[1], table_record->tag[2], table_record->tag[3]) == tag)
401 break;
402 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
403 table_offset += sizeof(*table_record);
406 IDWriteFontFileStream_ReleaseFileFragment(stream, sfnt_context);
407 if (SUCCEEDED(hr) && i < table_count)
409 int offset = GET_BE_DWORD(table_record->offset);
410 int length = GET_BE_DWORD(table_record->length);
411 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
413 if (found) *found = TRUE;
414 if (table_size) *table_size = length;
415 hr = IDWriteFontFileStream_ReadFileFragment(stream, table_data, offset, length, table_context);
418 return hr;
421 /**********
422 * CMAP
423 **********/
425 static int compare_group(const void *a, const void* b)
427 const DWORD *chr = a;
428 const CMAP_SegmentedCoverage_group *group = b;
430 if (*chr < GET_BE_DWORD(group->startCharCode))
431 return -1;
432 if (*chr > GET_BE_DWORD(group->endCharCode))
433 return 1;
434 return 0;
437 static void CMAP4_GetGlyphIndex(CMAP_SegmentMapping_0* format, UINT32 utf32c, UINT16 *pgi)
439 WORD *startCode;
440 SHORT *idDelta;
441 WORD *idRangeOffset;
442 int segment;
444 int segment_count = GET_BE_WORD(format->segCountX2)/2;
445 /* This is correct because of the padding before startCode */
446 startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count));
447 idDelta = (SHORT*)(((BYTE*)startCode) + (sizeof(WORD) * segment_count));
448 idRangeOffset = (WORD*)(((BYTE*)idDelta) + (sizeof(WORD) * segment_count));
450 segment = 0;
451 while(GET_BE_WORD(format->endCode[segment]) < 0xffff)
453 if (utf32c <= GET_BE_WORD(format->endCode[segment]))
454 break;
455 segment++;
457 if (segment >= segment_count)
458 return;
459 TRACE("Segment %i of %i\n",segment, segment_count);
460 if (GET_BE_WORD(startCode[segment]) > utf32c)
461 return;
462 TRACE("In range %i -> %i\n", GET_BE_WORD(startCode[segment]), GET_BE_WORD(format->endCode[segment]));
463 if (GET_BE_WORD(idRangeOffset[segment]) == 0)
465 *pgi = (SHORT)(GET_BE_WORD(idDelta[segment])) + utf32c;
467 else
469 WORD ro = GET_BE_WORD(idRangeOffset[segment])/2;
470 WORD co = (utf32c - GET_BE_WORD(startCode[segment]));
471 WORD *index = (WORD*)((BYTE*)&idRangeOffset[segment] + (ro + co));
472 *pgi = GET_BE_WORD(*index);
476 static void CMAP12_GetGlyphIndex(CMAP_SegmentedCoverage* format, UINT32 utf32c, UINT16 *pgi)
478 CMAP_SegmentedCoverage_group *group;
480 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups),
481 sizeof(CMAP_SegmentedCoverage_group), compare_group);
483 if (group)
485 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode);
486 *pgi = GET_BE_DWORD(group->startGlyphID) + offset;
490 void opentype_cmap_get_glyphindex(void *data, UINT32 utf32c, UINT16 *pgi)
492 CMAP_Header *CMAP_Table = data;
493 int i;
495 *pgi = 0;
497 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++)
499 WORD type;
500 WORD *table;
502 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
503 continue;
505 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
506 type = GET_BE_WORD(*table);
507 TRACE("table type %i\n", type);
509 switch (type)
511 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
512 CMAP4_GetGlyphIndex((CMAP_SegmentMapping_0*) table, utf32c, pgi);
513 break;
514 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
515 CMAP12_GetGlyphIndex((CMAP_SegmentedCoverage*) table, utf32c, pgi);
516 break;
517 default:
518 TRACE("table type %i unhandled.\n", type);
521 if (*pgi) return;
525 static UINT32 opentype_cmap_get_unicode_ranges_count(const CMAP_Header *CMAP_Table)
527 UINT32 count = 0;
528 int i;
530 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++) {
531 WORD type;
532 WORD *table;
534 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
535 continue;
537 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
538 type = GET_BE_WORD(*table);
539 TRACE("table type %i\n", type);
541 switch (type)
543 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
545 CMAP_SegmentMapping_0 *format = (CMAP_SegmentMapping_0*)table;
546 count += GET_BE_WORD(format->segCountX2)/2;
547 break;
549 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
551 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)table;
552 count += GET_BE_DWORD(format->nGroups);
553 break;
555 default:
556 FIXME("table type %i unhandled.\n", type);
560 return count;
563 HRESULT opentype_cmap_get_unicode_ranges(void *data, UINT32 max_count, DWRITE_UNICODE_RANGE *ranges, UINT32 *count)
565 CMAP_Header *CMAP_Table = data;
566 int i, k = 0;
568 if (!CMAP_Table)
569 return E_FAIL;
571 *count = opentype_cmap_get_unicode_ranges_count(CMAP_Table);
573 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables) && k < max_count; i++)
575 WORD type;
576 WORD *table;
577 int j;
579 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
580 continue;
582 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
583 type = GET_BE_WORD(*table);
584 TRACE("table type %i\n", type);
586 switch (type)
588 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
590 CMAP_SegmentMapping_0 *format = (CMAP_SegmentMapping_0*)table;
591 UINT16 segment_count = GET_BE_WORD(format->segCountX2)/2;
592 UINT16 *startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count));
594 for (j = 0; j < segment_count && GET_BE_WORD(format->endCode[j]) < 0xffff && k < max_count; j++, k++) {
595 ranges[k].first = GET_BE_WORD(startCode[j]);
596 ranges[k].last = GET_BE_WORD(format->endCode[j]);
598 break;
600 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
602 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)table;
603 for (j = 0; j < GET_BE_DWORD(format->nGroups) && k < max_count; j++, k++) {
604 ranges[k].first = GET_BE_DWORD(format->groups[j].startCharCode);
605 ranges[k].last = GET_BE_DWORD(format->groups[j].endCharCode);
607 break;
609 default:
610 FIXME("table type %i unhandled.\n", type);
614 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
617 VOID get_font_properties(LPCVOID os2, LPCVOID head, LPCVOID post, DWRITE_FONT_METRICS *metrics, DWRITE_FONT_STRETCH *stretch, DWRITE_FONT_WEIGHT *weight, DWRITE_FONT_STYLE *style)
619 TT_OS2_V2 *tt_os2 = (TT_OS2_V2*)os2;
620 TT_HEAD *tt_head = (TT_HEAD*)head;
621 TT_POST *tt_post = (TT_POST*)post;
623 /* default stretch, weight and style to normal */
624 *stretch = DWRITE_FONT_STRETCH_NORMAL;
625 *weight = DWRITE_FONT_WEIGHT_NORMAL;
626 *style = DWRITE_FONT_STYLE_NORMAL;
628 memset(metrics, 0, sizeof(*metrics));
630 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
631 if (tt_os2)
633 if (GET_BE_WORD(tt_os2->usWidthClass) <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
634 *stretch = GET_BE_WORD(tt_os2->usWidthClass);
636 *weight = GET_BE_WORD(tt_os2->usWeightClass);
637 TRACE("stretch=%d, weight=%d\n", *stretch, *weight);
639 metrics->ascent = GET_BE_WORD(tt_os2->sTypoAscender);
640 metrics->descent = GET_BE_WORD(tt_os2->sTypoDescender);
641 metrics->lineGap = GET_BE_WORD(tt_os2->sTypoLineGap);
642 metrics->capHeight = GET_BE_WORD(tt_os2->sCapHeight);
643 metrics->xHeight = GET_BE_WORD(tt_os2->sxHeight);
644 metrics->strikethroughPosition = GET_BE_WORD(tt_os2->yStrikeoutPosition);
645 metrics->strikethroughThickness = GET_BE_WORD(tt_os2->yStrikeoutSize);
648 if (tt_head)
650 USHORT macStyle = GET_BE_WORD(tt_head->macStyle);
651 metrics->designUnitsPerEm = GET_BE_WORD(tt_head->unitsPerEm);
652 if (macStyle & 0x0002)
653 *style = DWRITE_FONT_STYLE_ITALIC;
657 if (tt_post)
659 metrics->underlinePosition = GET_BE_WORD(tt_post->underlinePosition);
660 metrics->underlineThickness = GET_BE_WORD(tt_post->underlineThickness);
664 static UINT get_name_record_codepage(enum OPENTYPE_PLATFORM_ID platform, USHORT encoding)
666 UINT codepage = 0;
668 switch (platform) {
669 case OPENTYPE_PLATFORM_MAC:
670 switch (encoding)
672 case TT_NAME_MAC_ENCODING_ROMAN:
673 codepage = 10000;
674 break;
675 case TT_NAME_MAC_ENCODING_JAPANESE:
676 codepage = 10001;
677 break;
678 case TT_NAME_MAC_ENCODING_TRAD_CHINESE:
679 codepage = 10002;
680 break;
681 case TT_NAME_MAC_ENCODING_KOREAN:
682 codepage = 10003;
683 break;
684 case TT_NAME_MAC_ENCODING_ARABIC:
685 codepage = 10004;
686 break;
687 case TT_NAME_MAC_ENCODING_HEBREW:
688 codepage = 10005;
689 break;
690 case TT_NAME_MAC_ENCODING_GREEK:
691 codepage = 10006;
692 break;
693 case TT_NAME_MAC_ENCODING_RUSSIAN:
694 codepage = 10007;
695 break;
696 case TT_NAME_MAC_ENCODING_SIMPL_CHINESE:
697 codepage = 10008;
698 break;
699 case TT_NAME_MAC_ENCODING_THAI:
700 codepage = 10021;
701 break;
702 default:
703 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
704 break;
706 break;
707 case OPENTYPE_PLATFORM_WIN:
708 switch (encoding)
710 case TT_NAME_WINDOWS_ENCODING_SYMBOL:
711 case TT_NAME_WINDOWS_ENCODING_UCS2:
712 break;
713 case TT_NAME_WINDOWS_ENCODING_SJIS:
714 codepage = 932;
715 break;
716 case TT_NAME_WINDOWS_ENCODING_PRC:
717 codepage = 936;
718 break;
719 case TT_NAME_WINDOWS_ENCODING_BIG5:
720 codepage = 950;
721 break;
722 case TT_NAME_WINDOWS_ENCODING_WANSUNG:
723 codepage = 20949;
724 break;
725 case TT_NAME_WINDOWS_ENCODING_JOHAB:
726 codepage = 1361;
727 break;
728 default:
729 FIXME("encoding %u not handled, platform %d.\n", encoding, platform);
730 break;
732 break;
733 default:
734 FIXME("unknown platform %d\n", platform);
737 return codepage;
740 HRESULT opentype_get_font_strings_from_id(const void *table_data, DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **strings)
742 const TT_NAME_V0 *header;
743 BYTE *storage_area = 0;
744 USHORT count = 0;
745 UINT16 name_id;
746 BOOL exists;
747 HRESULT hr;
748 int i;
750 if (!table_data)
751 return E_FAIL;
753 hr = create_localizedstrings(strings);
754 if (FAILED(hr)) return hr;
756 header = table_data;
757 storage_area = (LPBYTE)table_data + GET_BE_WORD(header->stringOffset);
758 count = GET_BE_WORD(header->count);
760 name_id = dwriteid_to_opentypeid[id];
762 exists = FALSE;
763 for (i = 0; i < count; i++) {
764 const TT_NameRecord *record = &header->nameRecord[i];
765 USHORT lang_id, length, offset, encoding, platform;
767 if (GET_BE_WORD(record->nameID) != name_id)
768 continue;
770 exists = TRUE;
772 /* Right now only accept unicode and windows encoded fonts */
773 platform = GET_BE_WORD(record->platformID);
774 if (platform != OPENTYPE_PLATFORM_UNICODE &&
775 platform != OPENTYPE_PLATFORM_MAC &&
776 platform != OPENTYPE_PLATFORM_WIN)
778 FIXME("platform %i not supported\n", platform);
779 continue;
782 lang_id = GET_BE_WORD(record->languageID);
783 length = GET_BE_WORD(record->length);
784 offset = GET_BE_WORD(record->offset);
785 encoding = GET_BE_WORD(record->encodingID);
787 if (lang_id < 0x8000) {
788 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
789 WCHAR *name_string;
790 UINT codepage;
792 codepage = get_name_record_codepage(platform, encoding);
794 if (codepage) {
795 DWORD len = MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, NULL, 0);
796 name_string = heap_alloc(sizeof(WCHAR) * (len+1));
797 MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, name_string, len);
798 name_string[len] = 0;
800 else {
801 int i;
803 length /= sizeof(WCHAR);
804 name_string = heap_strdupnW((LPWSTR)(storage_area + offset), length);
805 for (i = 0; i < length; i++)
806 name_string[i] = GET_BE_WORD(name_string[i]);
809 if (!LCIDToLocaleName(MAKELCID(lang_id, SORT_DEFAULT), locale, sizeof(locale)/sizeof(WCHAR), 0)) {
810 static const WCHAR enusW[] = {'e','n','-','u','s',0};
811 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id, SORT_DEFAULT));
812 strcpyW(locale, enusW);
815 TRACE("string %s for locale %s found\n", debugstr_w(name_string), debugstr_w(locale));
816 add_localizedstring(*strings, locale, name_string);
817 heap_free(name_string);
819 else {
820 FIXME("handle NAME format 1");
821 continue;
825 if (!exists) {
826 IDWriteLocalizedStrings_Release(*strings);
827 *strings = NULL;
830 return hr;