1 #define _LARGEFILE_SOURCE
2 #ifndef _FILE_OFFSET_BITS
3 #define _FILE_OFFSET_BITS 64
4 #endif
5
6 #include "mupdf/fitz.h"
7
8 #include <errno.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <string.h>
12 #ifdef _WIN32
13 #include <io.h>
14 #else
15 #include <unistd.h>
16 #endif
17
18 static void
file_write(fz_context * ctx,void * opaque,const void * buffer,size_t count)19 file_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
20 {
21 FILE *file = opaque;
22 size_t n;
23
24 if (count == 0)
25 return;
26
27 if (count == 1)
28 {
29 int x = putc(((unsigned char*)buffer)[0], file);
30 if (x == EOF && ferror(file))
31 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fwrite: %s", strerror(errno));
32 return;
33 }
34
35 n = fwrite(buffer, 1, count, file);
36 if (n < count && ferror(file))
37 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fwrite: %s", strerror(errno));
38 }
39
40 static void
stdout_write(fz_context * ctx,void * opaque,const void * buffer,size_t count)41 stdout_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
42 {
43 file_write(ctx, stdout, buffer, count);
44 }
45
46 static fz_output fz_stdout_global = {
47 NULL,
48 stdout_write,
49 NULL,
50 NULL,
51 NULL,
52 };
53
fz_stdout(fz_context * ctx)54 fz_output *fz_stdout(fz_context *ctx)
55 {
56 return &fz_stdout_global;
57 }
58
59 static void
stderr_write(fz_context * ctx,void * opaque,const void * buffer,size_t count)60 stderr_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
61 {
62 file_write(ctx, stderr, buffer, count);
63 }
64
65 static fz_output fz_stderr_global = {
66 NULL,
67 stderr_write,
68 NULL,
69 NULL,
70 NULL,
71 };
72
fz_stderr(fz_context * ctx)73 fz_output *fz_stderr(fz_context *ctx)
74 {
75 return &fz_stderr_global;
76 }
77
78 static void
file_seek(fz_context * ctx,void * opaque,int64_t off,int whence)79 file_seek(fz_context *ctx, void *opaque, int64_t off, int whence)
80 {
81 FILE *file = opaque;
82 #ifdef _WIN32
83 int n = _fseeki64(file, off, whence);
84 #else
85 int n = fseeko(file, off, whence);
86 #endif
87 if (n < 0)
88 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fseek: %s", strerror(errno));
89 }
90
91 static int64_t
file_tell(fz_context * ctx,void * opaque)92 file_tell(fz_context *ctx, void *opaque)
93 {
94 FILE *file = opaque;
95 #ifdef _WIN32
96 int64_t off = _ftelli64(file);
97 #else
98 int64_t off = ftello(file);
99 #endif
100 if (off == -1)
101 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot ftell: %s", strerror(errno));
102 return off;
103 }
104
105 static void
file_drop(fz_context * ctx,void * opaque)106 file_drop(fz_context *ctx, void *opaque)
107 {
108 FILE *file = opaque;
109 int n = fclose(file);
110 if (n < 0)
111 fz_warn(ctx, "cannot fclose: %s", strerror(errno));
112 }
113
114 static fz_stream *
file_as_stream(fz_context * ctx,void * opaque)115 file_as_stream(fz_context *ctx, void *opaque)
116 {
117 FILE *file = opaque;
118 fflush(file);
119 return fz_open_file_ptr_no_close(ctx, file);
120 }
121
file_truncate(fz_context * ctx,void * opaque)122 static void file_truncate(fz_context *ctx, void *opaque)
123 {
124 FILE *file = opaque;
125 fflush(file);
126
127 #ifdef _WIN32
128 _chsize_s(fileno(file), ftell(file));
129 #else
130 ftruncate(fileno(file), ftell(file));
131 #endif
132 }
133
134 fz_output *
fz_new_output(fz_context * ctx,int bufsiz,void * state,fz_output_write_fn * write,fz_output_close_fn * close,fz_output_drop_fn * drop)135 fz_new_output(fz_context *ctx, int bufsiz, void *state, fz_output_write_fn *write, fz_output_close_fn *close, fz_output_drop_fn *drop)
136 {
137 fz_output *out = NULL;
138
139 fz_var(out);
140
141 fz_try(ctx)
142 {
143 out = fz_malloc_struct(ctx, fz_output);
144 out->state = state;
145 out->write = write;
146 out->close = close;
147 out->drop = drop;
148 if (bufsiz > 0)
149 {
150 out->bp = Memento_label(fz_malloc(ctx, bufsiz), "output_buf");
151 out->wp = out->bp;
152 out->ep = out->bp + bufsiz;
153 }
154 }
155 fz_catch(ctx)
156 {
157 if (drop)
158 drop(ctx, state);
159 fz_free(ctx, out);
160 fz_rethrow(ctx);
161 }
162 return out;
163 }
164
null_write(fz_context * ctx,void * opaque,const void * buffer,size_t count)165 static void null_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
166 {
167 }
168
169 fz_output *
fz_new_output_with_path(fz_context * ctx,const char * filename,int append)170 fz_new_output_with_path(fz_context *ctx, const char *filename, int append)
171 {
172 FILE *file;
173 fz_output *out;
174
175 if (filename == NULL)
176 fz_throw(ctx, FZ_ERROR_GENERIC, "no output to write to");
177
178 if (!strcmp(filename, "/dev/null") || !fz_strcasecmp(filename, "nul:"))
179 return fz_new_output(ctx, 0, NULL, null_write, NULL, NULL);
180
181 #ifdef _WIN32
182 /* Ensure we create a brand new file. We don't want to clobber our old file. */
183 if (!append)
184 {
185 if (fz_remove_utf8(filename) < 0)
186 if (errno != ENOENT)
187 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot remove file '%s': %s", filename, strerror(errno));
188 }
189 file = fz_fopen_utf8(filename, append ? "rb+" : "wb+");
190 if (append)
191 {
192 if (file == NULL)
193 file = fz_fopen_utf8(filename, "wb+");
194 else
195 fseek(file, 0, SEEK_END);
196 }
197 #else
198 /* Ensure we create a brand new file. We don't want to clobber our old file. */
199 if (!append)
200 {
201 if (remove(filename) < 0)
202 if (errno != ENOENT)
203 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot remove file '%s': %s", filename, strerror(errno));
204 }
205 file = fopen(filename, append ? "rb+" : "wb+");
206 if (file == NULL && append)
207 file = fopen(filename, "wb+");
208 #endif
209 if (!file)
210 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno));
211
212 setvbuf(file, NULL, _IONBF, 0); /* we do our own buffering */
213 out = fz_new_output(ctx, 8192, file, file_write, NULL, file_drop);
214 out->seek = file_seek;
215 out->tell = file_tell;
216 out->as_stream = file_as_stream;
217 out->truncate = file_truncate;
218
219 return out;
220 }
221
222 static void
buffer_write(fz_context * ctx,void * opaque,const void * data,size_t len)223 buffer_write(fz_context *ctx, void *opaque, const void *data, size_t len)
224 {
225 fz_buffer *buffer = opaque;
226 fz_append_data(ctx, buffer, data, len);
227 }
228
229 static void
buffer_seek(fz_context * ctx,void * opaque,int64_t off,int whence)230 buffer_seek(fz_context *ctx, void *opaque, int64_t off, int whence)
231 {
232 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek in buffer: %s", strerror(errno));
233 }
234
235 static int64_t
buffer_tell(fz_context * ctx,void * opaque)236 buffer_tell(fz_context *ctx, void *opaque)
237 {
238 fz_buffer *buffer = opaque;
239 return (int64_t)buffer->len;
240 }
241
242 static void
buffer_drop(fz_context * ctx,void * opaque)243 buffer_drop(fz_context *ctx, void *opaque)
244 {
245 fz_buffer *buffer = opaque;
246 fz_drop_buffer(ctx, buffer);
247 }
248
249 fz_output *
fz_new_output_with_buffer(fz_context * ctx,fz_buffer * buf)250 fz_new_output_with_buffer(fz_context *ctx, fz_buffer *buf)
251 {
252 fz_output *out = fz_new_output(ctx, 0, fz_keep_buffer(ctx, buf), buffer_write, NULL, buffer_drop);
253 out->seek = buffer_seek;
254 out->tell = buffer_tell;
255 return out;
256 }
257
258 void
fz_close_output(fz_context * ctx,fz_output * out)259 fz_close_output(fz_context *ctx, fz_output *out)
260 {
261 if (out == NULL)
262 return;
263 fz_flush_output(ctx, out);
264 if (out->close)
265 out->close(ctx, out->state);
266 out->close = NULL;
267 }
268
269 void
fz_drop_output(fz_context * ctx,fz_output * out)270 fz_drop_output(fz_context *ctx, fz_output *out)
271 {
272 if (out)
273 {
274 if (out->close)
275 fz_warn(ctx, "dropping unclosed output");
276 if (out->drop)
277 out->drop(ctx, out->state);
278 fz_free(ctx, out->bp);
279 if (out != &fz_stdout_global && out != &fz_stderr_global)
280 fz_free(ctx, out);
281 }
282 }
283
284 void
fz_seek_output(fz_context * ctx,fz_output * out,int64_t off,int whence)285 fz_seek_output(fz_context *ctx, fz_output *out, int64_t off, int whence)
286 {
287 if (out->seek == NULL)
288 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot seek in unseekable output stream\n");
289 fz_flush_output(ctx, out);
290 out->seek(ctx, out->state, off, whence);
291 }
292
293 int64_t
fz_tell_output(fz_context * ctx,fz_output * out)294 fz_tell_output(fz_context *ctx, fz_output *out)
295 {
296 if (out->tell == NULL)
297 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot tell in untellable output stream\n");
298 if (out->bp)
299 return out->tell(ctx, out->state) + (out->wp - out->bp);
300 return out->tell(ctx, out->state);
301 }
302
303 fz_stream *
fz_stream_from_output(fz_context * ctx,fz_output * out)304 fz_stream_from_output(fz_context *ctx, fz_output *out)
305 {
306 if (out->as_stream == NULL)
307 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot derive input stream from output stream");
308 fz_flush_output(ctx, out);
309 return out->as_stream(ctx, out->state);
310 }
311
312 void
fz_truncate_output(fz_context * ctx,fz_output * out)313 fz_truncate_output(fz_context *ctx, fz_output *out)
314 {
315 if (out->truncate == NULL)
316 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot truncate this output stream");
317 fz_flush_output(ctx, out);
318 out->truncate(ctx, out->state);
319 }
320
321 static void
fz_write_emit(fz_context * ctx,void * out,int c)322 fz_write_emit(fz_context *ctx, void *out, int c)
323 {
324 fz_write_byte(ctx, out, c);
325 }
326
327 void
fz_write_vprintf(fz_context * ctx,fz_output * out,const char * fmt,va_list args)328 fz_write_vprintf(fz_context *ctx, fz_output *out, const char *fmt, va_list args)
329 {
330 fz_format_string(ctx, out, fz_write_emit, fmt, args);
331 }
332
333 void
fz_write_printf(fz_context * ctx,fz_output * out,const char * fmt,...)334 fz_write_printf(fz_context *ctx, fz_output *out, const char *fmt, ...)
335 {
336 va_list args;
337 va_start(args, fmt);
338 fz_format_string(ctx, out, fz_write_emit, fmt, args);
339 va_end(args);
340 }
341
342 void
fz_flush_output(fz_context * ctx,fz_output * out)343 fz_flush_output(fz_context *ctx, fz_output *out)
344 {
345 if (out->wp > out->bp)
346 {
347 out->write(ctx, out->state, out->bp, out->wp - out->bp);
348 out->wp = out->bp;
349 }
350 }
351
352 void
fz_write_byte(fz_context * ctx,fz_output * out,unsigned char x)353 fz_write_byte(fz_context *ctx, fz_output *out, unsigned char x)
354 {
355 if (out->bp)
356 {
357 if (out->wp == out->ep)
358 {
359 out->write(ctx, out->state, out->bp, out->wp - out->bp);
360 out->wp = out->bp;
361 }
362 *out->wp++ = x;
363 }
364 else
365 {
366 out->write(ctx, out->state, &x, 1);
367 }
368 }
369
370 void
fz_write_char(fz_context * ctx,fz_output * out,char x)371 fz_write_char(fz_context *ctx, fz_output *out, char x)
372 {
373 fz_write_byte(ctx, out, (unsigned char)x);
374 }
375
376 void
fz_write_data(fz_context * ctx,fz_output * out,const void * data_,size_t size)377 fz_write_data(fz_context *ctx, fz_output *out, const void *data_, size_t size)
378 {
379 const char *data = data_;
380
381 if (out->bp)
382 {
383 if (size >= (size_t) (out->ep - out->bp)) /* too large for buffer */
384 {
385 if (out->wp > out->bp)
386 {
387 out->write(ctx, out->state, out->bp, out->wp - out->bp);
388 out->wp = out->bp;
389 }
390 out->write(ctx, out->state, data, size);
391 }
392 else if (out->wp + size <= out->ep) /* fits in current buffer */
393 {
394 memcpy(out->wp, data, size);
395 out->wp += size;
396 }
397 else /* fits if we flush first */
398 {
399 size_t n = out->ep - out->wp;
400 memcpy(out->wp, data, n);
401 out->write(ctx, out->state, out->bp, out->ep - out->bp);
402 memcpy(out->bp, data + n, size - n);
403 out->wp = out->bp + size - n;
404 }
405 }
406 else
407 {
408 out->write(ctx, out->state, data, size);
409 }
410 }
411
412 void
fz_write_string(fz_context * ctx,fz_output * out,const char * s)413 fz_write_string(fz_context *ctx, fz_output *out, const char *s)
414 {
415 fz_write_data(ctx, out, s, strlen(s));
416 }
417
418 void
fz_write_int32_be(fz_context * ctx,fz_output * out,int x)419 fz_write_int32_be(fz_context *ctx, fz_output *out, int x)
420 {
421 char data[4];
422
423 data[0] = x>>24;
424 data[1] = x>>16;
425 data[2] = x>>8;
426 data[3] = x;
427
428 fz_write_data(ctx, out, data, 4);
429 }
430
431 void
fz_write_uint32_be(fz_context * ctx,fz_output * out,unsigned int x)432 fz_write_uint32_be(fz_context *ctx, fz_output *out, unsigned int x)
433 {
434 fz_write_int32_be(ctx, out, (unsigned int)x);
435 }
436
437 void
fz_write_int32_le(fz_context * ctx,fz_output * out,int x)438 fz_write_int32_le(fz_context *ctx, fz_output *out, int x)
439 {
440 char data[4];
441
442 data[0] = x;
443 data[1] = x>>8;
444 data[2] = x>>16;
445 data[3] = x>>24;
446
447 fz_write_data(ctx, out, data, 4);
448 }
449
450 void
fz_write_uint32_le(fz_context * ctx,fz_output * out,unsigned int x)451 fz_write_uint32_le(fz_context *ctx, fz_output *out, unsigned int x)
452 {
453 fz_write_int32_le(ctx, out, (int)x);
454 }
455
456 void
fz_write_int16_be(fz_context * ctx,fz_output * out,int x)457 fz_write_int16_be(fz_context *ctx, fz_output *out, int x)
458 {
459 char data[2];
460
461 data[0] = x>>8;
462 data[1] = x;
463
464 fz_write_data(ctx, out, data, 2);
465 }
466
467 void
fz_write_uint16_be(fz_context * ctx,fz_output * out,unsigned int x)468 fz_write_uint16_be(fz_context *ctx, fz_output *out, unsigned int x)
469 {
470 fz_write_int16_be(ctx, out, (int)x);
471 }
472
473 void
fz_write_int16_le(fz_context * ctx,fz_output * out,int x)474 fz_write_int16_le(fz_context *ctx, fz_output *out, int x)
475 {
476 char data[2];
477
478 data[0] = x;
479 data[1] = x>>8;
480
481 fz_write_data(ctx, out, data, 2);
482 }
483
484 void
fz_write_uint16_le(fz_context * ctx,fz_output * out,unsigned int x)485 fz_write_uint16_le(fz_context *ctx, fz_output *out, unsigned int x)
486 {
487 fz_write_int16_le(ctx, out, (int)x);
488 }
489
490 void
fz_write_float_le(fz_context * ctx,fz_output * out,float f)491 fz_write_float_le(fz_context *ctx, fz_output *out, float f)
492 {
493 union {float f; int32_t i;} u;
494 u.f = f;
495 fz_write_int32_le(ctx, out, u.i);
496 }
497
498 void
fz_write_float_be(fz_context * ctx,fz_output * out,float f)499 fz_write_float_be(fz_context *ctx, fz_output *out, float f)
500 {
501 union {float f; int32_t i;} u;
502 u.f = f;
503 fz_write_int32_be(ctx, out, u.i);
504 }
505
506 void
fz_write_rune(fz_context * ctx,fz_output * out,int rune)507 fz_write_rune(fz_context *ctx, fz_output *out, int rune)
508 {
509 char data[10];
510 fz_write_data(ctx, out, data, fz_runetochar(data, rune));
511 }
512
513 void
fz_write_base64(fz_context * ctx,fz_output * out,const unsigned char * data,size_t size,int newline)514 fz_write_base64(fz_context *ctx, fz_output *out, const unsigned char *data, size_t size, int newline)
515 {
516 static const char set[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
517 size_t i;
518 for (i = 0; i + 3 <= size; i += 3)
519 {
520 int c = data[i];
521 int d = data[i+1];
522 int e = data[i+2];
523 if (newline && (i & 15) == 0)
524 fz_write_byte(ctx, out, '\n');
525 fz_write_byte(ctx, out, set[c>>2]);
526 fz_write_byte(ctx, out, set[((c&3)<<4)|(d>>4)]);
527 fz_write_byte(ctx, out, set[((d&15)<<2)|(e>>6)]);
528 fz_write_byte(ctx, out, set[e&63]);
529 }
530 if (size - i == 2)
531 {
532 int c = data[i];
533 int d = data[i+1];
534 fz_write_byte(ctx, out, set[c>>2]);
535 fz_write_byte(ctx, out, set[((c&3)<<4)|(d>>4)]);
536 fz_write_byte(ctx, out, set[((d&15)<<2)]);
537 fz_write_byte(ctx, out, '=');
538 }
539 else if (size - i == 1)
540 {
541 int c = data[i];
542 fz_write_byte(ctx, out, set[c>>2]);
543 fz_write_byte(ctx, out, set[((c&3)<<4)]);
544 fz_write_byte(ctx, out, '=');
545 fz_write_byte(ctx, out, '=');
546 }
547 }
548
549 void
fz_write_base64_buffer(fz_context * ctx,fz_output * out,fz_buffer * buf,int newline)550 fz_write_base64_buffer(fz_context *ctx, fz_output *out, fz_buffer *buf, int newline)
551 {
552 unsigned char *data;
553 size_t size = fz_buffer_storage(ctx, buf, &data);
554 fz_write_base64(ctx, out, data, size, newline);
555 }
556
557 void
fz_save_buffer(fz_context * ctx,fz_buffer * buf,const char * filename)558 fz_save_buffer(fz_context *ctx, fz_buffer *buf, const char *filename)
559 {
560 fz_output *out = fz_new_output_with_path(ctx, filename, 0);
561 fz_try(ctx)
562 {
563 fz_write_data(ctx, out, buf->data, buf->len);
564 fz_close_output(ctx, out);
565 }
566 fz_always(ctx)
567 fz_drop_output(ctx, out);
568 fz_catch(ctx)
569 fz_rethrow(ctx);
570 }
571
fz_new_band_writer_of_size(fz_context * ctx,size_t size,fz_output * out)572 fz_band_writer *fz_new_band_writer_of_size(fz_context *ctx, size_t size, fz_output *out)
573 {
574 fz_band_writer *writer = fz_calloc(ctx, size, 1);
575 writer->out = out;
576 return writer;
577 }
578
fz_write_header(fz_context * ctx,fz_band_writer * writer,int w,int h,int n,int alpha,int xres,int yres,int pagenum,fz_colorspace * cs,fz_separations * seps)579 void fz_write_header(fz_context *ctx, fz_band_writer *writer, int w, int h, int n, int alpha, int xres, int yres, int pagenum, fz_colorspace *cs, fz_separations *seps)
580 {
581 if (writer == NULL || writer->band == NULL)
582 return;
583
584 writer->w = w;
585 writer->h = h;
586 writer->s = fz_count_active_separations(ctx, seps);
587 writer->n = n;
588 writer->alpha = alpha;
589 writer->xres = xres;
590 writer->yres = yres;
591 writer->pagenum = pagenum;
592 writer->line = 0;
593 writer->seps = fz_keep_separations(ctx, seps);
594 writer->header(ctx, writer, cs);
595 }
596
fz_write_band(fz_context * ctx,fz_band_writer * writer,int stride,int band_height,const unsigned char * samples)597 void fz_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_height, const unsigned char *samples)
598 {
599 if (writer == NULL || writer->band == NULL)
600 return;
601 if (writer->line + band_height > writer->h)
602 band_height = writer->h - writer->line;
603 if (band_height < 0) {
604 fz_throw(ctx, FZ_ERROR_GENERIC, "Too much band data!");
605 }
606 if (band_height > 0) {
607 writer->band(ctx, writer, stride, writer->line, band_height, samples);
608 writer->line += band_height;
609 }
610 if (writer->line == writer->h && writer->trailer) {
611 writer->trailer(ctx, writer);
612 /* Protect against more band_height == 0 calls */
613 writer->line++;
614 }
615 }
616
fz_drop_band_writer(fz_context * ctx,fz_band_writer * writer)617 void fz_drop_band_writer(fz_context *ctx, fz_band_writer *writer)
618 {
619 if (writer == NULL)
620 return;
621 if (writer->drop != NULL)
622 writer->drop(ctx, writer);
623 fz_drop_separations(ctx, writer->seps);
624 fz_free(ctx, writer);
625 }
626
fz_output_supports_stream(fz_context * ctx,fz_output * out)627 int fz_output_supports_stream(fz_context *ctx, fz_output *out)
628 {
629 return out != NULL && out->as_stream != NULL;
630 }
631