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