Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4680 right-hear 1
#include "fitz.h"
2
#include "muxps.h"
3
 
4
#include 
5
 
6
xps_part *
7
xps_new_part(xps_context *ctx, char *name, int size)
8
{
9
	xps_part *part;
10
 
11
	part = fz_malloc(sizeof(xps_part));
12
	part->name = fz_strdup(name);
13
	part->size = size;
14
	part->data = fz_malloc(size + 1);
15
	part->data[size] = 0; /* null-terminate for xml parser */
16
 
17
	return part;
18
}
19
 
20
void
21
xps_free_part(xps_context *ctx, xps_part *part)
22
{
23
	fz_free(part->name);
24
	fz_free(part->data);
25
	fz_free(part);
26
}
27
 
28
static inline int getshort(fz_stream *file)
29
{
30
	int a = fz_read_byte(file);
31
	int b = fz_read_byte(file);
32
	return a | b << 8;
33
}
34
 
35
static inline int getlong(fz_stream *file)
36
{
37
	int a = fz_read_byte(file);
38
	int b = fz_read_byte(file);
39
	int c = fz_read_byte(file);
40
	int d = fz_read_byte(file);
41
	return a | b << 8 | c << 16 | d << 24;
42
}
43
 
44
static void *
45
xps_zip_alloc_items(xps_context *ctx, int items, int size)
46
{
47
	return fz_calloc(items, size);
48
}
49
 
50
static void
51
xps_zip_free(xps_context *ctx, void *ptr)
52
{
53
	fz_free(ptr);
54
}
55
 
56
static int
57
xps_compare_entries(const void *a0, const void *b0)
58
{
59
	xps_entry *a = (xps_entry*) a0;
60
	xps_entry *b = (xps_entry*) b0;
61
	return xps_strcasecmp(a->name, b->name);
62
}
63
 
64
static xps_entry *
65
xps_find_zip_entry(xps_context *ctx, char *name)
66
{
67
	int l = 0;
68
	int r = ctx->zip_count - 1;
69
	while (l <= r)
70
	{
71
		int m = (l + r) >> 1;
72
		int c = xps_strcasecmp(name, ctx->zip_table[m].name);
73
		if (c < 0)
74
			r = m - 1;
75
		else if (c > 0)
76
			l = m + 1;
77
		else
78
			return &ctx->zip_table[m];
79
	}
80
	return NULL;
81
}
82
 
83
static int
84
xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf)
85
{
86
	z_stream stream;
87
	unsigned char *inbuf;
88
	int sig;
89
	int version, general, method;
90
	int namelength, extralength;
91
	int code;
92
 
93
	fz_seek(ctx->file, ent->offset, 0);
94
 
95
	sig = getlong(ctx->file);
96
	if (sig != ZIP_LOCAL_FILE_SIG)
97
		return fz_throw("wrong zip local file signature (0x%x)", sig);
98
 
99
	version = getshort(ctx->file);
100
	general = getshort(ctx->file);
101
	method = getshort(ctx->file);
102
	(void) getshort(ctx->file); /* file time */
103
	(void) getshort(ctx->file); /* file date */
104
	(void) getlong(ctx->file); /* crc-32 */
105
	(void) getlong(ctx->file); /* csize */
106
	(void) getlong(ctx->file); /* usize */
107
	namelength = getshort(ctx->file);
108
	extralength = getshort(ctx->file);
109
 
110
	fz_seek(ctx->file, namelength + extralength, 1);
111
 
112
	if (method == 0)
113
	{
114
		fz_read(ctx->file, outbuf, ent->usize);
115
	}
116
	else if (method == 8)
117
	{
118
		inbuf = fz_malloc(ent->csize);
119
 
120
		fz_read(ctx->file, inbuf, ent->csize);
121
 
122
		memset(&stream, 0, sizeof(z_stream));
123
		stream.zalloc = (alloc_func) xps_zip_alloc_items;
124
		stream.zfree = (free_func) xps_zip_free;
125
		stream.opaque = ctx;
126
		stream.next_in = inbuf;
127
		stream.avail_in = ent->csize;
128
		stream.next_out = outbuf;
129
		stream.avail_out = ent->usize;
130
 
131
		code = inflateInit2(&stream, -15);
132
		if (code != Z_OK)
133
			return fz_throw("zlib inflateInit2 error: %s", stream.msg);
134
		code = inflate(&stream, Z_FINISH);
135
		if (code != Z_STREAM_END)
136
		{
137
			inflateEnd(&stream);
138
			return fz_throw("zlib inflate error: %s", stream.msg);
139
		}
140
		code = inflateEnd(&stream);
141
		if (code != Z_OK)
142
			return fz_throw("zlib inflateEnd error: %s", stream.msg);
143
 
144
		fz_free(inbuf);
145
	}
146
	else
147
	{
148
		return fz_throw("unknown compression method (%d)", method);
149
	}
150
 
151
	return fz_okay;
152
}
153
 
