dwrite: Pass raw NAME table pointer to table reading code.
[wine.git] / dlls / dwrite / opentype.c
blobc007b351ac5496cf3f32a50f195ab97c25d28d99
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 TT_NAME_WINDOWS_ENCODING_ID
206 TT_NAME_WINDOWS_ENCODING_SYMBOL = 0,
207 TT_NAME_WINDOWS_ENCODING_UCS2,
208 TT_NAME_WINDOWS_ENCODING_SJIS,
209 TT_NAME_WINDOWS_ENCODING_PRC,
210 TT_NAME_WINDOWS_ENCODING_BIG5,
211 TT_NAME_WINDOWS_ENCODING_WANSUNG,
212 TT_NAME_WINDOWS_ENCODING_JOHAB,
213 TT_NAME_WINDOWS_ENCODING_RESERVED1,
214 TT_NAME_WINDOWS_ENCODING_RESERVED2,
215 TT_NAME_WINDOWS_ENCODING_RESERVED3,
216 TT_NAME_WINDOWS_ENCODING_UCS4
219 enum OPENTYPE_STRING_ID
221 OPENTYPE_STRING_COPYRIGHT_NOTICE = 0,
222 OPENTYPE_STRING_FAMILY_NAME,
223 OPENTYPE_STRING_SUBFAMILY_NAME,
224 OPENTYPE_STRING_UNIQUE_IDENTIFIER,
225 OPENTYPE_STRING_FULL_FONTNAME,
226 OPENTYPE_STRING_VERSION_STRING,
227 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
228 OPENTYPE_STRING_TRADEMARK,
229 OPENTYPE_STRING_MANUFACTURER,
230 OPENTYPE_STRING_DESIGNER,
231 OPENTYPE_STRING_DESCRIPTION,
232 OPENTYPE_STRING_VENDOR_URL,
233 OPENTYPE_STRING_DESIGNER_URL,
234 OPENTYPE_STRING_LICENSE_DESCRIPTION,
235 OPENTYPE_STRING_LICENSE_INFO_URL,
236 OPENTYPE_STRING_RESERVED_ID15,
237 OPENTYPE_STRING_PREFERRED_FAMILY_NAME,
238 OPENTYPE_STRING_PREFERRED_SUBFAMILY_NAME,
239 OPENTYPE_STRING_COMPATIBLE_FULLNAME,
240 OPENTYPE_STRING_SAMPLE_TEXT,
241 OPENTYPE_STRING_POSTSCRIPT_CID_NAME,
242 OPENTYPE_STRING_WWS_FAMILY_NAME,
243 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
246 static const UINT16 dwriteid_to_opentypeid[DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME+1] =
248 (UINT16)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
249 OPENTYPE_STRING_COPYRIGHT_NOTICE,
250 OPENTYPE_STRING_VERSION_STRING,
251 OPENTYPE_STRING_TRADEMARK,
252 OPENTYPE_STRING_MANUFACTURER,
253 OPENTYPE_STRING_DESIGNER,
254 OPENTYPE_STRING_DESIGNER_URL,
255 OPENTYPE_STRING_DESCRIPTION,
256 OPENTYPE_STRING_VENDOR_URL,
257 OPENTYPE_STRING_LICENSE_DESCRIPTION,
258 OPENTYPE_STRING_LICENSE_INFO_URL,
259 OPENTYPE_STRING_FAMILY_NAME,
260 OPENTYPE_STRING_SUBFAMILY_NAME,
261 OPENTYPE_STRING_PREFERRED_FAMILY_NAME,
262 OPENTYPE_STRING_PREFERRED_SUBFAMILY_NAME,
263 OPENTYPE_STRING_SAMPLE_TEXT,
264 OPENTYPE_STRING_FULL_FONTNAME,
265 OPENTYPE_STRING_POSTSCRIPT_FONTNAME,
266 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
269 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, UINT32* font_count, DWRITE_FONT_FILE_TYPE *file_type, DWRITE_FONT_FACE_TYPE *face_type, BOOL *supported)
271 /* TODO: Do font validation */
272 const void *font_data;
273 const char* tag;
274 void *context;
275 HRESULT hr;
277 hr = IDWriteFontFileStream_ReadFileFragment(stream, &font_data, 0, sizeof(TTC_Header_V1), &context);
278 if (FAILED(hr))
279 return hr;
281 tag = font_data;
282 *supported = FALSE;
283 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
284 if (face_type)
285 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
286 *font_count = 0;
288 if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_TTCF_TAG)
290 const TTC_Header_V1 *header = font_data;
291 *font_count = GET_BE_DWORD(header->numFonts);
292 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE_COLLECTION;
293 if (face_type)
294 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION;
295 *supported = TRUE;
297 else if (GET_BE_DWORD(*(DWORD*)font_data) == 0x10000)
299 *font_count = 1;
300 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
301 if (face_type)
302 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
303 *supported = TRUE;
305 else if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_OTTO_TAG)
307 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
310 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
311 return S_OK;
314 HRESULT opentype_get_font_table(IDWriteFontFileStream *stream, DWRITE_FONT_FACE_TYPE type, UINT32 font_index, UINT32 tag,
315 const void **table_data, void **table_context, UINT32 *table_size, BOOL *found)
317 HRESULT hr;
318 TTC_SFNT_V1 *font_header = NULL;
319 void *sfnt_context;
320 TT_TableRecord *table_record = NULL;
321 void *table_record_context;
322 int table_count, table_offset = 0;
323 int i;
325 *found = FALSE;
327 if (type == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) {
328 const TTC_Header_V1 *ttc_header;
329 void * ttc_context;
330 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
331 if (SUCCEEDED(hr)) {
332 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[0]);
333 if (font_index >= GET_BE_DWORD(ttc_header->numFonts))
334 hr = E_INVALIDARG;
335 else
336 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
337 IDWriteFontFileStream_ReleaseFileFragment(stream, ttc_context);
340 else
341 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
343 if (FAILED(hr))
344 return hr;
346 table_count = GET_BE_WORD(font_header->numTables);
347 table_offset += sizeof(*font_header);
348 for (i = 0; i < table_count; i++)
350 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&table_record, table_offset, sizeof(*table_record), &table_record_context);
351 if (FAILED(hr))
352 break;
353 if (DWRITE_MAKE_OPENTYPE_TAG(table_record->tag[0], table_record->tag[1], table_record->tag[2], table_record->tag[3]) == tag)
354 break;
355 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
356 table_offset += sizeof(*table_record);
359 IDWriteFontFileStream_ReleaseFileFragment(stream, sfnt_context);
360 if (SUCCEEDED(hr) && i < table_count)
362 int offset = GET_BE_DWORD(table_record->offset);
363 int length = GET_BE_DWORD(table_record->length);
364 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
366 *found = TRUE;
367 *table_size = length;
368 hr = IDWriteFontFileStream_ReadFileFragment(stream, table_data, offset, length, table_context);
371 return hr;
374 /**********
375 * CMAP
376 **********/
378 static int compare_group(const void *a, const void* b)
380 const DWORD *chr = a;
381 const CMAP_SegmentedCoverage_group *group = b;
383 if (*chr < GET_BE_DWORD(group->startCharCode))
384 return -1;
385 if (*chr > GET_BE_DWORD(group->endCharCode))
386 return 1;
387 return 0;
390 static void CMAP4_GetGlyphIndex(CMAP_SegmentMapping_0* format, UINT32 utf32c, UINT16 *pgi)
392 WORD *startCode;
393 SHORT *idDelta;
394 WORD *idRangeOffset;
395 int segment;
397 int segment_count = GET_BE_WORD(format->segCountX2)/2;
398 /* This is correct because of the padding before startCode */
399 startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count));
400 idDelta = (SHORT*)(((BYTE*)startCode) + (sizeof(WORD) * segment_count));
401 idRangeOffset = (WORD*)(((BYTE*)idDelta) + (sizeof(WORD) * segment_count));
403 segment = 0;
404 while(GET_BE_WORD(format->endCode[segment]) < 0xffff)
406 if (utf32c <= GET_BE_WORD(format->endCode[segment]))
407 break;
408 segment++;
410 if (segment >= segment_count)
411 return;
412 TRACE("Segment %i of %i\n",segment, segment_count);
413 if (GET_BE_WORD(startCode[segment]) > utf32c)
414 return;
415 TRACE("In range %i -> %i\n", GET_BE_WORD(startCode[segment]), GET_BE_WORD(format->endCode[segment]));
416 if (GET_BE_WORD(idRangeOffset[segment]) == 0)
418 *pgi = (SHORT)(GET_BE_WORD(idDelta[segment])) + utf32c;
420 else
422 WORD ro = GET_BE_WORD(idRangeOffset[segment])/2;
423 WORD co = (utf32c - GET_BE_WORD(startCode[segment]));
424 WORD *index = (WORD*)((BYTE*)&idRangeOffset[segment] + (ro + co));
425 *pgi = GET_BE_WORD(*index);
429 static void CMAP12_GetGlyphIndex(CMAP_SegmentedCoverage* format, UINT32 utf32c, UINT16 *pgi)
431 CMAP_SegmentedCoverage_group *group;
433 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups),
434 sizeof(CMAP_SegmentedCoverage_group), compare_group);
436 if (group)
438 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode);
439 *pgi = GET_BE_DWORD(group->startGlyphID) + offset;
443 void opentype_cmap_get_glyphindex(void *data, UINT32 utf32c, UINT16 *pgi)
445 CMAP_Header *CMAP_Table = data;
446 int i;
448 *pgi = 0;
450 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++)
452 WORD type;
453 WORD *table;
455 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
456 continue;
458 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
459 type = GET_BE_WORD(*table);
460 TRACE("table type %i\n", type);
462 switch (type)
464 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
465 CMAP4_GetGlyphIndex((CMAP_SegmentMapping_0*) table, utf32c, pgi);
466 break;
467 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
468 CMAP12_GetGlyphIndex((CMAP_SegmentedCoverage*) table, utf32c, pgi);
469 break;
470 default:
471 TRACE("table type %i unhandled.\n", type);
474 if (*pgi) return;
478 static UINT32 opentype_cmap_get_unicode_ranges_count(const CMAP_Header *CMAP_Table)
480 UINT32 count = 0;
481 int i;
483 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++) {
484 WORD type;
485 WORD *table;
487 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
488 continue;
490 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
491 type = GET_BE_WORD(*table);
492 TRACE("table type %i\n", type);
494 switch (type)
496 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
498 CMAP_SegmentMapping_0 *format = (CMAP_SegmentMapping_0*)table;
499 count += GET_BE_WORD(format->segCountX2)/2;
500 break;
502 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
504 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)table;
505 count += GET_BE_DWORD(format->nGroups);
506 break;
508 default:
509 FIXME("table type %i unhandled.\n", type);
513 return count;
516 HRESULT opentype_cmap_get_unicode_ranges(void *data, UINT32 max_count, DWRITE_UNICODE_RANGE *ranges, UINT32 *count)
518 CMAP_Header *CMAP_Table = data;
519 int i, k = 0;
521 if (!CMAP_Table)
522 return E_FAIL;
524 *count = opentype_cmap_get_unicode_ranges_count(CMAP_Table);
526 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables) && k < max_count; i++)
528 WORD type;
529 WORD *table;
530 int j;
532 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
533 continue;
535 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
536 type = GET_BE_WORD(*table);
537 TRACE("table type %i\n", type);
539 switch (type)
541 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
543 CMAP_SegmentMapping_0 *format = (CMAP_SegmentMapping_0*)table;
544 UINT16 segment_count = GET_BE_WORD(format->segCountX2)/2;
545 UINT16 *startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count));
547 for (j = 0; j < segment_count && GET_BE_WORD(format->endCode[j]) < 0xffff && k < max_count; j++, k++) {
548 ranges[k].first = GET_BE_WORD(startCode[j]);
549 ranges[k].last = GET_BE_WORD(format->endCode[j]);
551 break;
553 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
555 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)table;
556 for (j = 0; j < GET_BE_DWORD(format->nGroups) && k < max_count; j++, k++) {
557 ranges[k].first = GET_BE_DWORD(format->groups[j].startCharCode);
558 ranges[k].last = GET_BE_DWORD(format->groups[j].endCharCode);
560 break;
562 default:
563 FIXME("table type %i unhandled.\n", type);
567 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
570 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)
572 TT_OS2_V2 *tt_os2 = (TT_OS2_V2*)os2;
573 TT_HEAD *tt_head = (TT_HEAD*)head;
574 TT_POST *tt_post = (TT_POST*)post;
576 /* default stretch, weight and style to normal */
577 *stretch = DWRITE_FONT_STRETCH_NORMAL;
578 *weight = DWRITE_FONT_WEIGHT_NORMAL;
579 *style = DWRITE_FONT_STYLE_NORMAL;
581 memset(metrics, 0, sizeof(*metrics));
583 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
584 if (tt_os2)
586 if (GET_BE_WORD(tt_os2->usWidthClass) <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
587 *stretch = GET_BE_WORD(tt_os2->usWidthClass);
589 *weight = GET_BE_WORD(tt_os2->usWeightClass);
590 TRACE("stretch=%d, weight=%d\n", *stretch, *weight);
592 metrics->ascent = GET_BE_WORD(tt_os2->sTypoAscender);
593 metrics->descent = GET_BE_WORD(tt_os2->sTypoDescender);
594 metrics->lineGap = GET_BE_WORD(tt_os2->sTypoLineGap);
595 metrics->capHeight = GET_BE_WORD(tt_os2->sCapHeight);
596 metrics->xHeight = GET_BE_WORD(tt_os2->sxHeight);
597 metrics->strikethroughPosition = GET_BE_WORD(tt_os2->yStrikeoutPosition);
598 metrics->strikethroughThickness = GET_BE_WORD(tt_os2->yStrikeoutSize);
601 if (tt_head)
603 USHORT macStyle = GET_BE_WORD(tt_head->macStyle);
604 metrics->designUnitsPerEm = GET_BE_WORD(tt_head->unitsPerEm);
605 if (macStyle & 0x0002)
606 *style = DWRITE_FONT_STYLE_ITALIC;
610 if (tt_post)
612 metrics->underlinePosition = GET_BE_WORD(tt_post->underlinePosition);
613 metrics->underlineThickness = GET_BE_WORD(tt_post->underlineThickness);
617 HRESULT opentype_get_font_strings_from_id(const void *table_data, DWRITE_INFORMATIONAL_STRING_ID id, IDWriteLocalizedStrings **strings)
619 const TT_NAME_V0 *header;
620 BYTE *storage_area = 0;
621 USHORT count = 0;
622 UINT16 name_id;
623 BOOL exists;
624 HRESULT hr;
625 int i;
627 if (!table_data)
628 return E_FAIL;
630 hr = create_localizedstrings(strings);
631 if (FAILED(hr)) return hr;
633 header = table_data;
634 storage_area = (LPBYTE)table_data + GET_BE_WORD(header->stringOffset);
635 count = GET_BE_WORD(header->count);
637 name_id = dwriteid_to_opentypeid[id];
639 exists = FALSE;
640 for (i = 0; i < count; i++) {
641 const TT_NameRecord *record = &header->nameRecord[i];
642 USHORT lang_id, length, offset, encoding, platform;
644 if (GET_BE_WORD(record->nameID) != name_id)
645 continue;
647 exists = TRUE;
649 /* Right now only accept unicode and windows encoded fonts */
650 platform = GET_BE_WORD(record->platformID);
651 if (platform != 0 && platform != 3) {
652 FIXME("platform %i not supported\n", platform);
653 continue;
656 lang_id = GET_BE_WORD(record->languageID);
657 length = GET_BE_WORD(record->length);
658 offset = GET_BE_WORD(record->offset);
659 encoding = GET_BE_WORD(record->encodingID);
661 if (lang_id < 0x8000) {
662 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
663 WCHAR *name_string;
664 UINT codepage = 0;
666 if (platform == 3)
668 switch (encoding)
670 case TT_NAME_WINDOWS_ENCODING_SYMBOL:
671 case TT_NAME_WINDOWS_ENCODING_UCS2:
672 break;
673 case TT_NAME_WINDOWS_ENCODING_SJIS:
674 codepage = 932;
675 break;
676 case TT_NAME_WINDOWS_ENCODING_PRC:
677 codepage = 936;
678 break;
679 case TT_NAME_WINDOWS_ENCODING_BIG5:
680 codepage = 950;
681 break;
682 case TT_NAME_WINDOWS_ENCODING_WANSUNG:
683 codepage = 20949;
684 break;
685 case TT_NAME_WINDOWS_ENCODING_JOHAB:
686 codepage = 1361;
687 break;
688 default:
689 FIXME("encoding %d not handled.\n", encoding);
693 if (codepage) {
694 DWORD len = MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, NULL, 0);
695 name_string = heap_alloc(sizeof(WCHAR) * len);
696 MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, name_string, len);
698 else {
699 int i;
701 length /= sizeof(WCHAR);
702 name_string = heap_strdupnW((LPWSTR)(storage_area + offset), length);
703 for (i = 0; i < length; i++)
704 name_string[i] = GET_BE_WORD(name_string[i]);
707 if (!LCIDToLocaleName(MAKELCID(lang_id, SORT_DEFAULT), locale, sizeof(locale)/sizeof(WCHAR), 0)) {
708 static const WCHAR enusW[] = {'e','n','-','u','s',0};
709 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id, SORT_DEFAULT));
710 strcpyW(locale, enusW);
713 TRACE("string %s for locale %s found\n", debugstr_w(name_string), debugstr_w(locale));
714 add_localizedstring(*strings, locale, name_string);
715 heap_free(name_string);
717 else {
718 FIXME("handle NAME format 1");
719 continue;
723 if (!exists) {
724 IDWriteLocalizedStrings_Release(*strings);
725 *strings = NULL;
728 return hr;