Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1989-2003 artofcode, LLC.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
/* $Id: gp_macio.c,v 1.37 2004/12/09 03:47:52 giles Exp $ */
18
 
19
#ifndef __CARBON__
20
//#include "MacHeaders"
21
#include <Palettes.h>
22
#include <Aliases.h>
23
#include <Quickdraw.h>
24
#include <QDOffscreen.h>
25
#include <AppleEvents.h>
26
#include <Fonts.h>
27
#include <Controls.h>
28
#include <Script.h>
29
#include <Timer.h>
30
#include <Folders.h>
31
#include <Resources.h>
32
#include <Sound.h>
33
#include <ToolUtils.h>
34
#include <Menus.h>
35
#include <LowMem.h>
36
#include <Devices.h>
37
#include <Scrap.h>
38
#include <StringCompare.h>
39
#include <Gestalt.h>
40
#include <Folders.h>
41
#include <Files.h>
42
#include <Fonts.h>
43
#include <FixMath.h>
44
#include <Resources.h>
45
#else
46
#include <Carbon.h>
47
#include <CoreServices.h>
48
#endif /* __CARBON__ */
49
 
50
#include "stdio_.h"
51
#include "math_.h"
52
#include "string_.h"
53
#include <stdlib.h>
54
#include <stdarg.h>
55
#include <console.h>
56
 
57
#include "gx.h"
58
#include "gp.h"
59
#include "gpmisc.h"
60
#include "gxdevice.h"
61
 
62
#include "gp_mac.h"
63
 
64
#include "stream.h"
65
#include "gxiodev.h"			/* must come after stream.h */
66
//#include "gp_macAE.h"
67
#include "gsdll.h"
68
 
69
//HWND hwndtext;
70
 
71
 
72
extern void
73
convertSpecToPath(FSSpec * s, char * p, int pLen)
74
{
75
	OSStatus	err = noErr;
76
	CInfoPBRec	params;
77
	Str255		dirName;
78
	int		totLen = 0, dirLen = 0;
79
 
80
	memcpy(p, s->name + 1, s->name[0]);
81
	totLen += s->name[0];
82
 
83
	params.dirInfo.ioNamePtr = dirName;
84
	params.dirInfo.ioVRefNum = s->vRefNum;
85
	params.dirInfo.ioDrParID = s->parID;
86
	params.dirInfo.ioFDirIndex = -1;
87
 
88
	do {
89
		params.dirInfo.ioDrDirID = params.dirInfo.ioDrParID;
90
		err = PBGetCatInfoSync(&params);
91
 
92
		if ((err != noErr) || (totLen + dirName[0] + 2 > pLen)) {
93
			p[0] = 0;
94
			return;
95
		}
96
 
97
		dirName[++dirName[0]] = ':';
98
		memmove(p + dirName[0], p, totLen);
99
		memcpy(p, dirName + 1, dirName[0]);
100
		totLen += dirName[0];
101
	} while (params.dirInfo.ioDrParID != fsRtParID);
102
 
103
	p[totLen] = 0;
104
 
105
	return;
106
}
107
 
108
OSErr
109
convertPathToSpec(const char *path, const int pathlength, FSSpec * spec)
110
{
111
	Str255 filename;
112
 
113
	/* path must be shorter than 255 bytes */
114
	if (pathlength > 254) return bdNamErr;
115
 
116
	*filename = pathlength;
117
	memcpy(filename + 1, path, pathlength);
118
 
119
	return FSMakeFSSpec(0, 0, filename, spec);
120
}
121
 
122
/* ------ File name syntax ------ */
123
 
124
/* Define the character used for separating file names in a list. */
125
const char gp_file_name_list_separator = ',';
126
 
127
/* Define the default scratch file name prefix. */
128
const char gp_scratch_file_name_prefix[] = "tempgs_";
129
 
130
/* Define the name of the null output file. */
131
const char gp_null_file_name[] = "????";
132
 
133
/* Define the name that designates the current directory. */
134
extern const char gp_current_directory_name[] = ":";
135
 
136
int fake_stdin = 0;
137
 
138
 
139
/* Do platform-dependent initialization */
140
 
141
void
142
setenv(const char * env, char *p) {
143
//	if ( strcmp(env,"outfile") == 0) {
144
//	   sprintf((char *)&g_fcout[0],"%s",p);
145
//	}
146
}
147
 
