dwrite: Return on first found glyph.
[wine.git] / dlls / dwrite / opentype.c
blob487f56b73b0682875229dec901224f98d482231d
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 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, UINT32* font_count, DWRITE_FONT_FILE_TYPE *file_type, DWRITE_FONT_FACE_TYPE *face_type, BOOL *supported)
190 /* TODO: Do font validation */
191 const void *font_data;
192 const char* tag;
193 void *context;
194 HRESULT hr;
196 hr = IDWriteFontFileStream_ReadFileFragment(stream, &font_data, 0, sizeof(TTC_Header_V1), &context);
197 if (FAILED(hr))
198 return hr;
200 tag = font_data;
201 *supported = FALSE;
202 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
203 if (face_type)
204 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
205 *font_count = 0;
207 if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_TTCF_TAG)
209 const TTC_Header_V1 *header = font_data;
210 *font_count = GET_BE_DWORD(header->numFonts);
211 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE_COLLECTION;
212 if (face_type)
213 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION;
214 *supported = TRUE;
216 else if (GET_BE_DWORD(*(DWORD*)font_data) == 0x10000)
218 *font_count = 1;
219 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
220 if (face_type)
221 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
222 *supported = TRUE;
224 else if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_OTTO_TAG)
226 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
229 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
230 return S_OK;
233 HRESULT opentype_get_font_table(IDWriteFontFileStream *stream, DWRITE_FONT_FACE_TYPE type, UINT32 font_index, UINT32 tag,
234 const void **table_data, void **table_context, UINT32 *table_size, BOOL *found)
236 HRESULT hr;
237 TTC_SFNT_V1 *font_header = NULL;
238 void *sfnt_context;
239 TT_TableRecord *table_record = NULL;
240 void *table_record_context;
241 int table_count, table_offset = 0;
242 int i;
244 *found = FALSE;
246 if (type == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) {
247 const TTC_Header_V1 *ttc_header;
248 void * ttc_context;
249 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
250 if (SUCCEEDED(hr)) {
251 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[0]);
252 if (font_index >= GET_BE_DWORD(ttc_header->numFonts))
253 hr = E_INVALIDARG;
254 else
255 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
256 IDWriteFontFileStream_ReleaseFileFragment(stream, ttc_context);
259 else
260 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
262 if (FAILED(hr))
263 return hr;
265 table_count = GET_BE_WORD(font_header->numTables);
266 table_offset += sizeof(*font_header);
267 for (i = 0; i < table_count; i++)
269 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&table_record, table_offset, sizeof(*table_record), &table_record_context);
270 if (FAILED(hr))
271 break;
272 if (DWRITE_MAKE_OPENTYPE_TAG(table_record->tag[0], table_record->tag[1], table_record->tag[2], table_record->tag[3]) == tag)
273 break;
274 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
275 table_offset += sizeof(*table_record);
278 IDWriteFontFileStream_ReleaseFileFragment(stream, sfnt_context);
279 if (SUCCEEDED(hr) && i < table_count)
281 int offset = GET_BE_DWORD(table_record->offset);
282 int length = GET_BE_DWORD(table_record->length);
283 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
285 *found = TRUE;
286 *table_size = length;
287 hr = IDWriteFontFileStream_ReadFileFragment(stream, table_data, offset, length, table_context);
290 return hr;
293 /**********
294 * CMAP
295 **********/
297 static int compare_group(const void *a, const void* b)
299 const DWORD *chr = a;
300 const CMAP_SegmentedCoverage_group *group = b;
302 if (*chr < GET_BE_DWORD(group->startCharCode))
303 return -1;
304 if (*chr > GET_BE_DWORD(group->endCharCode))
305 return 1;
306 return 0;
309 static void CMAP4_GetGlyphIndex(CMAP_SegmentMapping_0* format, UINT32 utf32c, UINT16 *pgi)
311 WORD *startCode;
312 SHORT *idDelta;
313 WORD *idRangeOffset;
314 int segment;
316 int segment_count = GET_BE_WORD(format->segCountX2)/2;
317 /* This is correct because of the padding before startCode */
318 startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count));
319 idDelta = (SHORT*)(((BYTE*)startCode) + (sizeof(WORD) * segment_count));
320 idRangeOffset = (WORD*)(((BYTE*)idDelta) + (sizeof(WORD) * segment_count));
322 segment = 0;
323 while(GET_BE_WORD(format->endCode[segment]) < 0xffff)
325 if (utf32c <= GET_BE_WORD(format->endCode[segment]))
326 break;
327 segment++;
329 if (segment >= segment_count)
330 return;
331 TRACE("Segment %i of %i\n",segment, segment_count);
332 if (GET_BE_WORD(startCode[segment]) > utf32c)
333 return;
334 TRACE("In range %i -> %i\n", GET_BE_WORD(startCode[segment]), GET_BE_WORD(format->endCode[segment]));
335 if (GET_BE_WORD(idRangeOffset[segment]) == 0)
337 *pgi = (SHORT)(GET_BE_WORD(idDelta[segment])) + utf32c;
339 else
341 WORD ro = GET_BE_WORD(idRangeOffset[segment])/2;
342 WORD co = (utf32c - GET_BE_WORD(startCode[segment]));
343 WORD *index = (WORD*)((BYTE*)&idRangeOffset[segment] + (ro + co));
344 *pgi = GET_BE_WORD(*index);
348 static void CMAP12_GetGlyphIndex(CMAP_SegmentedCoverage* format, UINT32 utf32c, UINT16 *pgi)
350 CMAP_SegmentedCoverage_group *group;
352 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups),
353 sizeof(CMAP_SegmentedCoverage_group), compare_group);
355 if (group)
357 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode);
358 *pgi = GET_BE_DWORD(group->startGlyphID) + offset;
362 void opentype_cmap_get_glyphindex(void *data, UINT32 utf32c, UINT16 *pgi)
364 CMAP_Header *CMAP_Table = data;
365 int i;
367 *pgi = 0;
369 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++)
371 WORD type;
372 WORD *table;
374 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
375 continue;
377 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
378 type = GET_BE_WORD(*table);
379 TRACE("table type %i\n", type);
381 switch (type)
383 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
384 CMAP4_GetGlyphIndex((CMAP_SegmentMapping_0*) table, utf32c, pgi);
385 break;
386 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
387 CMAP12_GetGlyphIndex((CMAP_SegmentedCoverage*) table, utf32c, pgi);
388 break;
389 default:
390 TRACE("table type %i unhandled.\n", type);
393 if (*pgi) return;
397 static UINT32 opentype_cmap_get_unicode_ranges_count(const CMAP_Header *CMAP_Table)
399 UINT32 count = 0;
400 int i;
402 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++) {
403 WORD type;
404 WORD *table;
406 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
407 continue;
409 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
410 type = GET_BE_WORD(*table);
411 TRACE("table type %i\n", type);
413 switch (type)
415 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
417 CMAP_SegmentMapping_0 *format = (CMAP_SegmentMapping_0*)table;
418 count += GET_BE_WORD(format->segCountX2)/2;
419 break;
421 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
423 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)table;
424 count += GET_BE_DWORD(format->nGroups);
425 break;
427 default:
428 FIXME("table type %i unhandled.\n", type);
432 return count;
435 HRESULT opentype_cmap_get_unicode_ranges(void *data, UINT32 max_count, DWRITE_UNICODE_RANGE *ranges, UINT32 *count)
437 CMAP_Header *CMAP_Table = data;
438 int i, k = 0;
440 if (!CMAP_Table)
441 return E_FAIL;
443 *count = opentype_cmap_get_unicode_ranges_count(CMAP_Table);
445 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables) && k < max_count; i++)
447 WORD type;
448 WORD *table;
449 int j;
451 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
452 continue;
454 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
455 type = GET_BE_WORD(*table);
456 TRACE("table type %i\n", type);
458 switch (type)
460 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING:
462 CMAP_SegmentMapping_0 *format = (CMAP_SegmentMapping_0*)table;
463 UINT16 segment_count = GET_BE_WORD(format->segCountX2)/2;
464 UINT16 *startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count));
466 for (j = 0; j < segment_count && GET_BE_WORD(format->endCode[j]) < 0xffff && k < max_count; j++, k++) {
467 ranges[k].first = GET_BE_WORD(startCode[j]);
468 ranges[k].last = GET_BE_WORD(format->endCode[j]);
470 break;
472 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE:
474 CMAP_SegmentedCoverage *format = (CMAP_SegmentedCoverage*)table;
475 for (j = 0; j < GET_BE_DWORD(format->nGroups) && k < max_count; j++, k++) {
476 ranges[k].first = GET_BE_DWORD(format->groups[j].startCharCode);
477 ranges[k].last = GET_BE_DWORD(format->groups[j].endCharCode);
479 break;
481 default:
482 FIXME("table type %i unhandled.\n", type);
486 return *count > max_count ? E_NOT_SUFFICIENT_BUFFER : S_OK;
489 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)
491 TT_OS2_V2 *tt_os2 = (TT_OS2_V2*)os2;
492 TT_HEAD *tt_head = (TT_HEAD*)head;
493 TT_POST *tt_post = (TT_POST*)post;
495 /* default stretch, weight and style to normal */
496 *stretch = DWRITE_FONT_STRETCH_NORMAL;
497 *weight = DWRITE_FONT_WEIGHT_NORMAL;
498 *style = DWRITE_FONT_STYLE_NORMAL;
500 memset(metrics, 0, sizeof(*metrics));
502 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
503 if (tt_os2)
505 if (GET_BE_WORD(tt_os2->usWidthClass) <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
506 *stretch = GET_BE_WORD(tt_os2->usWidthClass);
508 *weight = GET_BE_WORD(tt_os2->usWeightClass);
509 TRACE("stretch=%d, weight=%d\n", *stretch, *weight);
511 metrics->ascent = GET_BE_WORD(tt_os2->sTypoAscender);
512 metrics->descent = GET_BE_WORD(tt_os2->sTypoDescender);
513 metrics->lineGap = GET_BE_WORD(tt_os2->sTypoLineGap);
514 metrics->capHeight = GET_BE_WORD(tt_os2->sCapHeight);
515 metrics->xHeight = GET_BE_WORD(tt_os2->sxHeight);
516 metrics->strikethroughPosition = GET_BE_WORD(tt_os2->yStrikeoutPosition);
517 metrics->strikethroughThickness = GET_BE_WORD(tt_os2->yStrikeoutSize);
520 if (tt_head)
522 USHORT macStyle = GET_BE_WORD(tt_head->macStyle);
523 metrics->designUnitsPerEm = GET_BE_WORD(tt_head->unitsPerEm);
524 if (macStyle & 0x0002)
525 *style = DWRITE_FONT_STYLE_ITALIC;
529 if (tt_post)
531 metrics->underlinePosition = GET_BE_WORD(tt_post->underlinePosition);
532 metrics->underlineThickness = GET_BE_WORD(tt_post->underlineThickness);