xref: /plan9/sys/src/cmd/gs/src/gsiomacres.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2002 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: gsiomacres.c,v 1.5 2003/09/03 03:22:59 giles Exp $ */
18 /* %macresource% IODevice implementation for loading MacOS fonts */
19 
20 /*
21  * MacOS often stores fonts in 'resource forks' an extended attribute
22  * of the HFS filesystem that allows storing chunks of data indexed by
23  * a resource 'type' and 'id'. MacOS X introduced the '.dfont' format
24  * for compatibility with non-HFS filesystems; it consists of the
25  * resource data for a font stored on the data fork (normal file).
26  *
27  * We provide here a special %macresource% IODevice so that Ghostscript
28  * can open these native fonts on MacOS. It opens the requested file and
29  * returns a stream containing the indicated resource data, whether from
30  * the resource fork or, if that fails, from the datafork assuming a
31  * .dfont file.
32  *
33  * Since the only use at this point is font loading, we don't implement
34  * the usual complement of delete, rename and so on.
35  */
36 
37 #include "std.h"
38 #include "stdio_.h"
39 #include "string_.h"
40 #include "malloc_.h"
41 #include "ierrors.h"
42 #include "gserror.h"
43 #include "gstypes.h"
44 #include "gsmemory.h"
45 #include "stream.h"
46 #include "gdebug.h"
47 #include "gxiodev.h"
48 #include "gp.h"
49 
50 /* dfont loading code */
51 
52 /* 4-byte resource types as uint32s */
53 #define TYPE_sfnt 0x73666e74
54 #define TYPE_FOND 0x464f4e44
55 #define TYPE_NFNT 0x4e464e54
56 #define TYPE_ftag 0x66746167
57 #define TYPE_vers 0x76657273
58 
59 typedef struct {
60 	unsigned int data_offset;
61 	unsigned int map_offset;
62 	unsigned int data_length;
63 	unsigned int map_length;
64 } resource_header;
65 
66 typedef struct {
67 	unsigned int type;
68 	unsigned int offset;
69 	unsigned int length;
70 	byte *data;
71 	char *name;
72 	unsigned short id;
73 	byte flags;
74 } resource;
75 
76 typedef struct {
77     resource *resources;
78     int n_resources;
79 } resource_list;
80 
81 private
get_int32(byte * p)82 int get_int32(byte *p) {
83     return (p[0]&0xFF)<<24 | (p[1]&0xFF)<<16 | (p[2]&0xFF)<<8 | (p[3]&0xFF);
84 }
85 
86 private
get_int24(byte * p)87 int get_int24(byte *p) {
88     return (p[0]&0xFF)<<16 | (p[1]&0xFF)<<8 | (p[2]&0xFF);
89 }
90 
91 private
get_int16(byte * p)92 int get_int16(byte *p) {
93     return (p[0]&0xFF)<<8 | (p[1]&0xFF);
94 }
95 
96 private
read_int32(FILE * in,void * p)97 int read_int32(FILE *in, void *p)
98 {
99 	byte w[4], err;
100 
101 	err = fread(w, 1, 4, in);
102 	if (err != 4) return -1;
103 
104 	*(unsigned int*)p = get_int32(w);
105 	return 0;
106 }
107 
108 private
read_int24(FILE * in,void * p)109 int read_int24(FILE *in, void *p)
110 {
111 	byte w[3], err;
112 
113 	err = fread(w, 1, 3, in);
114 	if (err != 3) return -1;
115 
116 	*(unsigned int*)p = get_int24(w);
117 	return 0;
118 }
119 
120 private
read_int16(FILE * in,void * p)121 int read_int16(FILE *in, void *p)
122 {
123 	byte w[2], err;
124 
125 	err = fread(w, 1, 2, in);
126 	if (err != 2) return -1;
127 
128 	*(unsigned short*)p = get_int16(w);
129 	return 0;
130 }
131 
132 private
read_int8(FILE * in,void * p)133 int read_int8(FILE *in, void *p)
134 {
135 	byte c = fgetc(in);
136 	if (c < 0) return -1;
137 
138     *(byte*)p = (c&0xFF);
139     return 0;
140 }
141 
142 /* convert a 4-character typecode from C string to uint32 representation */
res_string2type(const char * type_string)143 private unsigned int res_string2type(const char *type_string)
144 {
145 	unsigned int type = type_string[0] << 24 |
146            				type_string[1] << 16 |
147           				type_string[2] <<  8 |
148            				type_string[3];
149     return (type);
150 }
151 /* convert a 4-character typecode from unsigned int to C string representation */
res_type2string(const unsigned int type,char * type_string)152 private char * res_type2string(const unsigned int type, char *type_string)
153 {
154 	if (type_string == NULL) return NULL;
155 
156 	type_string[0] = (type >> 24) & 0xFF;
157     type_string[1] = (type >> 16) & 0xFF;
158     type_string[2] = (type >> 8) & 0xFF;
159     type_string[3] = (type) & 0xFF;
160     type_string[4] = '\0';
161 
162     return (type_string);
163 }
164 
165 private
read_resource_header(FILE * in,int offset)166 resource_header *read_resource_header(FILE *in, int offset)
167 {
168 	resource_header *header = malloc(sizeof(*header));
169 
170 	fseek(in, offset, SEEK_SET);
171 
172 	read_int32(in, &(header->data_offset));
173 	read_int32(in, &(header->map_offset));
174 	read_int32(in, &(header->data_length));
175 	read_int32(in, &(header->map_length));
176 
177         if (feof(in)) {
178             free (header);
179             header = NULL;
180         }
181 
182 	return header;
183 }
184 
185 private
read_resource_map(FILE * in,resource_header * header)186 resource_list *read_resource_map(FILE *in, resource_header *header)
187 {
188     resource_list *list;
189     int n_resources;
190 	int type_offset, name_offset, this_name_offset;
191     unsigned int *types;
192     int *number, *ref_offsets;
193     int n_types;
194     char type_string[5];
195     byte *buf, *p;
196     int i,j,k;
197 
198     buf = malloc(sizeof(*buf)*header->map_length);
199     if (buf == NULL) {
200         if_debug1('s', "error: could not allocate %d bytes for resource map\n", header->map_length);
201         return NULL;
202     }
203 
204     /* read in the whole resource map */
205 	fseek(in, header->map_offset, SEEK_SET);
206     fread(buf, 1, header->map_length, in);
207 
208     type_offset = get_int16(buf + 24);
209     name_offset = get_int16(buf + 26);
210     n_types = get_int16(buf + 28); n_types++;
211 
212     if (type_offset != 30)
213         if_debug1('s', "[s] warning! resource type list offset is %d, not 30!\n", type_offset);
214 
215     /* determine the total number of resources */
216     types = malloc(sizeof(*types)*n_types);
217     number = malloc(sizeof(*number)*n_types);
218     ref_offsets = malloc(sizeof(*ref_offsets)*n_types);
219     n_resources = 0;
220     p = buf + type_offset;	/* this seems to be off by two in files!? */
221     p = buf + 30;
222     for (i = 0; i < n_types; i++) {
223         types[i] = get_int32(p);
224         number[i] = get_int16(p + 4) + 1;
225         ref_offsets[i] = get_int16(p + 6);
226         p += 8;
227         n_resources += number[i];
228     }
229 
230     /* parse the individual resources */
231     list = malloc(sizeof(resource_list));
232     list->resources = malloc(sizeof(resource)*n_resources);
233     list->n_resources = n_resources;
234     k = 0;
235     for (i = 0; i < n_types; i++) {
236     	res_type2string(types[i], type_string);
237     	if_debug2('s', "[s] %d resources of type '%s':\n", number[i], type_string);
238         p = buf + type_offset + ref_offsets[i]; /* FIXME: also off? */
239         /* p = buf + 32 + ref_offsets[i]; */
240         for (j = 0; j < number[i]; j++) {
241             list->resources[k].type = types[i];
242             list->resources[k].id = get_int16(p);
243             this_name_offset = get_int16(p + 2);
244             if (this_name_offset == 0xFFFF) { /* no name field */
245                 list->resources[k].name = NULL;
246             } else { /* read name field (a pascal string) */
247                 char *c = buf + name_offset + this_name_offset;
248                 int len = *c;
249                 list->resources[k].name = malloc(sizeof(char)*(len+1));
250                 memcpy(list->resources[k].name, c + 1, len);
251                 list->resources[k].name[len] = '\0';
252             }
253             list->resources[k].flags = *(p+4);
254             list->resources[k].offset = get_int24(p+5);
255             /* load the actual resource data separately */
256             list->resources[k].length = 0;
257             list->resources[k].data = NULL;
258 
259             p += 12;
260             if_debug4('s', "\tid %d offset 0x%08x flags 0x%02x '%s'\n",
261                 list->resources[k].id, list->resources[k].offset,
262                 list->resources[k].flags, list->resources[k].name);
263             k++;
264         }
265     }
266 
267     free(buf);
268 
269 	return list;
270 }
271 
272 private
load_resource(FILE * in,resource_header * header,resource * res)273 void load_resource(FILE *in, resource_header *header, resource *res)
274 {
275     unsigned int len;
276     byte *buf;
277 
278     fseek(in, header->data_offset + res->offset, SEEK_SET);
279     read_int32(in, &len);
280 
281     buf = malloc(len);
282     fread(buf, 1, len, in);
283     res->data = buf;
284     res->length = len;
285 
286     return;
287 }
288 
289 private
read_datafork_resource(byte * buf,const char * fname,const uint type,const ushort id)290 int read_datafork_resource(byte *buf, const char *fname, const uint type, const ushort id)
291 {
292     resource_header *header;
293     resource_list *list;
294     FILE *in;
295     int i;
296 
297 
298     in = fopen(fname, "rb");
299     if (in == NULL) {
300         if_debug1('s', "[s] couldn't open '%s'\n", fname);
301         return 0;
302     }
303 
304     header = read_resource_header(in, 0);
305     if (header == NULL) {
306         if_debug0('s', "[s] could not read resource file header.\n");
307         if_debug0('s', "[s] not a serialized data fork resource file?\n");
308         return 0;
309     }
310 
311     if_debug0('s', "[s] loading resource map\n");
312 	list = read_resource_map(in, header);
313     if (list == NULL) {
314         if_debug0('s', "[s] couldn't read resource map.\n");
315         return 0;
316     }
317 
318     /* load the resource data we're interested in */
319     for (i = 0; i < list->n_resources; i++) {
320         if ((list->resources[i].type == type) &&
321         	(list->resources[i].id == id)) {
322         	if_debug2('s', "[s] loading '%s' resource id %d",
323                 list->resources[i].name, list->resources[i].id);
324             load_resource(in, header, &(list->resources[i]));
325             if_debug1('s', " (%d bytes)\n", list->resources[i].length);
326             fclose(in);
327             if (buf) memcpy (buf, list->resources[i].data, list->resources[i].length);
328             return (list->resources[i].length);
329         }
330     }
331 
332     fclose(in);
333     free(list);
334     free(header);
335 
336     return (0);
337 }
338 
339 /* end dfont loading code */
340 
341 
342 /* prototypes */
343 private iodev_proc_init(iodev_macresource_init);
344 private iodev_proc_open_file(iodev_macresource_open_file);
345 /* there is no close_file()...stream closure takes care of it */
346 /* ignore rest for now */
347 
348 /* Define the %macresource% device */
349 const gx_io_device gs_iodev_macresource =
350 {
351     "%macresource%", "FileSystem",
352     {
353      iodev_macresource_init, iodev_no_open_device,
354      iodev_macresource_open_file,
355      iodev_no_fopen, iodev_no_fclose,
356      iodev_no_delete_file, iodev_no_rename_file,
357      iodev_no_file_status,
358      iodev_no_enumerate_files, NULL, NULL,
359      iodev_no_get_params, iodev_no_put_params
360     }
361 };
362 
363 /* init state. don't know if we need state yet--probably not */
364 private int
iodev_macresource_init(gx_io_device * iodev,gs_memory_t * mem)365 iodev_macresource_init(gx_io_device *iodev, gs_memory_t *mem)
366 {
367     return 0;
368 }
369 
370 /* open the requested file return (in ps) a stream containing the resource data */
371 private int
iodev_macresource_open_file(gx_io_device * iodev,const char * fname,uint namelen,const char * access,stream ** ps,gs_memory_t * mem)372 iodev_macresource_open_file(gx_io_device *iodev, const char *fname, uint namelen,
373     const char *access, stream **ps, gs_memory_t *mem)
374 {
375     char filename[gp_file_name_sizeof];
376     char *res_type_string, *res_id_string;
377     uint type;
378     ushort id;
379     bool datafork = 0;
380     int size;
381     byte *buf;
382 
383     /* return NULL if there's an error */
384     *ps = NULL;
385 
386     strncpy(filename, fname, min(namelen, gp_file_name_sizeof));
387     if (namelen < gp_file_name_sizeof) filename[namelen] = '\0';
388     /* parse out the resource type and id. they're appended to the pathname
389        in the form '#<type>+<id>' */
390     res_type_string = strrchr(filename, '#');
391     if (res_type_string == NULL) {
392         if_debug0('s', "[s] couldn't find resource type separator\n");
393         return_error(e_invalidfileaccess);
394     }
395     *res_type_string++ = '\0';
396     res_id_string = strrchr(res_type_string, '+');
397     if (res_id_string == NULL) {
398         if_debug0('s', "couldn't find resource id separator\n");
399         return_error(e_invalidfileaccess);
400     }
401     *res_id_string++ = '\0';
402     type = res_string2type(res_type_string);
403     id = (ushort)atoi(res_id_string);
404     if_debug3('s', "[s] opening resource fork of '%s' for type '%s' id '%d'\n", filename, res_type_string, id);
405 
406     /* we call with a NULL buffer to get the size */
407     size = gp_read_macresource(NULL, filename, type, id);
408     if (size == 0) {
409         /* this means that opening the resource fork failed */
410         /* try to open as a .dfont from here */
411         if_debug0('s', "[s] trying to open as a datafork file instead...\n");
412         size = read_datafork_resource(NULL, filename, type, id);
413     	if (size != 0) {
414             datafork = true;
415     	} else {
416             if_debug0('s', "could not get resource size\n");
417     	    return_error(e_invalidfileaccess);
418     	}
419     }
420     if_debug1('s', "[s] got resource size %d bytes\n", size);
421     /* allocate a buffer */
422     buf = gs_alloc_string(mem, size, "macresource buffer");
423     if (buf == NULL) {
424         if_debug0('s', "macresource: could not allocate buffer for resource data\n");
425         return_error(e_VMerror);
426     }
427     /* call again to get the resource data */
428     if (!datafork) {
429         size = gp_read_macresource(buf, filename, type, id);
430     } else {
431         size = read_datafork_resource(buf, filename, type, id);
432     }
433 
434     /* allocate stream *ps and set it up with the buffered data */
435     *ps = s_alloc(mem, "macresource");
436     sread_string(*ps, buf, size);
437 
438     /* return success */
439     return 0;
440 }
441