154
/*
155
 * Read the central directory in a zip file.
156
 */
157
 
158
static int
159
xps_read_zip_dir(xps_context *ctx, int start_offset)
160
{
161
	int sig;
162
	int offset, count;
163
	int namesize, metasize, commentsize;
164
	int i;
165
 
166
	fz_seek(ctx->file, start_offset, 0);
167
 
168
	sig = getlong(ctx->file);
169
	if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG)
170
		return fz_throw("wrong zip end of central directory signature (0x%x)", sig);
171
 
172
	(void) getshort(ctx->file); /* this disk */
173
	(void) getshort(ctx->file); /* start disk */
174
	(void) getshort(ctx->file); /* entries in this disk */
175
	count = getshort(ctx->file); /* entries in central directory disk */
176
	(void) getlong(ctx->file); /* size of central directory */
177
	offset = getlong(ctx->file); /* offset to central directory */
178
 
179
	ctx->zip_count = count;
180
	ctx->zip_table = fz_calloc(count, sizeof(xps_entry));
181
	memset(ctx->zip_table, 0, sizeof(xps_entry) * count);
182
 
183
	fz_seek(ctx->file, offset, 0);
184
 
185
	for (i = 0; i < count; i++)
186
	{
187
		sig = getlong(ctx->file);
188
		if (sig != ZIP_CENTRAL_DIRECTORY_SIG)
189
			return fz_throw("wrong zip central directory signature (0x%x)", sig);
190
 
191
		(void) getshort(ctx->file); /* version made by */
192
		(void) getshort(ctx->file); /* version to extract */
193
		(void) getshort(ctx->file); /* general */
194
		(void) getshort(ctx->file); /* method */
195
		(void) getshort(ctx->file); /* last mod file time */
196
		(void) getshort(ctx->file); /* last mod file date */
197
		(void) getlong(ctx->file); /* crc-32 */
198
		ctx->zip_table[i].csize = getlong(ctx->file);
199
		ctx->zip_table[i].usize = getlong(ctx->file);
200
		namesize = getshort(ctx->file);
201
		metasize = getshort(ctx->file);
202
		commentsize = getshort(ctx->file);
203
		(void) getshort(ctx->file); /* disk number start */
204
		(void) getshort(ctx->file); /* int file atts */
205
		(void) getlong(ctx->file); /* ext file atts */
206
		ctx->zip_table[i].offset = getlong(ctx->file);
207
 
208
		ctx->zip_table[i].name = fz_malloc(namesize + 1);
209
		fz_read(ctx->file, (unsigned char*)ctx->zip_table[i].name, namesize);
210
		ctx->zip_table[i].name[namesize] = 0;
211
 
212
		fz_seek(ctx->file, metasize, 1);
213
		fz_seek(ctx->file, commentsize, 1);
214
	}
215
 
216
	qsort(ctx->zip_table, count, sizeof(xps_entry), xps_compare_entries);
217
 
218
	return fz_okay;
219
}
220
 
