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
convertSpecToPath(FSSpec * s,char * p,int pLen)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(¶ms);
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
convertPathToSpec(const char * path,const int pathlength,FSSpec * spec)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
setenv(const char * env,char * p)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 *
getenv(const char * env)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
mac_stdio_init(gx_io_device * iodev,gs_memory_t * mem)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
mac_stdin_open(gx_io_device * iodev,const char * access,stream ** ps,gs_memory_t * mem)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
mac_stdout_open(gx_io_device * iodev,const char * access,stream ** ps,gs_memory_t * mem)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
mac_stderr_open(gx_io_device * iodev,const char * access,stream ** ps,gs_memory_t * mem)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
mac_std_init(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
mac_stdin_read_process(stream_state * st,stream_cursor_read * ignore_pr,stream_cursor_write * pw,bool last)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
mac_stdout_write_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * ignore_pw,bool last)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
mac_stderr_write_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * ignore_pw,bool last)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
mac_std_available(register stream * s,long * pl)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 *
gp_open_printer(char * fname,int binary_mode)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
gp_close_printer(FILE * pfile,const char * fname)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
gp_setmode_binary(FILE * pfile,bool binary)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 *
gp_open_scratch_file(const char * prefix,char fname[gp_file_name_sizeof],const char * mode)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
gp_read_macresource(byte * buf,const char * fname,const uint type,const ushort id)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 */
gp_native_fontmap(char * names[],char * paths[],int * count)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 *
gp_enumerate_files_init(const char * pat,uint patlen,gs_memory_t * memory)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
gp_enumerate_files_next(file_enum * pfen,char * ptr,uint maxlen)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
gp_enumerate_files_close(file_enum * pfen)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 *
gp_fopen(const char * fname,const char * mode)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 *
popen(const char * fname,const char * mode)553 popen (const char * fname, const char * mode ) {
554 return gp_fopen (fname, mode);
555 }
556
557 int
pclose(FILE * pipe)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 */
compare_UniStr(HFSUniStr255 u,const char * c,uint len)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
gp_file_name_root(const char * fname,uint len)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 */
gp_file_name_root(const char * fname,uint len)618 uint gp_file_name_root(const char *fname, uint len)
619 {
620 return 0;
621 }
622
623 #endif /* __CARBON__ */
624
625
gs_file_name_check_separator(const char * fname,int len,const char * item)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
gp_file_name_is_parent(const char * fname,uint len)642 bool gp_file_name_is_parent(const char *fname, uint len)
643 { return len == 1 && fname[0] == ':';
644 }
645
gp_file_name_is_current(const char * fname,uint len)646 bool gp_file_name_is_current(const char *fname, uint len)
647 { return (len == 0) || (len == 1 && fname[0] == ':');
648 }
649
gp_file_name_separator(void)650 const char *gp_file_name_separator(void)
651 { return ":";
652 }
653
gp_file_name_directory_separator(void)654 const char *gp_file_name_directory_separator(void)
655 { return ":";
656 }
657
gp_file_name_parent(void)658 const char *gp_file_name_parent(void)
659 { return "::";
660 }
661
gp_file_name_current(void)662 const char *gp_file_name_current(void)
663 { return ":";
664 }
665
gp_file_name_is_partent_allowed(void)666 bool gp_file_name_is_partent_allowed(void)
667 { return true;
668 }
669
gp_file_name_is_empty_item_meanful(void)670 bool gp_file_name_is_empty_item_meanful(void)
671 { return true;
672 }
673
674 gp_file_name_combine_result
gp_file_name_combine(const char * prefix,uint plen,const char * fname,uint flen,bool no_sibling,char * buffer,uint * blen)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!
MacStr2c(char * pstring)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
fond_table_new(int entries)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
fond_table_free(fond_table * table)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
fond_table_grow(fond_table * table,int entries)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
get_int16(unsigned char * p)745 static int get_int16(unsigned char *p) {
746 return (p[0]&0xFF)<<8 | (p[1]&0xFF);
747 }
748
get_int32(unsigned char * p)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 */
parse_fond(FSSpec * spec)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 */
is_ttf_file(const char * path)824 static int is_ttf_file(const char *path)
825 {
826 int len = strlen(path);
827 return !memcmp(path+len-4,".ttf",4);
828 }
is_otf_file(const char * path)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
strip_char(char * string,int len,const int c)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 */
makePSFontName(FMFontFamily Family,FMFontStyle Style)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
gp_enumerate_fonts_init(gs_memory_t * mem)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
gp_enumerate_fonts_free(void * enum_state)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
gp_enumerate_fonts_next(void * enum_state,char ** fontname,char ** path)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
1020