- Merge from trunk up to r45543
[reactos.git] / lib / 3rdparty / freetype / src / base / ftmac.c
1 /***************************************************************************/
2 /* */
3 /* ftmac.c */
4 /* */
5 /* Mac FOND support. Written by just@letterror.com. */
6 /* Heavily modified by mpsuzuki, George Williams, and Sean McBride. */
7 /* */
8 /* This file is for Mac OS X only; see builds/mac/ftoldmac.c for */
9 /* classic platforms built by MPW. */
10 /* */
11 /* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
12 /* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */
13 /* */
14 /* This file is part of the FreeType project, and may only be used, */
15 /* modified, and distributed under the terms of the FreeType project */
16 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
17 /* this file you indicate that you have read the license and */
18 /* understand and accept it fully. */
19 /* */
20 /***************************************************************************/
21
22
23 /*
24 Notes
25
26 Mac suitcase files can (and often do!) contain multiple fonts. To
27 support this I use the face_index argument of FT_(Open|New)_Face()
28 functions, and pretend the suitcase file is a collection.
29
30 Warning: fbit and NFNT bitmap resources are not supported yet. In old
31 sfnt fonts, bitmap glyph data for each size is stored in each `NFNT'
32 resources instead of the `bdat' table in the sfnt resource. Therefore,
33 face->num_fixed_sizes is set to 0, because bitmap data in `NFNT'
34 resource is unavailable at present.
35
36 The Mac FOND support works roughly like this:
37
38 - Check whether the offered stream points to a Mac suitcase file. This
39 is done by checking the file type: it has to be 'FFIL' or 'tfil'. The
40 stream that gets passed to our init_face() routine is a stdio stream,
41 which isn't usable for us, since the FOND resources live in the
42 resource fork. So we just grab the stream->pathname field.
43
44 - Read the FOND resource into memory, then check whether there is a
45 TrueType font and/or(!) a Type 1 font available.
46
47 - If there is a Type 1 font available (as a separate `LWFN' file), read
48 its data into memory, massage it slightly so it becomes PFB data, wrap
49 it into a memory stream, load the Type 1 driver and delegate the rest
50 of the work to it by calling FT_Open_Face(). (XXX TODO: after this
51 has been done, the kerning data from the FOND resource should be
52 appended to the face: On the Mac there are usually no AFM files
53 available. However, this is tricky since we need to map Mac char
54 codes to ps glyph names to glyph ID's...)
55
56 - If there is a TrueType font (an `sfnt' resource), read it into memory,
57 wrap it into a memory stream, load the TrueType driver and delegate
58 the rest of the work to it, by calling FT_Open_Face().
59
60 - Some suitcase fonts (notably Onyx) might point the `LWFN' file to
61 itself, even though it doesn't contains `POST' resources. To handle
62 this special case without opening the file an extra time, we just
63 ignore errors from the `LWFN' and fallback to the `sfnt' if both are
64 available.
65 */
66
67
68 #include <ft2build.h>
69 #include FT_FREETYPE_H
70 #include FT_INTERNAL_STREAM_H
71
72 /* This is for Mac OS X. Without redefinition, OS_INLINE */
73 /* expands to `static inline' which doesn't survive the */
74 /* -ansi compilation flag of GCC. */
75 #if !HAVE_ANSI_OS_INLINE
76 #undef OS_INLINE
77 #define OS_INLINE static __inline__
78 #endif
79 #include <Carbon/Carbon.h>
80
81 #ifndef HFS_MAXPATHLEN
82 #define HFS_MAXPATHLEN 1024
83 #endif
84
85 #define FT_DEPRECATED_ATTRIBUTE
86
87 #include FT_MAC_H
88
89 /* undefine blocking-macros in ftmac.h */
90 #undef FT_GetFile_From_Mac_Name( a, b, c )
91 #undef FT_GetFile_From_Mac_ATS_Name( a, b, c )
92 #undef FT_New_Face_From_FSSpec( a, b, c, d )
93
94
95 /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
96 TrueType in case *both* are available (this is not common,
97 but it *is* possible). */
98 #ifndef PREFER_LWFN
99 #define PREFER_LWFN 1
100 #endif
101
102
103 FT_EXPORT_DEF( FT_Error )
104 FT_GetFile_From_Mac_Name( const char* fontName,
105 FSSpec* pathSpec,
106 FT_Long* face_index )
107 {
108 FT_UNUSED( fontName );
109 FT_UNUSED( pathSpec );
110 FT_UNUSED( face_index );
111
112 return FT_Err_Unimplemented_Feature;
113 }
114
115
116 /* Private function. */
117 /* The FSSpec type has been discouraged for a long time, */
118 /* but for some reason, there is no FSRef version of */
119 /* ATSFontGetFileSpecification(), so we made our own. */
120 /* Apple will provide one eventually. */
121 static OSStatus
122 FT_ATSFontGetFileReference( ATSFontRef ats_font_id,
123 FSRef* ats_font_ref )
124 {
125 #if __LP64__
126 FT_UNUSED( ats_font_id );
127 FT_UNUSED( ats_font_ref );
128
129 return fnfErr;
130 #else
131 OSStatus err;
132 FSSpec spec;
133
134
135 err = ATSFontGetFileSpecification( ats_font_id, &spec );
136 if ( noErr == err )
137 err = FSpMakeFSRef( &spec, ats_font_ref );
138
139 return err;
140 #endif
141 }
142
143
144 static FT_Error
145 FT_GetFileRef_From_Mac_ATS_Name( const char* fontName,
146 FSRef* ats_font_ref,
147 FT_Long* face_index )
148 {
149 CFStringRef cf_fontName;
150 ATSFontRef ats_font_id;
151
152
153 *face_index = 0;
154
155 cf_fontName = CFStringCreateWithCString( NULL, fontName,
156 kCFStringEncodingMacRoman );
157 ats_font_id = ATSFontFindFromName( cf_fontName,
158 kATSOptionFlagsUnRestrictedScope );
159 CFRelease( cf_fontName );
160
161 if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
162 return FT_Err_Unknown_File_Format;
163
164 if ( noErr != FT_ATSFontGetFileReference( ats_font_id, ats_font_ref ) )
165 return FT_Err_Unknown_File_Format;
166
167 /* face_index calculation by searching preceding fontIDs */
168 /* with same FSRef */
169 {
170 ATSFontRef id2 = ats_font_id - 1;
171 FSRef ref2;
172
173
174 while ( id2 > 0 )
175 {
176 if ( noErr != FT_ATSFontGetFileReference( id2, &ref2 ) )
177 break;
178 if ( noErr != FSCompareFSRefs( ats_font_ref, &ref2 ) )
179 break;
180
181 id2 --;
182 }
183 *face_index = ats_font_id - ( id2 + 1 );
184 }
185
186 return FT_Err_Ok;
187 }
188
189
190 FT_EXPORT_DEF( FT_Error )
191 FT_GetFilePath_From_Mac_ATS_Name( const char* fontName,
192 UInt8* path,
193 UInt32 maxPathSize,
194 FT_Long* face_index )
195 {
196 FSRef ref;
197 FT_Error err;
198
199
200 err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
201 if ( FT_Err_Ok != err )
202 return err;
203
204 if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) )
205 return FT_Err_Unknown_File_Format;
206
207 return FT_Err_Ok;
208 }
209
210
211 /* This function is deprecated because FSSpec is deprecated in Mac OS X */
212 FT_EXPORT_DEF( FT_Error )
213 FT_GetFile_From_Mac_ATS_Name( const char* fontName,
214 FSSpec* pathSpec,
215 FT_Long* face_index )
216 {
217 #if __LP64__
218 FT_UNUSED( fontName );
219 FT_UNUSED( pathSpec );
220 FT_UNUSED( face_index );
221
222 return FT_Err_Unimplemented_Feature;
223 #else
224 FSRef ref;
225 FT_Error err;
226
227
228 err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
229 if ( FT_Err_Ok != err )
230 return err;
231
232 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL,
233 pathSpec, NULL ) )
234 return FT_Err_Unknown_File_Format;
235
236 return FT_Err_Ok;
237 #endif
238 }
239
240
241 static OSErr
242 FT_FSPathMakeRes( const UInt8* pathname,
243 short* res )
244 {
245 OSErr err;
246 FSRef ref;
247
248
249 if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
250 return FT_Err_Cannot_Open_Resource;
251
252 /* at present, no support for dfont format */
253 err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res );
254 if ( noErr == err )
255 return err;
256
257 /* fallback to original resource-fork font */
258 *res = FSOpenResFile( &ref, fsRdPerm );
259 err = ResError();
260
261 return err;
262 }
263
264
265 /* Return the file type for given pathname */
266 static OSType
267 get_file_type_from_path( const UInt8* pathname )
268 {
269 FSRef ref;
270 FSCatalogInfo info;
271
272
273 if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
274 return ( OSType ) 0;
275
276 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info,
277 NULL, NULL, NULL ) )
278 return ( OSType ) 0;
279
280 return ((FInfo *)(info.finderInfo))->fdType;
281 }
282
283
284 /* Given a PostScript font name, create the Macintosh LWFN file name. */
285 static void
286 create_lwfn_name( char* ps_name,
287 Str255 lwfn_file_name )
288 {
289 int max = 5, count = 0;
290 FT_Byte* p = lwfn_file_name;
291 FT_Byte* q = (FT_Byte*)ps_name;
292
293
294 lwfn_file_name[0] = 0;
295
296 while ( *q )
297 {
298 if ( ft_isupper( *q ) )
299 {
300 if ( count )
301 max = 3;
302 count = 0;
303 }
304 if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
305 {
306 *++p = *q;
307 lwfn_file_name[0]++;
308 count++;
309 }
310 q++;
311 }
312 }
313
314
315 static short
316 count_faces_sfnt( char* fond_data )
317 {
318 /* The count is 1 greater than the value in the FOND. */
319 /* Isn't that cute? :-) */
320
321 return EndianS16_BtoN( *( (short*)( fond_data +
322 sizeof ( FamRec ) ) ) ) + 1;
323 }
324
325
326 static short
327 count_faces_scalable( char* fond_data )
328 {
329 AsscEntry* assoc;
330 FamRec* fond;
331 short i, face, face_all;
332
333
334 fond = (FamRec*)fond_data;
335 face_all = EndianS16_BtoN( *( (short *)( fond_data +
336 sizeof ( FamRec ) ) ) ) + 1;
337 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
338 face = 0;
339
340 for ( i = 0; i < face_all; i++ )
341 {
342 if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
343 face++;
344 }
345 return face;
346 }
347
348
349 /* Look inside the FOND data, answer whether there should be an SFNT
350 resource, and answer the name of a possible LWFN Type 1 file.
351
352 Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
353 to load a face OTHER than the first one in the FOND!
354 */
355
356
357 static void
358 parse_fond( char* fond_data,
359 short* have_sfnt,
360 short* sfnt_id,
361 Str255 lwfn_file_name,
362 short face_index )
363 {
364 AsscEntry* assoc;
365 AsscEntry* base_assoc;
366 FamRec* fond;
367
368
369 *sfnt_id = 0;
370 *have_sfnt = 0;
371 lwfn_file_name[0] = 0;
372
373 fond = (FamRec*)fond_data;
374 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
375 base_assoc = assoc;
376
377 /* Let's do a little range checking before we get too excited here */
378 if ( face_index < count_faces_sfnt( fond_data ) )
379 {
380 assoc += face_index; /* add on the face_index! */
381
382 /* if the face at this index is not scalable,
383 fall back to the first one (old behavior) */
384 if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
385 {
386 *have_sfnt = 1;
387 *sfnt_id = EndianS16_BtoN( assoc->fontID );
388 }
389 else if ( base_assoc->fontSize == 0 )
390 {
391 *have_sfnt = 1;
392 *sfnt_id = EndianS16_BtoN( base_assoc->fontID );
393 }
394 }
395
396 if ( EndianS32_BtoN( fond->ffStylOff ) )
397 {
398 unsigned char* p = (unsigned char*)fond_data;
399 StyleTable* style;
400 unsigned short string_count;
401 char ps_name[256];
402 unsigned char* names[64];
403 int i;
404
405
406 p += EndianS32_BtoN( fond->ffStylOff );
407 style = (StyleTable*)p;
408 p += sizeof ( StyleTable );
409 string_count = EndianS16_BtoN( *(short*)(p) );
410 p += sizeof ( short );
411
412 for ( i = 0; i < string_count && i < 64; i++ )
413 {
414 names[i] = p;
415 p += names[i][0];
416 p++;
417 }
418
419 {
420 size_t ps_name_len = (size_t)names[0][0];
421
422
423 if ( ps_name_len != 0 )
424 {
425 ft_memcpy(ps_name, names[0] + 1, ps_name_len);
426 ps_name[ps_name_len] = 0;
427 }
428 if ( style->indexes[0] > 1 )
429 {
430 unsigned char* suffixes = names[style->indexes[0] - 1];
431
432
433 for ( i = 1; i <= suffixes[0]; i++ )
434 {
435 unsigned char* s;
436 size_t j = suffixes[i] - 1;
437
438
439 if ( j < string_count && ( s = names[j] ) != NULL )
440 {
441 size_t s_len = (size_t)s[0];
442
443
444 if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
445 {
446 ft_memcpy( ps_name + ps_name_len, s + 1, s_len );
447 ps_name_len += s_len;
448 ps_name[ps_name_len] = 0;
449 }
450 }
451 }
452 }
453 }
454
455 create_lwfn_name( ps_name, lwfn_file_name );
456 }
457 }
458
459
460 static FT_Error
461 lookup_lwfn_by_fond( const UInt8* path_fond,
462 ConstStr255Param base_lwfn,
463 UInt8* path_lwfn,
464 size_t path_size )
465 {
466 FSRef ref, par_ref;
467 int dirname_len;
468
469
470 /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */
471 /* We should not extract parent directory by string manipulation. */
472
473 if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) )
474 return FT_Err_Invalid_Argument;
475
476 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
477 NULL, NULL, NULL, &par_ref ) )
478 return FT_Err_Invalid_Argument;
479
480 if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) )
481 return FT_Err_Invalid_Argument;
482
483 if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size )
484 return FT_Err_Invalid_Argument;
485
486 /* now we have absolute dirname in path_lwfn */
487 ft_strcat( (char *)path_lwfn, "/" );
488 dirname_len = ft_strlen( (char *)path_lwfn );
489 ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 );
490 path_lwfn[dirname_len + base_lwfn[0]] = '\0';
491
492 if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) )
493 return FT_Err_Cannot_Open_Resource;
494
495 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
496 NULL, NULL, NULL, NULL ) )
497 return FT_Err_Cannot_Open_Resource;
498
499 return FT_Err_Ok;
500 }
501
502
503 static short
504 count_faces( Handle fond,
505 const UInt8* pathname )
506 {
507 short sfnt_id;
508 short have_sfnt, have_lwfn;
509 Str255 lwfn_file_name;
510 UInt8 buff[HFS_MAXPATHLEN];
511 FT_Error err;
512 short num_faces;
513
514
515 have_sfnt = have_lwfn = 0;
516
517 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
518
519 if ( lwfn_file_name[0] )
520 {
521 err = lookup_lwfn_by_fond( pathname, lwfn_file_name,
522 buff, sizeof ( buff ) );
523 if ( FT_Err_Ok == err )
524 have_lwfn = 1;
525 }
526
527 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
528 num_faces = 1;
529 else
530 num_faces = count_faces_scalable( *fond );
531
532 return num_faces;
533 }
534
535
536 /* Read Type 1 data from the POST resources inside the LWFN file,
537 return a PFB buffer. This is somewhat convoluted because the FT2
538 PFB parser wants the ASCII header as one chunk, and the LWFN
539 chunks are often not organized that way, so we glue chunks
540 of the same type together. */
541 static FT_Error
542 read_lwfn( FT_Memory memory,
543 short res,
544 FT_Byte** pfb_data,
545 FT_ULong* size )
546 {
547 FT_Error error = FT_Err_Ok;
548 short res_id;
549 unsigned char *buffer, *p, *size_p = NULL;
550 FT_ULong total_size = 0;
551 FT_ULong old_total_size = 0;
552 FT_ULong post_size, pfb_chunk_size;
553 Handle post_data;
554 char code, last_code;
555
556
557 UseResFile( res );
558
559 /* First pass: load all POST resources, and determine the size of */
560 /* the output buffer. */
561 res_id = 501;
562 last_code = -1;
563
564 for (;;)
565 {
566 post_data = Get1Resource( 'POST', res_id++ );
567 if ( post_data == NULL )
568 break; /* we are done */
569
570 code = (*post_data)[0];
571
572 if ( code != last_code )
573 {
574 if ( code == 5 )
575 total_size += 2; /* just the end code */
576 else
577 total_size += 6; /* code + 4 bytes chunk length */
578 }
579
580 total_size += GetHandleSize( post_data ) - 2;
581 last_code = code;
582
583 /* detect integer overflows */
584 if ( total_size < old_total_size )
585 {
586 error = FT_Err_Array_Too_Large;
587 goto Error;
588 }
589
590 old_total_size = total_size;
591 }
592
593 if ( FT_ALLOC( buffer, (FT_Long)total_size ) )
594 goto Error;
595
596 /* Second pass: append all POST data to the buffer, add PFB fields. */
597 /* Glue all consecutive chunks of the same type together. */
598 p = buffer;
599 res_id = 501;
600 last_code = -1;
601 pfb_chunk_size = 0;
602
603 for (;;)
604 {
605 post_data = Get1Resource( 'POST', res_id++ );
606 if ( post_data == NULL )
607 break; /* we are done */
608
609 post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
610 code = (*post_data)[0];
611
612 if ( code != last_code )
613 {
614 if ( last_code != -1 )
615 {
616 /* we are done adding a chunk, fill in the size field */
617 if ( size_p != NULL )
618 {
619 *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF );
620 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF );
621 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
622 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
623 }
624 pfb_chunk_size = 0;
625 }
626
627 *p++ = 0x80;
628 if ( code == 5 )
629 *p++ = 0x03; /* the end */
630 else if ( code == 2 )
631 *p++ = 0x02; /* binary segment */
632 else
633 *p++ = 0x01; /* ASCII segment */
634
635 if ( code != 5 )
636 {
637 size_p = p; /* save for later */
638 p += 4; /* make space for size field */
639 }
640 }
641
642 ft_memcpy( p, *post_data + 2, post_size );
643 pfb_chunk_size += post_size;
644 p += post_size;
645 last_code = code;
646 }
647
648 *pfb_data = buffer;
649 *size = total_size;
650
651 Error:
652 CloseResFile( res );
653 return error;
654 }
655
656
657 /* Finalizer for a memory stream; gets called by FT_Done_Face().
658 It frees the memory it uses. */
659 static void
660 memory_stream_close( FT_Stream stream )
661 {
662 FT_Memory memory = stream->memory;
663
664
665 FT_FREE( stream->base );
666
667 stream->size = 0;
668 stream->base = 0;
669 stream->close = 0;
670 }
671
672
673 /* Create a new memory stream from a buffer and a size. */
674 static FT_Error
675 new_memory_stream( FT_Library library,
676 FT_Byte* base,
677 FT_ULong size,
678 FT_Stream_CloseFunc close,
679 FT_Stream* astream )
680 {
681 FT_Error error;
682 FT_Memory memory;
683 FT_Stream stream;
684
685
686 if ( !library )
687 return FT_Err_Invalid_Library_Handle;
688
689 if ( !base )
690 return FT_Err_Invalid_Argument;
691
692 *astream = 0;
693 memory = library->memory;
694 if ( FT_NEW( stream ) )
695 goto Exit;
696
697 FT_Stream_OpenMemory( stream, base, size );
698
699 stream->close = close;
700
701 *astream = stream;
702
703 Exit:
704 return error;
705 }
706
707
708 /* Create a new FT_Face given a buffer and a driver name. */
709 static FT_Error
710 open_face_from_buffer( FT_Library library,
711 FT_Byte* base,
712 FT_ULong size,
713 FT_Long face_index,
714 char* driver_name,
715 FT_Face* aface )
716 {
717 FT_Open_Args args;
718 FT_Error error;
719 FT_Stream stream;
720 FT_Memory memory = library->memory;
721
722
723 error = new_memory_stream( library,
724 base,
725 size,
726 memory_stream_close,
727 &stream );
728 if ( error )
729 {
730 FT_FREE( base );
731 return error;
732 }
733
734 args.flags = FT_OPEN_STREAM;
735 args.stream = stream;
736 if ( driver_name )
737 {
738 args.flags = args.flags | FT_OPEN_DRIVER;
739 args.driver = FT_Get_Module( library, driver_name );
740 }
741
742 /* At this point, face_index has served its purpose; */
743 /* whoever calls this function has already used it to */
744 /* locate the correct font data. We should not propagate */
745 /* this index to FT_Open_Face() (unless it is negative). */
746
747 if ( face_index > 0 )
748 face_index = 0;
749
750 error = FT_Open_Face( library, &args, face_index, aface );
751 if ( error == FT_Err_Ok )
752 (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
753 else
754 FT_Stream_Free( stream, 0 );
755
756 return error;
757 }
758
759
760 /* Create a new FT_Face from a file spec to an LWFN file. */
761 static FT_Error
762 FT_New_Face_From_LWFN( FT_Library library,
763 const UInt8* pathname,
764 FT_Long face_index,
765 FT_Face* aface )
766 {
767 FT_Byte* pfb_data;
768 FT_ULong pfb_size;
769 FT_Error error;
770 short res;
771
772
773 if ( noErr != FT_FSPathMakeRes( pathname, &res ) )
774 return FT_Err_Cannot_Open_Resource;
775
776 pfb_data = NULL;
777 pfb_size = 0;
778 error = read_lwfn( library->memory, res, &pfb_data, &pfb_size );
779 CloseResFile( res ); /* PFB is already loaded, useless anymore */
780 if ( error )
781 return error;
782
783 return open_face_from_buffer( library,
784 pfb_data,
785 pfb_size,
786 face_index,
787 "type1",
788 aface );
789 }
790
791
792 /* Create a new FT_Face from an SFNT resource, specified by res ID. */
793 static FT_Error
794 FT_New_Face_From_SFNT( FT_Library library,
795 short sfnt_id,
796 FT_Long face_index,
797 FT_Face* aface )
798 {
799 Handle sfnt = NULL;
800 FT_Byte* sfnt_data;
801 size_t sfnt_size;
802 FT_Error error = FT_Err_Ok;
803 FT_Memory memory = library->memory;
804 int is_cff;
805
806
807 sfnt = GetResource( 'sfnt', sfnt_id );
808 if ( ResError() )
809 return FT_Err_Invalid_Handle;
810
811 sfnt_size = (FT_ULong)GetHandleSize( sfnt );
812 if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
813 {
814 ReleaseResource( sfnt );
815 return error;
816 }
817
818 ft_memcpy( sfnt_data, *sfnt, sfnt_size );
819 ReleaseResource( sfnt );
820
821 is_cff = sfnt_size > 4 && sfnt_data[0] == 'O' &&
822 sfnt_data[1] == 'T' &&
823 sfnt_data[2] == 'T' &&
824 sfnt_data[3] == 'O';
825
826 return open_face_from_buffer( library,
827 sfnt_data,
828 sfnt_size,
829 face_index,
830 is_cff ? "cff" : "truetype",
831 aface );
832 }
833
834
835 /* Create a new FT_Face from a file spec to a suitcase file. */
836 static FT_Error
837 FT_New_Face_From_Suitcase( FT_Library library,
838 const UInt8* pathname,
839 FT_Long face_index,
840 FT_Face* aface )
841 {
842 FT_Error error = FT_Err_Cannot_Open_Resource;
843 short res_ref, res_index;
844 Handle fond;
845 short num_faces_in_res, num_faces_in_fond;
846
847
848 if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
849 return FT_Err_Cannot_Open_Resource;
850
851 UseResFile( res_ref );
852 if ( ResError() )
853 return FT_Err_Cannot_Open_Resource;
854
855 num_faces_in_res = 0;
856 for ( res_index = 1; ; ++res_index )
857 {
858 fond = Get1IndResource( 'FOND', res_index );
859 if ( ResError() )
860 break;
861
862 num_faces_in_fond = count_faces( fond, pathname );
863 num_faces_in_res += num_faces_in_fond;
864
865 if ( 0 <= face_index && face_index < num_faces_in_fond && error )
866 error = FT_New_Face_From_FOND( library, fond, face_index, aface );
867
868 face_index -= num_faces_in_fond;
869 }
870
871 CloseResFile( res_ref );
872 if ( FT_Err_Ok == error && NULL != aface )
873 (*aface)->num_faces = num_faces_in_res;
874 return error;
875 }
876
877
878 /* documentation is in ftmac.h */
879
880 FT_EXPORT_DEF( FT_Error )
881 FT_New_Face_From_FOND( FT_Library library,
882 Handle fond,
883 FT_Long face_index,
884 FT_Face* aface )
885 {
886 short sfnt_id, have_sfnt, have_lwfn = 0;
887 short fond_id;
888 OSType fond_type;
889 Str255 fond_name;
890 Str255 lwfn_file_name;
891 UInt8 path_lwfn[HFS_MAXPATHLEN];
892 OSErr err;
893 FT_Error error = FT_Err_Ok;
894
895
896 GetResInfo( fond, &fond_id, &fond_type, fond_name );
897 if ( ResError() != noErr || fond_type != 'FOND' )
898 return FT_Err_Invalid_File_Format;
899
900 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
901
902 if ( lwfn_file_name[0] )
903 {
904 short res;
905
906
907 res = HomeResFile( fond );
908 if ( noErr != ResError() )
909 goto found_no_lwfn_file;
910
911 {
912 UInt8 path_fond[HFS_MAXPATHLEN];
913 FSRef ref;
914
915
916 err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
917 NULL, NULL, NULL, &ref, NULL );
918 if ( noErr != err )
919 goto found_no_lwfn_file;
920
921 err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
922 if ( noErr != err )
923 goto found_no_lwfn_file;
924
925 error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
926 path_lwfn, sizeof ( path_lwfn ) );
927 if ( FT_Err_Ok == error )
928 have_lwfn = 1;
929 }
930 }
931
932 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
933 error = FT_New_Face_From_LWFN( library,
934 path_lwfn,
935 face_index,
936 aface );
937 else
938 error = FT_Err_Unknown_File_Format;
939
940 found_no_lwfn_file:
941 if ( have_sfnt && FT_Err_Ok != error )
942 error = FT_New_Face_From_SFNT( library,
943 sfnt_id,
944 face_index,
945 aface );
946
947 return error;
948 }
949
950
951 /* Common function to load a new FT_Face from a resource file. */
952 static FT_Error
953 FT_New_Face_From_Resource( FT_Library library,
954 const UInt8* pathname,
955 FT_Long face_index,
956 FT_Face* aface )
957 {
958 OSType file_type;
959 FT_Error error;
960
961
962 /* LWFN is a (very) specific file format, check for it explicitly */
963 file_type = get_file_type_from_path( pathname );
964 if ( file_type == 'LWFN' )
965 return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
966
967 /* Otherwise the file type doesn't matter (there are more than */
968 /* `FFIL' and `tfil'). Just try opening it as a font suitcase; */
969 /* if it works, fine. */
970
971 error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
972 if ( error == 0 )
973 return error;
974
975 /* let it fall through to normal loader (.ttf, .otf, etc.); */
976 /* we signal this by returning no error and no FT_Face */
977 *aface = NULL;
978 return 0;
979 }
980
981
982 /*************************************************************************/
983 /* */
984 /* <Function> */
985 /* FT_New_Face */
986 /* */
987 /* <Description> */
988 /* This is the Mac-specific implementation of FT_New_Face. In */
989 /* addition to the standard FT_New_Face() functionality, it also */
990 /* accepts pathnames to Mac suitcase files. For further */
991 /* documentation see the original FT_New_Face() in freetype.h. */
992 /* */
993 FT_EXPORT_DEF( FT_Error )
994 FT_New_Face( FT_Library library,
995 const char* pathname,
996 FT_Long face_index,
997 FT_Face* aface )
998 {
999 FT_Open_Args args;
1000 FT_Error error;
1001
1002
1003 /* test for valid `library' and `aface' delayed to FT_Open_Face() */
1004 if ( !pathname )
1005 return FT_Err_Invalid_Argument;
1006
1007 error = FT_Err_Ok;
1008 *aface = NULL;
1009
1010 /* try resourcefork based font: LWFN, FFIL */
1011 error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
1012 face_index, aface );
1013 if ( error != 0 || *aface != NULL )
1014 return error;
1015
1016 /* let it fall through to normal loader (.ttf, .otf, etc.) */
1017 args.flags = FT_OPEN_PATHNAME;
1018 args.pathname = (char*)pathname;
1019 return FT_Open_Face( library, &args, face_index, aface );
1020 }
1021
1022
1023 /*************************************************************************/
1024 /* */
1025 /* <Function> */
1026 /* FT_New_Face_From_FSRef */
1027 /* */
1028 /* <Description> */
1029 /* FT_New_Face_From_FSRef is identical to FT_New_Face except it */
1030 /* accepts an FSRef instead of a path. */
1031 /* */
1032 FT_EXPORT_DEF( FT_Error )
1033 FT_New_Face_From_FSRef( FT_Library library,
1034 const FSRef* ref,
1035 FT_Long face_index,
1036 FT_Face* aface )
1037 {
1038 FT_Error error;
1039 FT_Open_Args args;
1040 OSErr err;
1041 UInt8 pathname[HFS_MAXPATHLEN];
1042
1043
1044 if ( !ref )
1045 return FT_Err_Invalid_Argument;
1046
1047 err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
1048 if ( err )
1049 error = FT_Err_Cannot_Open_Resource;
1050
1051 error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1052 if ( error != 0 || *aface != NULL )
1053 return error;
1054
1055 /* fallback to datafork font */
1056 args.flags = FT_OPEN_PATHNAME;
1057 args.pathname = (char*)pathname;
1058 return FT_Open_Face( library, &args, face_index, aface );
1059 }
1060
1061
1062 /*************************************************************************/
1063 /* */
1064 /* <Function> */
1065 /* FT_New_Face_From_FSSpec */
1066 /* */
1067 /* <Description> */
1068 /* FT_New_Face_From_FSSpec is identical to FT_New_Face except it */
1069 /* accepts an FSSpec instead of a path. */
1070 /* */
1071 FT_EXPORT_DEF( FT_Error )
1072 FT_New_Face_From_FSSpec( FT_Library library,
1073 const FSSpec* spec,
1074 FT_Long face_index,
1075 FT_Face* aface )
1076 {
1077 #if __LP64__
1078 FT_UNUSED( library );
1079 FT_UNUSED( spec );
1080 FT_UNUSED( face_index );
1081 FT_UNUSED( aface );
1082
1083 return FT_Err_Unimplemented_Feature;
1084 #else
1085 FSRef ref;
1086
1087
1088 if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
1089 return FT_Err_Invalid_Argument;
1090 else
1091 return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
1092 #endif
1093 }
1094
1095
1096 /* END */