148
char *
149
getenv(const char * env) {
150
 
151
	char 			*p;
152
	FSSpec			pFile;
153
	OSErr			err = 0;
154
	char			fpath[256]="";
155
 
156
	if ( strcmp(env,"GS_LIB") == 0) {
157
 
158
	    	pFile.name[0] = 0;
159
	    	err = FindFolder(kOnSystemDisk, kApplicationSupportFolderType, kDontCreateFolder,
160
			 					&pFile.vRefNum, &pFile.parID);
161
 
162
			if (err != noErr) goto failed;
163
 
164
//		FSMakeFSSpec(pFile.vRefNum, pFile.parID,thepfname, &pfile);
165
		convertSpecToPath(&pFile, fpath, 256);
166
//		sprintf(fpath,"%s",fpath);
167
		p = (char*)malloc((size_t) ( 4*strlen(fpath) + 40));
168
		sprintf(p,"%s,%sGhostscript:lib,%sGhostscript:fonts",
169
						(char *)&fpath[0],(char *)&fpath[0],
170
						(char *)&fpath[0] );
171
 
172
		return p;
173
failed:
174
 
175
		return NULL;
176
	} else
177
	    return NULL;
178
 
179
}
180
 
181
/* ====== Substitute for stdio ====== */
182
 
183
/* Forward references */
184
private void mac_std_init(void);
185
private stream_proc_process(mac_stdin_read_process);
186
private stream_proc_process(mac_stdout_write_process);
187
private stream_proc_process(mac_stderr_write_process);
188
private stream_proc_available(mac_std_available);
189
 
190
/* Use a pseudo IODevice to get mac_stdio_init called at the right time. */
191
/* This is bad architecture; we'll fix it later. */
192
private iodev_proc_init(mac_stdio_init);
193
const gx_io_device gs_iodev_macstdio =
194
{
195
    "macstdio", "Special",
196
    {mac_stdio_init, iodev_no_open_device,
197
     iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,
198
     iodev_no_delete_file, iodev_no_rename_file,
199
     iodev_no_file_status, iodev_no_enumerate_files
200
    }
201
};
202
 
203
/* Do one-time initialization */
204
private int
205
mac_stdio_init(gx_io_device * iodev, gs_memory_t * mem)
206
{
207
    mac_std_init();		/* redefine stdin/out/err to our window routines */
208
    return 0;
209
}
210
 
211
/* Define alternate 'open' routines for our stdin/out/err streams. */
212
 
213
extern const gx_io_device gs_iodev_stdin;
214
private int
215
mac_stdin_open(gx_io_device * iodev, const char *access, stream ** ps,
216
	       gs_memory_t * mem)
217
{
218
    int code = gs_iodev_stdin.procs.open_device(iodev, access, ps, mem);
219
    stream *s = *ps;
220
 
221
    if (code != 1)
222
	return code;
223
    s->procs.process = mac_stdin_read_process;
224
    s->procs.available = mac_std_available;
225
    s->file = NULL;
226
    return 0;
227
}
228
 
229
extern const gx_io_device gs_iodev_stdout;
230
private int
231
mac_stdout_open(gx_io_device * iodev, const char *access, stream ** ps,
232
		gs_memory_t * mem)
233
{
234
    int code = gs_iodev_stdout.procs.open_device(iodev, access, ps, mem);
235
    stream *s = *ps;
236
 
237
    if (code != 1)
238
	return code;
239
    s->procs.process = mac_stdout_write_process;
240
    s->procs.available = mac_std_available;
241
    s->file = NULL;
242
    return 0;
243
}
244
 
245
extern const gx_io_device gs_iodev_stderr;
246
private int
247
mac_stderr_open(gx_io_device * iodev, const char *access, stream ** ps,
248
		gs_memory_t * mem)
249
{
250
    int code = gs_iodev_stderr.procs.open_device(iodev, access, ps, mem);
251
    stream *s = *ps;
252
 
253
    if (code != 1)
254
	return code;
255
    s->procs.process = mac_stderr_write_process;
256
    s->procs.available = mac_std_available;
257
    s->file = NULL;
258
    return 0;
259
}
260
 
261
/* Patch stdin/out/err to use our windows. */
262
private void
263
mac_std_init(void)
264
{
265
    /* If stdxxx is the console, replace the 'open' routines, */
266
    /* which haven't gotten called yet. */
267
 
268
//    if (gp_file_is_console(gs_stdin))
269
	gs_findiodevice((const byte *)"%stdin", 6)->procs.open_device =
270
	    mac_stdin_open;
271
 
272
//    if (gp_file_is_console(gs_stdout))
273
	gs_findiodevice((const byte *)"%stdout", 7)->procs.open_device =
274
	    mac_stdout_open;
275
 
276
//    if (gp_file_is_console(gs_stderr))
277
	gs_findiodevice((const byte *)"%stderr", 7)->procs.open_device =
278
	    mac_stderr_open;
279
}
280
 
