dwrite: Pass stream pointer to OpenType parsing code.
[wine.git] / dlls / dwrite / opentype.c
blob26ad3334a9add170fe37ca6f5108c3042f8b5b54
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.h"
24 #include "dwrite_private.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
28 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
29 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
31 #ifdef WORDS_BIGENDIAN
32 #define GET_BE_WORD(x) (x)
33 #define GET_BE_DWORD(x) (x)
34 #else
35 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
36 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)))
37 #endif
39 typedef struct {
40 CHAR TTCTag[4];
41 DWORD Version;
42 DWORD numFonts;
43 DWORD OffsetTable[1];
44 } TTC_Header_V1;
46 typedef struct {
47 DWORD version;
48 WORD numTables;
49 WORD searchRange;
50 WORD entrySelector;
51 WORD rangeShift;
52 } TTC_SFNT_V1;
54 typedef struct {
55 CHAR tag[4];
56 DWORD checkSum;
57 DWORD offset;
58 DWORD length;
59 } TT_TableRecord;
61 typedef struct {
62 WORD platformID;
63 WORD encodingID;
64 DWORD offset;
65 } CMAP_EncodingRecord;
67 typedef struct {
68 WORD version;
69 WORD numTables;
70 CMAP_EncodingRecord tables[1];
71 } CMAP_Header;
73 typedef struct {
74 DWORD startCharCode;
75 DWORD endCharCode;
76 DWORD startGlyphID;
77 } CMAP_SegmentedCoverage_group;
79 typedef struct {
80 WORD format;
81 WORD reserved;
82 DWORD length;
83 DWORD language;
84 DWORD nGroups;
85 CMAP_SegmentedCoverage_group groups[1];
86 } CMAP_SegmentedCoverage;
88 typedef struct {
89 WORD format;
90 WORD length;
91 WORD language;
92 WORD segCountX2;
93 WORD searchRange;
94 WORD entrySelector;
95 WORD rangeShift;
96 WORD endCode[1];
97 } CMAP_SegmentMapping_0;
99 /* PANOSE is 10 bytes in size, need to pack the structure properly */
100 #include "pshpack2.h"
101 typedef struct
103 ULONG version;
104 ULONG revision;
105 ULONG checksumadj;
106 ULONG magic;
107 USHORT flags;
108 USHORT unitsPerEm;
109 ULONGLONG created;
110 ULONGLONG modified;
111 SHORT xMin;
112 SHORT yMin;
113 SHORT xMax;
114 SHORT yMax;
115 USHORT macStyle;
116 USHORT lowestRecPPEM;
117 SHORT direction_hint;
118 SHORT index_format;
119 SHORT glyphdata_format;
120 } TT_HEAD;
122 typedef struct
124 ULONG Version;
125 ULONG italicAngle;
126 SHORT underlinePosition;
127 SHORT underlineThickness;
128 ULONG fixed_pitch;
129 ULONG minmemType42;
130 ULONG maxmemType42;
131 ULONG minmemType1;
132 ULONG maxmemType1;
133 } TT_POST;
135 typedef struct
137 USHORT version;
138 SHORT xAvgCharWidth;
139 USHORT usWeightClass;
140 USHORT usWidthClass;
141 SHORT fsType;
142 SHORT ySubscriptXSize;
143 SHORT ySubscriptYSize;
144 SHORT ySubscriptXOffset;
145 SHORT ySubscriptYOffset;
146 SHORT ySuperscriptXSize;
147 SHORT ySuperscriptYSize;
148 SHORT ySuperscriptXOffset;
149 SHORT ySuperscriptYOffset;
150 SHORT yStrikeoutSize;
151 SHORT yStrikeoutPosition;
152 SHORT sFamilyClass;
153 PANOSE panose;
154 ULONG ulUnicodeRange1;
155 ULONG ulUnicodeRange2;
156 ULONG ulUnicodeRange3;
157 ULONG ulUnicodeRange4;
158 CHAR achVendID[4];
159 USHORT fsSelection;
160 USHORT usFirstCharIndex;
161 USHORT usLastCharIndex;
162 /* According to the Apple spec, original version didn't have the below fields,
163 * version numbers were taken from the OpenType spec.
165 /* version 0 (TrueType 1.5) */
166 USHORT sTypoAscender;
167 USHORT sTypoDescender;
168 USHORT sTypoLineGap;
169 USHORT usWinAscent;
170 USHORT usWinDescent;
171 /* version 1 (TrueType 1.66) */
172 ULONG ulCodePageRange1;
173 ULONG ulCodePageRange2;
174 /* version 2 (OpenType 1.2) */
175 SHORT sxHeight;
176 SHORT sCapHeight;
177 USHORT usDefaultChar;
178 USHORT usBreakChar;
179 USHORT usMaxContext;
180 } TT_OS2_V2;
181 #include "poppack.h"
183 HRESULT opentype_analyze_font(IDWriteFontFileStream *stream, UINT32* font_count, DWRITE_FONT_FILE_TYPE *file_type, DWRITE_FONT_FACE_TYPE *face_type, BOOL *supported)
185 /* TODO: Do font validation */
186 const void *font_data;
187 const char* tag;
188 void *context;
189 HRESULT hr;
191 hr = IDWriteFontFileStream_ReadFileFragment(stream, &font_data, 0, sizeof(TTC_Header_V1), &context);
192 if (FAILED(hr))
193 return hr;
195 tag = font_data;
196 *supported = FALSE;
197 *file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
198 if (face_type)
199 *face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
200 *font_count = 0;
202 if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_TTCF_TAG)
204 const TTC_Header_V1 *header = font_data;
205 *font_count = GET_BE_DWORD(header->numFonts);
206 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE_COLLECTION;
207 if (face_type)
208 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION;
209 *supported = TRUE;
211 else if (GET_BE_DWORD(*(DWORD*)font_data) == 0x10000)
213 *font_count = 1;
214 *file_type = DWRITE_FONT_FILE_TYPE_TRUETYPE;
215 if (face_type)
216 *face_type = DWRITE_FONT_FACE_TYPE_TRUETYPE;
217 *supported = TRUE;
219 else if (DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]) == MS_OTTO_TAG)
221 *file_type = DWRITE_FONT_FILE_TYPE_CFF;
224 IDWriteFontFileStream_ReleaseFileFragment(stream, context);
225 return S_OK;
228 HRESULT find_font_table(IDWriteFontFileStream *stream, UINT32 font_index, UINT32 tag, const void** table_data, void** table_context, UINT32 *table_size, BOOL* found)
230 const CHAR *first_data;
231 void *first_context;
232 HRESULT hr;
233 TTC_SFNT_V1 *font_header = NULL;
234 void *sfnt_context;
235 TT_TableRecord *table_record = NULL;
236 void *table_record_context;
237 int i;
238 int table_count;
239 int table_offset = 0;
241 *found = FALSE;
243 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&first_data, 0, 4, &first_context);
244 if (SUCCEEDED(hr))
246 if (DWRITE_MAKE_OPENTYPE_TAG(first_data[0], first_data[1], first_data[2], first_data[3]) == MS_TTCF_TAG)
248 const TTC_Header_V1 *ttc_header;
249 void * ttc_context;
250 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&ttc_header, 0, sizeof(*ttc_header), &ttc_context);
251 if (SUCCEEDED(hr))
253 table_offset = GET_BE_DWORD(ttc_header->OffsetTable[0]);
254 if (font_index >= GET_BE_DWORD(ttc_header->numFonts))
255 hr = E_INVALIDARG;
256 else
257 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, table_offset, sizeof(*font_header), &sfnt_context);
258 IDWriteFontFileStream_ReleaseFileFragment(stream, ttc_context);
261 else
263 if (font_index > 0)
264 hr = E_INVALIDARG;
265 else
266 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&font_header, 0, sizeof(*font_header), &sfnt_context);
268 IDWriteFontFileStream_ReleaseFileFragment(stream, first_context);
270 if (FAILED(hr))
271 return hr;
273 table_count = GET_BE_WORD(font_header->numTables);
274 table_offset += sizeof(*font_header);
275 for (i = 0; i < table_count; i++)
277 hr = IDWriteFontFileStream_ReadFileFragment(stream, (const void**)&table_record, table_offset, sizeof(*table_record), &table_record_context);
278 if (FAILED(hr))
279 break;
280 if (DWRITE_MAKE_OPENTYPE_TAG(table_record->tag[0], table_record->tag[1], table_record->tag[2], table_record->tag[3]) == tag)
281 break;
282 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
283 table_offset += sizeof(*table_record);
286 IDWriteFontFileStream_ReleaseFileFragment(stream, sfnt_context);
287 if (SUCCEEDED(hr) && i < table_count)
289 int offset = GET_BE_DWORD(table_record->offset);
290 int length = GET_BE_DWORD(table_record->length);
291 IDWriteFontFileStream_ReleaseFileFragment(stream, table_record_context);
293 *found = TRUE;
294 *table_size = length;
295 hr = IDWriteFontFileStream_ReadFileFragment(stream, table_data, offset, length, table_context);
298 return hr;
301 /**********
302 * CMAP
303 **********/
305 static int compare_group(const void *a, const void* b)
307 const DWORD *chr = a;
308 const CMAP_SegmentedCoverage_group *group = b;
310 if (*chr < GET_BE_DWORD(group->startCharCode))
311 return -1;
312 if (*chr > GET_BE_DWORD(group->endCharCode))
313 return 1;
314 return 0;
317 static void CMAP4_GetGlyphIndex(CMAP_SegmentMapping_0* format, DWORD utf32c, LPWORD pgi)
319 WORD *startCode;
320 SHORT *idDelta;
321 WORD *idRangeOffset;
322 int segment;
324 int segment_count = GET_BE_WORD(format->segCountX2)/2;
325 /* This is correct because of the padding before startCode */
326 startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count));
327 idDelta = (SHORT*)(((BYTE*)startCode) + (sizeof(WORD) * segment_count));
328 idRangeOffset = (WORD*)(((BYTE*)idDelta) + (sizeof(WORD) * segment_count));
330 segment = 0;
331 while(GET_BE_WORD(format->endCode[segment]) < 0xffff)
333 if (utf32c <= GET_BE_WORD(format->endCode[segment]))
334 break;
335 segment++;
337 if (segment >= segment_count)
338 return;
339 TRACE("Segment %i of %i\n",segment, segment_count);
340 if (GET_BE_WORD(startCode[segment]) > utf32c)
341 return;
342 TRACE("In range %i -> %i\n", GET_BE_WORD(startCode[segment]), GET_BE_WORD(format->endCode[segment]));
343 if (GET_BE_WORD(idRangeOffset[segment]) == 0)
345 *pgi = (SHORT)(GET_BE_WORD(idDelta[segment])) + utf32c;
347 else
349 WORD ro = GET_BE_WORD(idRangeOffset[segment])/2;
350 WORD co = (utf32c - GET_BE_WORD(startCode[segment]));
351 WORD *index = (WORD*)((BYTE*)&idRangeOffset[segment] + (ro + co));
352 *pgi = GET_BE_WORD(*index);
356 static void CMAP12_GetGlyphIndex(CMAP_SegmentedCoverage* format, DWORD utf32c, LPWORD pgi)
358 CMAP_SegmentedCoverage_group *group = NULL;
360 group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups),
361 sizeof(CMAP_SegmentedCoverage_group), compare_group);
363 if (group)
365 DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode);
366 *pgi = GET_BE_DWORD(group->startGlyphID) + offset;
370 VOID OpenType_CMAP_GetGlyphIndex(LPVOID data, DWORD utf32c, LPWORD pgi, DWORD flags)
372 int i;
373 CMAP_Header *CMAP_Table = NULL;
375 if (flags & GGI_MARK_NONEXISTING_GLYPHS)
376 *pgi = 0xffff;
377 else
378 *pgi = 0;
380 CMAP_Table = data;
382 for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++)
384 WORD type;
385 WORD *table;
387 if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3)
388 continue;
390 table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset));
391 type = GET_BE_WORD(*table);
392 TRACE("Type %i\n", type);
393 /* Break when we find a handled type */
394 switch(type)
396 case 4:
397 CMAP4_GetGlyphIndex((CMAP_SegmentMapping_0*) table, utf32c, pgi);
398 break;
399 case 12:
400 CMAP12_GetGlyphIndex((CMAP_SegmentedCoverage*) table, utf32c, pgi);
401 break;
402 default:
403 TRACE("Type %i unhandled.\n", type);
408 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)
410 TT_OS2_V2 *tt_os2 = (TT_OS2_V2*)os2;
411 TT_HEAD *tt_head = (TT_HEAD*)head;
412 TT_POST *tt_post = (TT_POST*)post;
414 /* default stretch, weight and style to normal */
415 *stretch = DWRITE_FONT_STRETCH_NORMAL;
416 *weight = DWRITE_FONT_WEIGHT_NORMAL;
417 *style = DWRITE_FONT_STYLE_NORMAL;
419 memset(metrics, 0, sizeof(*metrics));
421 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
422 if (tt_os2)
424 if (GET_BE_WORD(tt_os2->usWidthClass) <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
425 *stretch = GET_BE_WORD(tt_os2->usWidthClass);
427 *weight = GET_BE_WORD(tt_os2->usWeightClass);
428 TRACE("stretch=%d, weight=%d\n", *stretch, *weight);
430 metrics->ascent = GET_BE_WORD(tt_os2->sTypoAscender);
431 metrics->descent = GET_BE_WORD(tt_os2->sTypoDescender);
432 metrics->lineGap = GET_BE_WORD(tt_os2->sTypoLineGap);
433 metrics->capHeight = GET_BE_WORD(tt_os2->sCapHeight);
434 metrics->xHeight = GET_BE_WORD(tt_os2->sxHeight);
435 metrics->strikethroughPosition = GET_BE_WORD(tt_os2->yStrikeoutPosition);
436 metrics->strikethroughThickness = GET_BE_WORD(tt_os2->yStrikeoutSize);
439 if (tt_head)
441 USHORT macStyle = GET_BE_WORD(tt_head->macStyle);
442 metrics->designUnitsPerEm = GET_BE_WORD(tt_head->unitsPerEm);
443 if (macStyle & 0x0002)
444 *style = DWRITE_FONT_STYLE_ITALIC;
448 if (tt_post)
450 metrics->underlinePosition = GET_BE_WORD(tt_post->underlinePosition);
451 metrics->underlineThickness = GET_BE_WORD(tt_post->underlineThickness);