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 | |
14 | static void |
15 | file_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 | |
36 | static void |
37 | stdout_write(fz_context *ctx, void *opaque, const void *buffer, size_t count) |
38 | { |
39 | file_write(ctx, stdout, buffer, count); |
40 | } |
41 | |
42 | static fz_output fz_stdout_global = { |
43 | NULL, |
44 | stdout_write, |
45 | NULL, |
46 | NULL, |
47 | NULL, |
48 | }; |
49 | |
50 | fz_output *fz_stdout(fz_context *ctx) |
51 | { |
52 | return &fz_stdout_global; |
53 | } |
54 | |
55 | static void |
56 | file_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 | |
68 | static int64_t |
69 | file_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 | |
82 | static void |
83 | file_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 | |
91 | static fz_stream * |
92 | file_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 | */ |
110 | fz_output * |
111 | 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) |
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 | |
141 | static 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 | */ |
154 | fz_output * |
155 | fz_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 | |
194 | static void |
195 | buffer_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 | |
201 | static void |
202 | buffer_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 | |
207 | static int64_t |
208 | buffer_tell(fz_context *ctx, void *opaque) |
209 | { |
210 | fz_buffer *buffer = opaque; |
211 | return (int64_t)buffer->len; |
212 | } |
213 | |
214 | static void |
215 | buffer_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 | */ |
227 | fz_output * |
228 | fz_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 | */ |
239 | void |
240 | fz_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 | */ |
253 | void |
254 | fz_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 | */ |
274 | void |
275 | fz_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 | */ |
288 | int64_t |
289 | fz_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 | */ |
306 | fz_stream * |
307 | fz_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 | |
315 | static void |
316 | fz_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 | */ |
324 | void |
325 | fz_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 | */ |
334 | void |
335 | fz_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 | */ |
346 | void |
347 | fz_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 | |
356 | void |
357 | fz_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 | */ |
380 | void |
381 | fz_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 | */ |
419 | void |
420 | fz_write_string(fz_context *ctx, fz_output *out, const char *s) |
421 | { |
422 | fz_write_data(ctx, out, s, strlen(s)); |
423 | } |
424 | |
425 | void |
426 | fz_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 | |
438 | void |
439 | fz_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 | |
451 | void |
452 | fz_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 | |
462 | void |
463 | fz_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 | */ |
476 | void |
477 | fz_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 | |
483 | void |
484 | fz_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 | |
519 | void |
520 | fz_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 | |
527 | void |
528 | fz_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 | |
542 | fz_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 | */ |
567 | void (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 | */ |
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 | |
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 | |