281
 
282
private int
283
mac_stdin_read_process(stream_state *st, stream_cursor_read *ignore_pr,
284
  stream_cursor_write *pw, bool last)
285
{
286
    uint count = pw->limit - pw->ptr;
287
    /* callback to get more input */
288
    if (pgsdll_callback == NULL) return EOFC;
289
    count = (*pgsdll_callback) (GSDLL_STDIN, (char*)pw->ptr + 1, count);
290
	pw->ptr += count;	
291
	return 1;
292
}
293
 
294
 
295
private int
296
mac_stdout_write_process(stream_state *st, stream_cursor_read *pr,
297
  stream_cursor_write *ignore_pw, bool last)
298
{	uint count = pr->limit - pr->ptr;
299
 
300
    if (pgsdll_callback == NULL) return EOFC;
301
    (*pgsdll_callback) (GSDLL_STDOUT, (char *)(pr->ptr + 1), count);
302
	pr->ptr = pr->limit;
303
	return 0;
304
}
305
 
306
private int
307
mac_stderr_write_process(stream_state *st, stream_cursor_read *pr,
308
  stream_cursor_write *ignore_pw, bool last)
309
{	uint count = pr->limit - pr->ptr;
310
 
311
    if (pgsdll_callback == NULL) return EOFC;
312
    (*pgsdll_callback) (GSDLL_STDOUT, (char *)(pr->ptr + 1), count);
313
	pr->ptr = pr->limit;
314
	return 0;
315
}
316
 
317
private int
318
mac_std_available(register stream * s, long *pl)
319
{
320
    *pl = -1;		// EOF, since we can't do it
321
    return 0;		// OK
322
}
323
 
324
/* ------ Printer accessing ------ */
325
 
326
/* These should NEVER be called. */
327
 
328
/* Open a connection to a printer.  A null file name means use the */
329
/* standard printer connected to the machine, if any. */
330
/* "|command" opens an output pipe. */
331
/* Return NULL if the connection could not be opened. */
332
 
333
FILE *
334
gp_open_printer (char *fname, int binary_mode)
335
{
336
    if (strlen(fname) == 0)
337
        return gp_open_scratch_file(gp_scratch_file_name_prefix, fname, binary_mode ? "wb" : "w");
338
    else
339
        return gp_fopen(fname, binary_mode ? "wb" : "b");
340
}
341
 
342
/* Close the connection to the printer. */
343
 
344
void
345
gp_close_printer (FILE *pfile, const char *fname)
346
{
347
    fclose(pfile);
348
}
349
 
350
 
351
/* Define whether case is insignificant in file names. */
352
/* OBSOLETE
353
const int gp_file_names_ignore_case = 1;
354
*/
355
 
356
/* Define the string to be concatenated with the file mode */
357
/* for opening files without end-of-line conversion. */
358
const char gp_fmode_binary_suffix[] = "b";
359
 
360
/* Define the file modes for binary reading or writing. */
361
const char gp_fmode_rb[] = "rb";
362
const char gp_fmode_wb[] = "wb";
363
 
364
 
365
/* Set a file into binary or text mode. */
366
int
367
gp_setmode_binary(FILE *pfile, bool binary)
368
{	return 0;	/* Noop under VMS */
369
}
370
 
371
 
372
/* Create and open a scratch file with a given name prefix. */
373
/* Write the actual file name at fname. */
374
 
