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