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