375
FILE *
376
gp_open_scratch_file (const char *prefix, char fname[gp_file_name_sizeof], const char *mode)
377
{
378
    char thefname[256];
379
    Str255 thepfname;
380
    OSErr myErr;
381
    short foundVRefNum;
382
    long foundDirID;
383
    FSSpec fSpec;
384
    FILE *f;
385
    int prefix_length = strlen(prefix);
386
 
387
    if (prefix_length > gp_file_name_sizeof) return NULL;
388
    strcpy (fname, (char *) prefix);
389
      {
390
	char newName[50];
391
 
392
	tmpnam (newName);
393
	if ( prefix_length + strlen(newName) > gp_file_name_sizeof ) return NULL;
394
	strcat (fname, newName);
395
      }
396
 
397
   if ( strlen(fname) > 255 ) return NULL;
398
   if ( strrchr(fname,':') == NULL ) {
399
       memmove((char*)&thepfname[1],(char *)&fname[0],strlen(fname));
400
	   thepfname[0]=strlen(fname);
401
		myErr = FindFolder(kOnSystemDisk,kTemporaryFolderType,kCreateFolder,
402
			&foundVRefNum, &foundDirID);
403
		if ( myErr != noErr ) {
404
			eprintf("Can't find temp folder.\n");
405
			return (NULL);
406
		}
407
		FSMakeFSSpec(foundVRefNum, foundDirID,thepfname, &fSpec);
408
		convertSpecToPath(&fSpec, thefname, sizeof(thefname) - 1);
409
		sprintf(fname,"%s",thefname);
410
   } else {
411
       sprintf((char*)&thefname[0],"%s\0",fname);
412
       memmove((char*)&thepfname[1],(char *)&thefname[0],strlen(thefname));
413
	   thepfname[0]=strlen(thefname);
414
   }
415
 
416
    f = gp_fopen (thefname, mode);
417
    if (f == NULL)
418
	eprintf1("**** Could not open temporary file %s\n", fname);
419
    return f;
420
}
421
 
422
/* read a resource and copy the data into a buffer */
423
/* we don't have access to an allocator, nor any context for local  */
424
/* storage, so we implement the following idiom: we return the size */
425
/* of the requested resource and copy the data into buf iff it's    */
426
/* non-NULL. Thus, the caller can pass NULL for buf the first time, */
427
/* allocate the appropriate sized buffer, and then call us a second */
428
/* time to actually transfer the data.                              */
429
int
430
gp_read_macresource(byte *buf, const char *fname, const uint type, const ushort id)
431
{
432
    Handle resource = NULL;
433
    SInt32 size = 0;
434
    FSSpec spec;
435
    SInt16 fileref;
436
    OSErr result;
437
 
438
    /* open file */
439
    result = convertPathToSpec(fname, strlen(fname), &spec);
440
    if (result != noErr) goto fin;
441
    fileref = FSpOpenResFile(&spec, fsRdPerm);
442
    if (fileref == -1) goto fin;
443
 
444
    if_debug1('s', "[s] loading resource from fileref %d\n", fileref);
445
 
446
    /* load resource */
447
    resource = Get1Resource((ResType)type, (SInt16)id);
448
    if (resource == NULL) goto fin;
449
 
450
    /* allocate res */
451
    /* GetResourceSize() is probably good enough */
452
    //size = GetResourceSizeOnDisk(resource);
453
    size = GetMaxResourceSize(resource);
454
 
455
    if_debug1('s', "[s] resource size on disk is %d bytes\n", size);
456
 
457
    /* if we don't have a buffer to fill, just return */
458
    if (buf == NULL) goto fin;
459
 
460
    /* otherwise, copy resource into res from handle */
461
    HLock(resource);
462
    memcpy(buf, *resource, size);
463
    HUnlock(resource);
464
 
465
fin:
466
    /* free resource, if necessary */
467
    ReleaseResource(resource);
468
    CloseResFile(fileref);
469
 
470
    return (size);
471
}
472
 
473
/* return a list of font names and corresponding paths from 
474
 * the native system locations
475
 */
476
int gp_native_fontmap(char *names[], char *paths[], int *count)
477
{
478
    return 0;
479
}
480
 
481
/* ------ File enumeration ------ */
482
 
483
/****** THIS IS NOT SUPPORTED ON MACINTOSH SYSTEMS. ******/
484
 
485
struct file_enum_s {
486
	char *pattern;
487
	int first_time;
488
	gs_memory_t *memory;
489
};
490
 
491
/* Initialize an enumeration.  NEEDS WORK ON HANDLING * ? \. */
492
 
493
file_enum *
494
gp_enumerate_files_init (const char *pat, uint patlen, gs_memory_t *memory)
495
 
496
{	file_enum *pfen = 
497
		(file_enum *)gs_alloc_bytes(memory, sizeof(file_enum), "gp_enumerate_files");
498
	char *pattern;
499
	if ( pfen == 0 ) return 0;
500
	pattern = 
501
		(char *)gs_alloc_bytes(memory, patlen + 1, "gp_enumerate_files(pattern)");
502
	if ( pattern == 0 ) return 0;
503
	memcpy(pattern, pat, patlen);
504
	pattern[patlen] = 0;
505
	pfen->pattern = pattern;
506
	pfen->memory = memory;
507
	pfen->first_time = 1;
508
	return pfen;
509
}
510
 
