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