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