511
/* Enumerate the next file. */
512
 
513
uint
514
gp_enumerate_files_next (file_enum *pfen, char *ptr, uint maxlen)
515
 
516
{	if ( pfen->first_time )
517
	   {	pfen->first_time = 0;
518
	   }
519
	return -1;
520
}
521
 
522
/* Clean up the file enumeration. */
523
 
524
void
525
gp_enumerate_files_close (file_enum *pfen)
526
 
527
{	
528
	gs_free_object(pfen->memory, pfen->pattern, "gp_enumerate_files_close(pattern)");
529
	gs_free_object(pfen->memory, (char *)pfen, "gp_enumerate_files_close");
530
}
531
 
532
FILE * 
533
gp_fopen (const char * fname, const char * mode) {
534
 
535
   char thefname[256];
536
   FILE *fid;
537
 
538
//sprintf((char*)&thefname[0],"\n%s\n",fname);
539
//(*pgsdll_callback) (GSDLL_STDOUT, thefname, strlen(fname));
540
   if ( strrchr(fname,':') == NULL ) 
541
//      sprintf((char *)&thefname[0],"%s%s\0",g_homeDir,fname);
542
      sprintf((char *)&thefname[0],"%s%s\0","",fname);
543
   else
544
       sprintf((char*)&thefname[0],"%s\0",fname);
545
 
546
   fid = fopen(thefname,mode);
547
 
548
   return fid;
549
 
550
}
551
 
552
FILE * 
553
popen (const char * fname, const char * mode ) {
554
	return gp_fopen (fname,  mode);
555
}
556
 
557
int
558
pclose (FILE * pipe ) {
559
	return fclose (pipe);
560
}
561
 
562
/* -------------- Helpers for gp_file_name_combine_generic ------------- */
563
 
564
#ifdef __CARBON__
565
 
566
/* compare an HFSUnitStr255 with a C string */
567
static int compare_UniStr(HFSUniStr255 u, const char *c, uint len)
568
{
569
	int i,searchlen,unichar;
570
	searchlen = min(len,u.length);
571
	for (i = 0; i < searchlen; i++) {
572
	  unichar = u.unicode[i];
573
	  /* punt on wide characters. we should really convert */
574
	  if (unichar & !0xFF) return -1;
575
	  /* otherwise return the the index of the first non-matching character */
576
	  if (unichar != c[i]) break;
577
	}
578
	/* return the offset iff we matched the whole volume name */
579
	return (i == u.length) ? i : 0;
580
}
581
 
582
uint gp_file_name_root(const char *fname, uint len)
583
{
584
	OSErr err = noErr;
585
   	HFSUniStr255 volumeName;
586
   	FSRef rootDirectory;
587
   	int index, match;
588
 
589
    if (len > 0 && fname[0] == ':')
590
		return 0; /* A relative path, no root. */
591
 
592
	/* iterate over mounted volumes and compare our path */
593
	index = 1;
594
	while (err == noErr) {
595
		err = FSGetVolumeInfo (kFSInvalidVolumeRefNum, index,
596
			NULL, kFSVolInfoNone, NULL, /* not interested in these fields */
597
			&volumeName, &rootDirectory);
598
		if (err == nsvErr) return 0; /* no more volumes */
599
		if (err == noErr) {
600
			match = compare_UniStr(volumeName, fname, len);
601
			if (match > 0) {
602
    			/* include the separator if it's present  */
603
				if (fname[match] == ':') return match + 1;
604
				return match;
605
			}
606
		}
607
		index++;
608
	}
609
 
610
	/* nothing matched */
611
    return 0;
612
}
613
 
614
#else /* Classic MacOS */
615
 
616
/* FSGetVolumeInfo requires carbonlib or macos >= 9
617
   we essentially leave this unimplemented on Classic */
618
uint gp_file_name_root(const char *fname, uint len)
619
{
620
	return 0;
621
}
622
 
623
#endif /* __CARBON__ */
624
 
625
 
626
uint gs_file_name_check_separator(const char *fname, int len, const char *item)
627
{   if (len > 0) {
628
	if (fname[0] == ':') {
629
	    if (fname == item + 1 && item[0] == ':')
630
		return 1; /* It is a separator after parent. */
631
	    if (len > 1 && fname[1] == ':')
632
		return 0; /* It is parent, not a separator. */
633
	    return 1;
634
	}
635
    } else if (len < 0) {
636
	if (fname[-1] == ':')
637
	    return 1;
638
    }
639
    return 0;
640
}
641
 