221
static int
222
xps_find_and_read_zip_dir(xps_context *ctx)
223
{
224
	unsigned char buf[512];
225
	int file_size, back, maxback;
226
	int i, n;
227
 
228
	fz_seek(ctx->file, 0, SEEK_END);
229
	file_size = fz_tell(ctx->file);
230
 
231
	maxback = MIN(file_size, 0xFFFF + sizeof buf);
232
	back = MIN(maxback, sizeof buf);
233
 
234
	while (back < maxback)
235
	{
236
		fz_seek(ctx->file, file_size - back, 0);
237
 
238
		n = fz_read(ctx->file, buf, sizeof buf);
239
		if (n < 0)
240
			return fz_throw("cannot read end of central directory");
241
 
242
		for (i = n - 4; i > 0; i--)
243
			if (!memcmp(buf + i, "PK\5\6", 4))
244
				return xps_read_zip_dir(ctx, file_size - back + i);
245
 
246
		back += sizeof buf - 4;
247
	}
248
 
249
	return fz_throw("cannot find end of central directory");
250
}
251
 
252
/*
253
 * Read and interleave split parts from a ZIP file.
254
 */
255
static xps_part *
256
xps_read_zip_part(xps_context *ctx, char *partname)
257
{
258
	char buf[2048];
259
	xps_entry *ent;
260
	xps_part *part;
261
	int count, size, offset, i;
262
	char *name;
263
 
264
	name = partname;
265
	if (name[0] == '/')
266
		name ++;
267
 
268
	/* All in one piece */
269
	ent = xps_find_zip_entry(ctx, name);
270
	if (ent)
271
	{
272
		part = xps_new_part(ctx, partname, ent->usize);
273
		xps_read_zip_entry(ctx, ent, part->data);
274
		return part;
275
	}
276
 
277
	/* Count the number of pieces and their total size */
278
	count = 0;
279
	size = 0;
280
	while (1)
281
	{
282
		sprintf(buf, "%s/[%d].piece", name, count);
283
		ent = xps_find_zip_entry(ctx, buf);
284
		if (!ent)
285
		{
286
			sprintf(buf, "%s/[%d].last.piece", name, count);
287
			ent = xps_find_zip_entry(ctx, buf);
288
		}
289
		if (!ent)
290
			break;
291
		count ++;
292
		size += ent->usize;
293
	}
294
 
295
	/* Inflate the pieces */
296
	if (count)
297
	{
298
		part = xps_new_part(ctx, partname, size);
299
		offset = 0;
300
		for (i = 0; i < count; i++)
301
		{
302
			if (i < count - 1)
303
				sprintf(buf, "%s/[%d].piece", name, i);
304
			else
305
				sprintf(buf, "%s/[%d].last.piece", name, i);
306
			ent = xps_find_zip_entry(ctx, buf);
307
			xps_read_zip_entry(ctx, ent, part->data + offset);
308
			offset += ent->usize;
309
		}
310
		return part;
311
	}
312
 
313
	return NULL;
314
}
315
 
316
/*
317
 * Read and interleave split parts from files in the directory.
318
 */
319
static xps_part *
320
xps_read_dir_part(xps_context *ctx, char *name)
321
{
322
	char buf[2048];
323
	xps_part *part;
324
	FILE *file;
325
	int count, size, offset, i, n;
326
 
327
	fz_strlcpy(buf, ctx->directory, sizeof buf);
328
	fz_strlcat(buf, name, sizeof buf);
329
 
330
	/* All in one piece */
331
	file = fopen(buf, "rb");
332
	if (file)
333
	{
334
		fseek(file, 0, SEEK_END);
335
		size = ftell(file);
336
		fseek(file, 0, SEEK_SET);
337
		part = xps_new_part(ctx, name, size);
338
		fread(part->data, 1, size, file);
339
		fclose(file);
340
		return part;
341
	}
342
 
343
	/* Count the number of pieces and their total size */
344
	count = 0;
345
	size = 0;
346
	while (1)
347
	{
348
		sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, count);
349
		file = fopen(buf, "rb");
350
		if (!file)
351
		{
352
			sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, count);
353
			file = fopen(buf, "rb");
354
		}
355
		if (!file)
356
			break;
357
		count ++;
358
		fseek(file, 0, SEEK_END);