642
bool gp_file_name_is_parent(const char *fname, uint len)
643
{   return len == 1 && fname[0] == ':';
644
}
645
 
646
bool gp_file_name_is_current(const char *fname, uint len)
647
{   return (len == 0) || (len == 1 && fname[0] == ':');
648
}
649
 
650
const char *gp_file_name_separator(void)
651
{   return ":";
652
}
653
 
654
const char *gp_file_name_directory_separator(void)
655
{   return ":";
656
}
657
 
658
const char *gp_file_name_parent(void)
659
{   return "::";
660
}
661
 
662
const char *gp_file_name_current(void)
663
{   return ":";
664
}
665
 
666
bool gp_file_name_is_partent_allowed(void)
667
{   return true;
668
}
669
 
670
bool gp_file_name_is_empty_item_meanful(void)
671
{   return true;
672
}
673
 
674
gp_file_name_combine_result
675
gp_file_name_combine(const char *prefix, uint plen, const char *fname, uint flen, 
676
		    bool no_sibling, char *buffer, uint *blen)
677
{
678
    return gp_file_name_combine_generic(prefix, plen, 
679
	    fname, flen, no_sibling, buffer, blen);
680
}
681
 
682
// FIXME: there must be a system util for this!
683
static char *MacStr2c(char *pstring)
684
{
685
	char *cstring;
686
	int len = (pstring[0] < 256) ? pstring[0] : 255;
687
 
688
	if (len == 0) return NULL;
689
 
690
	cstring = malloc(len + 1);
691
	if (cstring != NULL) {
692
		memcpy(cstring, &(pstring[1]), len);
693
		cstring[len] = '\0';
694
	}
695
 
696
	return(cstring);
697
}
698
 
699
/* ------ Font enumeration ------ */
700
 
701
 /* This is used to query the native os for a list of font names and
702
  * corresponding paths. The general idea is to save the hassle of
703
  * building a custom fontmap file
704
  */
705
 
706
typedef struct {
707
    int size, style, id;
708
} fond_entry;
709
 
710
typedef struct {
711
    int entries;
712
    fond_entry *refs;
713
} fond_table;
714
 
715
static fond_table *fond_table_new(int entries)
716
{
717
    fond_table *table = malloc(sizeof(fond_table));
718
    if (table != NULL) {
719
        table->entries = entries;
720
        table->refs = malloc(entries * sizeof(fond_entry));
721
        if (table->refs == NULL) { free(table); table = NULL; }
722
    }
723
    return table;
724
}
725
 
726
static void fond_table_free(fond_table *table)
727
{
728
    if (table != NULL) {
729
        if (table->refs) free(table->refs);
730
        free(table);
731
    }
732
}
733
 
734
static fond_table *fond_table_grow(fond_table *table, int entries)
735
{
736
    if (table == NULL) {
737
        table = fond_table_new(entries);
738
    } else {
739
        table->entries += entries;
740
        table->refs = realloc(table->refs, table->entries * sizeof(fond_entry));
741
    }
742
    return table;
743
}
744
 
745
static int get_int16(unsigned char *p) {
746
    return (p[0]&0xFF)<<8 | (p[1]&0xFF);
747
}
748
 
749
static int get_int32(unsigned char *p) {
750
    return (p[0]&0xFF)<<24 | (p[1]&0xFF)<<16 | (p[2]&0xFF)<<8 | (p[3]&0xFF);
751
}
752
 
753
/* parse and summarize FOND resource information */
754
static fond_table * parse_fond(FSSpec *spec)
755
{
756
    OSErr result = noErr;
757
    FSRef specref;
758
    SInt16 ref;
759
    Handle fond = NULL;
760
    unsigned char *res;
761
    fond_table *table = NULL;
762
    int i,j, count, n, start;
763
 
764
	/* FSpOpenResFile will fail for data fork resource (.dfont) files.
765
	   FSOpenResourceFile can open either, but cannot handle broken resource
766
	   maps, as often occurs in font files (the suitcase version of Arial,
767
	   for example) Thus, we try one, and then the other. */
768
 
769
    result = FSpMakeFSRef(spec,&specref);
770
#ifdef __CARBON__
771
   	if (result == noErr)
772
   		result = FSOpenResourceFile(&specref, 0, NULL, fsRdPerm, &ref);
773
#else
774
	result = bdNamErr; /* simulate failure of the carbon routine above */
775
#endif
776
    if (result != noErr) {
777
	    ref = FSpOpenResFile(spec, fsRdPerm);
778
	    result = ResError();
779
	}
780
    if (result != noErr || ref <= 0) {
781
    	char path[256];
782
    	convertSpecToPath(spec, path, 256);
783
      	dlprintf2("unable to open resource file '%s' for font enumeration (error %d)\n",
784
      		path, result);
785
      	goto fin;
786
    }
787
 
788
    /* we've opened the font file, now loop over the FOND resource(s)
789
       and construct a table of the font references */
790
 
791
    start = 0; /* number of entries so far */
792
    UseResFile(ref);
793
    count = Count1Resources('FOND');
794
    for (i = 0; i < count; i++) {
795
        fond = Get1IndResource('FOND', i+1);
796
        if (fond == NULL) {
797
            result = ResError();
798
            goto fin;
799
        }
800
 
801
        /* The FOND resource structure corresponds to the FamRec and AsscEntry
802
           data structures documented in the FontManager reference. However,
803
           access to these types is deprecated in Carbon. We therefore access the
804
           data by direct offset in the hope that the resource format will not change
805
           even if api access to the in-memory versions goes away. */
806
        HLock(fond);
807
        res = *fond + 52; /* offset to association table */
808
        n = get_int16(res) + 1;	res += 2;
809
		table = fond_table_grow(table, n);
810
        for (j = start; j < start + n; j++ ) {
811
            table->refs[j].size = get_int16(res); res += 2;
812
            table->refs[j].style = get_int16(res); res += 2;
813
            table->refs[j].id = get_int16(res); res += 2;
814
        }
815
        start += n;
816
        HUnlock(fond);
817
    }
818
fin:
819
    CloseResFile(ref);
820
    return table;
821
}
822
 
823
/* FIXME: should check for uppercase as well */
824
static int is_ttf_file(const char *path)
825
{
826
    int len = strlen(path);
827
    return !memcmp(path+len-4,".ttf",4);
828
}
829
static int is_otf_file(const char *path)
830
{
831
    int len = strlen(path);
832
    return !memcmp(path+len-4,".otf",4);
833
}
834
 
835
static void strip_char(char *string, int len, const int c)
836
{
837
    char *bit;
838
    len += 1;
839
    while(bit = strchr(string,' ')) {
840
        memmove(bit, bit + 1, string + len - bit - 1);
841
    }
842
}
843
 
844
/* get the macos name for the font instance and mangle it into a PS
845
   fontname */
846
static char *makePSFontName(FMFontFamily Family, FMFontStyle Style)
847
{
848
	Str255 Name;
849
	OSStatus result;
850
	int length;
851
	char *stylename, *fontname;
852
	char *psname;
853
 
854
	result = FMGetFontFamilyName(Family, Name);
855
	if (result != noErr) return NULL;
856
	fontname = MacStr2c(Name);
857
	if (fontname == NULL) return NULL;
858
	strip_char(fontname, strlen(fontname), ' ');
859
 
860
	switch (Style) {
861
		case 0: stylename=""; break;;
862
		case 1: stylename="Bold"; break;;
863
		case 2: stylename="Italic"; break;;
864
		case 3: stylename="BoldItalic"; break;;
865
		default: stylename="Unknown"; break;;
866
	}
867
 
868
	length = strlen(fontname) + strlen(stylename) + 2;
869
	psname = malloc(length);
870
	if (Style != 0)
871
		snprintf(psname, length, "%s-%s", fontname, stylename);
872
	else
873
		snprintf(psname, length, "%s", fontname);
874
 
875
	free(fontname);
876
 
877
	return psname;	
878
}
879
 
880
typedef struct {
881
    int count;
882
    FMFontIterator Iterator;
883
    char *name;
884
    char *path;
885
    FSSpec last_container;
886
    char *last_container_path;
887
    fond_table *last_table;
888
} fontenum_t;
889
 
890
void *gp_enumerate_fonts_init(gs_memory_t *mem)
891
{
892
    fontenum_t *state = gs_alloc_bytes(mem, sizeof(fontenum_t),
893
	"macos font enumerator state");
894
	FMFontIterator *Iterator = &state->Iterator;
895
	OSStatus result;
896
 
897
    if (state != NULL) {
898
		state->count = 0;
899
		state->name = NULL;
900
		state->path = NULL;
901
		result = FMCreateFontIterator(NULL, NULL,
902
			kFMLocalIterationScope, Iterator);
903
		if (result != noErr) return NULL;
904
		memset(&state->last_container, 0, sizeof(FSSpec));
905
		state->last_container_path = NULL;
906
		state->last_table = NULL;
907
    }
908
 
909
    return (void *)state;
910
}
911
 