359
		size += ftell(file);
360
		fclose(file);
361
	}
362
 
363
	/* Inflate the pieces */
364
	if (count)
365
	{
366
		part = xps_new_part(ctx, name, size);
367
		offset = 0;
368
		for (i = 0; i < count; i++)
369
		{
370
			if (i < count - 1)
371
				sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, i);
372
			else
373
				sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, i);
374
			file = fopen(buf, "rb");
375
			n = fread(part->data + offset, 1, size - offset, file);
376
			offset += n;
377
			fclose(file);
378
		}
379
		return part;
380
	}
381
 
382
	return NULL;
383
}
384
 
385
xps_part *
386
xps_read_part(xps_context *ctx, char *partname)
387
{
388
	if (ctx->directory)
389
		return xps_read_dir_part(ctx, partname);
390
	return xps_read_zip_part(ctx, partname);
391
}
392
 
393
static int
394
xps_open_directory(xps_context **ctxp, char *directory)
395
{
396
	xps_context *ctx;
397
	int code;
398
 
399
	ctx = fz_malloc(sizeof(xps_context));
400
	memset(ctx, 0, sizeof(xps_context));
401
 
402
	ctx->directory = fz_strdup(directory);
403
 
404
	code = xps_read_page_list(ctx);
405
	if (code)
406
	{
407
		xps_free_context(ctx);
408
		return fz_rethrow(code, "cannot read page list");
409
	}
410
 
411
	*ctxp = ctx;
412
	return fz_okay;
413
}
414
 
415
int
416
xps_open_stream(xps_context **ctxp, fz_stream *file)
417
{
418
	xps_context *ctx;
419
	int code;
420
 
421
	ctx = fz_malloc(sizeof(xps_context));
422
	memset(ctx, 0, sizeof(xps_context));
423
 
424
	ctx->file = fz_keep_stream(file);
425
 
426
	code = xps_find_and_read_zip_dir(ctx);
427
	if (code < 0)
428
	{
429
		xps_free_context(ctx);
430
		return fz_rethrow(code, "cannot read zip central directory");
431
	}
432
 
433
	code = xps_read_page_list(ctx);
434
	if (code)
435
	{
436
		xps_free_context(ctx);
437
		return fz_rethrow(code, "cannot read page list");
438
	}
439
 
440
	*ctxp = ctx;
441
	return fz_okay;
442
}
443
 
444
int
445
xps_open_file(xps_context **ctxp, char *filename)
446
{
447
	char buf[2048];
448
	fz_stream *file;
449
	char *p;
450
	int code;
451
 
452
	if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels"))
453
	{
454
		fz_strlcpy(buf, filename, sizeof buf);
455
		p = strstr(buf, "/_rels/.rels");
456
		if (!p)
457
			p = strstr(buf, "\\_rels\\.rels");
458
		*p = 0;
459
		return xps_open_directory(ctxp, buf);
460
	}
461
 
462
	file = fz_open_file(filename);
463
	if (!file)
464
		return fz_throw("cannot open file '%s': %s", filename, strerror(errno));
465
 
466
	code = xps_open_stream(ctxp, file);
467
	fz_close(file);
468
	if (code)
469
		return fz_rethrow(code, "cannot load document '%s'", filename);
470
	return fz_okay;
471
}
472
 
473
void
474
xps_free_context(xps_context *ctx)
475
{
476
	xps_font_cache *font, *next;
477
	int i;
478
 
479
	if (ctx->file)
480
		fz_close(ctx->file);
481
 
482
	for (i = 0; i < ctx->zip_count; i++)
483
		fz_free(ctx->zip_table[i].name);
484
	fz_free(ctx->zip_table);
485
 
486
	font = ctx->font_table;
487
	while (font)
488
	{
489
		next = font->next;
490
		fz_drop_font(font->font);
491
		fz_free(font->name);
492
		fz_free(font);
493
		font = next;
494
	}
495
 
496
	xps_free_page_list(ctx);
497
 
498
	fz_free(ctx->start_part);
499
	fz_free(ctx->directory);
500
	fz_free(ctx);
501
}