912
void gp_enumerate_fonts_free(void *enum_state)
913
{
914
    fontenum_t *state = (fontenum_t *)enum_state;
915
	FMFontIterator *Iterator = &state->Iterator;
916
 
917
	FMDisposeFontIterator(Iterator);
918
 
919
    /* free any malloc'd stuff here */
920
    if (state->name) free(state->name);
921
    if (state->path) free(state->path);
922
    if (state->last_container_path) free(state->last_container_path);
923
    if (state->last_table) fond_table_free(state->last_table);
924
    /* the garbage collector will take care of the struct itself */
925
 
926
}
927
 
928
int gp_enumerate_fonts_next(void *enum_state, char **fontname, char **path)
929
{
930
    fontenum_t *state = (fontenum_t *)enum_state;
931
	FMFontIterator *Iterator = &state->Iterator;
932
	FMFont Font;
933
	FourCharCode Format;
934
	FMFontFamily FontFamily;
935
	FMFontStyle Style;
936
	FSSpec FontContainer;
937
	char type[5];
938
	char fontpath[256];
939
	char *psname;
940
	fond_table *table = NULL;
941
	OSStatus result;
942
 
943
	result = FMGetNextFont(Iterator, &Font);
944
    if (result != noErr) return 0; /* no more fonts */
945
 
946
	result = FMGetFontFormat(Font, &Format);
947
	type[0] = ((char*)&Format)[0];
948
	type[1] = ((char*)&Format)[1];
949
	type[2] = ((char*)&Format)[2];
950
	type[3] = ((char*)&Format)[3];
951
	type[4] = '\0';
952
 
953
 	FMGetFontFamilyInstanceFromFont(Font, &FontFamily, &Style);
954
    if (state->name) free (state->name);
955
 
956
    psname = makePSFontName(FontFamily, Style);
957
    if (psname == NULL) {
958
		state->name = strdup("GSPlaceHolder");
959
	} else {
960
		state->name = psname;
961
	}
962
 
963
	result = FMGetFontContainer(Font, &FontContainer);
964
	if (!memcmp(&FontContainer, &state->last_container, sizeof(FSSpec))) {
965
		/* we have cached data on this file */
966
		strncpy(fontpath, state->last_container_path, 256);
967
		table = state->last_table;
968
	} else {
969
		convertSpecToPath(&FontContainer, fontpath, 256);
970
		if (!is_ttf_file(fontpath) && !is_otf_file(fontpath))
971
	    	table = parse_fond(&FontContainer);
972
	    /* cache data on the new font file */
973
	    memcpy(&state->last_container, &FontContainer, sizeof(FSSpec));
974
	    if (state->last_container_path) free (state->last_container_path);
975
		state->last_container_path = strdup(fontpath);
976
		if (state->last_table) fond_table_free(state->last_table);
977
		state->last_table = table;
978
	}
979
 
980
	if (state->path) {
981
		free(state->path);
982
		state->path = NULL;
983
	}
984
    if (table != NULL) {
985
    	int i;
986
    	for (i = 0; i < table->entries; i++) {
987
            if (table->refs[i].size == 0) { /* ignore non-scalable fonts */
988
                if (table->refs[i].style == Style) {
989
                    int len = strlen(fontpath) + strlen("%macresource%#sfnt+") + 6;
990
                	state->path = malloc(len);
991
                    snprintf(state->path, len, "%%macresource%%%s#sfnt+%d", 
992
                        fontpath, table->refs[i].id);
993
                    break;
994
                }
995
            }
996
        }
997
    } else {
998
        /* regular font file */
999
        state->path = strdup(fontpath);
1000
    }
1001
    if (state->path == NULL) {
1002
    	/* no matching font was found in the FOND resource table. this usually */
1003
    	/* means an LWFN file, which we don't handle yet. */
1004
    	/* we still specify these with a %macresource% path, but no res id */
1005
    	/* TODO: check file type */
1006
    	int len = strlen(fontpath) + strlen("%macresource%#POST") + 1;
1007
    	state->path = malloc(len);
1008
    	snprintf(state->path, len, "%%macresource%%%s#POST", fontpath);
1009
    }
1010
#ifdef DEBUG
1011
    dlprintf2("fontenum: returning '%s' in '%s'\n", state->name, state->path);
1012
#endif
1013
    *fontname = state->name;
1014
    *path = state->path;
1015
 
1016
	state->count += 1;
1017
	return 1;
1018
}
1019