Coverage Report

Created: 2023-06-07 06:20

/src/mupdf/source/fitz/output.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2022 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#define _LARGEFILE_SOURCE
24
#ifndef _FILE_OFFSET_BITS
25
#define _FILE_OFFSET_BITS 64
26
#endif
27
28
#include "mupdf/fitz.h"
29
30
#include <errno.h>
31
#include <stdarg.h>
32
#include <stdio.h>
33
#include <string.h>
34
#ifdef _WIN32
35
#include <io.h>
36
#include <windows.h>
37
#else
38
#include <unistd.h>
39
#endif
40
41
static void
42
file_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
43
0
{
44
0
  FILE *file = opaque;
45
0
  size_t n;
46
47
0
  if (count == 0)
48
0
    return;
49
50
0
  if (count == 1)
51
0
  {
52
0
    int x = putc(((unsigned char*)buffer)[0], file);
53
0
    if (x == EOF && ferror(file))
54
0
      fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fwrite: %s", strerror(errno));
55
0
    return;
56
0
  }
57
58
0
  n = fwrite(buffer, 1, count, file);
59
0
  if (n < count && ferror(file))
60
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fwrite: %s", strerror(errno));
61
0
}
62
63
static void
64
stdout_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
65
0
{
66
0
  file_write(ctx, stdout, buffer, count);
67
0
}
68
69
static fz_output fz_stdout_global = {
70
  NULL,
71
  stdout_write,
72
  NULL,
73
  NULL,
74
  NULL,
75
};
76
77
fz_output *fz_stdout(fz_context *ctx)
78
0
{
79
0
  return &fz_stdout_global;
80
0
}
81
82
static void
83
stderr_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
84
0
{
85
0
  file_write(ctx, stderr, buffer, count);
86
0
}
87
88
static fz_output fz_stderr_global = {
89
  NULL,
90
  stderr_write,
91
  NULL,
92
  NULL,
93
  NULL,
94
};
95
96
fz_output *fz_stderr(fz_context *ctx)
97
0
{
98
0
  return &fz_stderr_global;
99
0
}
100
101
#ifdef _WIN32
102
static void
103
stdods_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
104
{
105
  unsigned char *buf = fz_malloc(ctx, count+1);
106
107
  memcpy(buf, buffer, count);
108
  buf[count] = 0;
109
  OutputDebugStringA(buf);
110
  fz_free(ctx, buf);
111
}
112
113
static fz_output fz_stdods_global = {
114
  NULL,
115
  stdods_write,
116
  NULL,
117
  NULL,
118
  NULL,
119
};
120
121
fz_output *fz_stdods(fz_context *ctx)
122
{
123
  return &fz_stdods_global;
124
}
125
#endif
126
127
fz_output *fz_stddbg(fz_context *ctx)
128
0
{
129
0
  if (ctx->stddbg)
130
0
    return ctx->stddbg;
131
132
0
  return fz_stderr(ctx);
133
0
}
134
135
void fz_set_stddbg(fz_context *ctx, fz_output *out)
136
0
{
137
0
  if (ctx == NULL)
138
0
    return;
139
140
0
  ctx->stddbg = out;
141
0
}
142
143
static void
144
file_seek(fz_context *ctx, void *opaque, int64_t off, int whence)
145
0
{
146
0
  FILE *file = opaque;
147
#ifdef _WIN32
148
  int n = _fseeki64(file, off, whence);
149
#else
150
0
  int n = fseeko(file, off, whence);
151
0
#endif
152
0
  if (n < 0)
153
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fseek: %s", strerror(errno));
154
0
}
155
156
static int64_t
157
file_tell(fz_context *ctx, void *opaque)
158
0
{
159
0
  FILE *file = opaque;
160
#ifdef _WIN32
161
  int64_t off = _ftelli64(file);
162
#else
163
0
  int64_t off = ftello(file);
164
0
#endif
165
0
  if (off == -1)
166
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "cannot ftell: %s", strerror(errno));
167
0
  return off;
168
0
}
169
170
static void
171
file_drop(fz_context *ctx, void *opaque)
172
0
{
173
0
  FILE *file = opaque;
174
0
  int n = fclose(file);
175
0
  if (n < 0)
176
0
    fz_warn(ctx, "cannot fclose: %s", strerror(errno));
177
0
}
178
179
static fz_stream *
180
file_as_stream(fz_context *ctx, void *opaque)
181
0
{
182
0
  FILE *file = opaque;
183
0
  fflush(file);
184
0
  return fz_open_file_ptr_no_close(ctx, file);
185
0
}
186
187
static void file_truncate(fz_context *ctx, void *opaque)
188
0
{
189
0
  FILE *file = opaque;
190
0
  fflush(file);
191
192
#ifdef _WIN32
193
  {
194
    __int64 pos = _ftelli64(file);
195
    if (pos >= 0)
196
      _chsize_s(fileno(file), pos);
197
  }
198
#else
199
0
  {
200
0
    off_t pos = ftello(file);
201
0
    if (pos >= 0)
202
0
      (void)ftruncate(fileno(file), pos);
203
0
  }
204
0
#endif
205
0
}
206
207
fz_output *
208
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)
209
0
{
210
0
  fz_output *out = NULL;
211
212
0
  fz_var(out);
213
214
0
  fz_try(ctx)
215
0
  {
216
0
    out = fz_malloc_struct(ctx, fz_output);
217
0
    out->state = state;
218
0
    out->write = write;
219
0
    out->close = close;
220
0
    out->drop = drop;
221
0
    if (bufsiz > 0)
222
0
    {
223
0
      out->bp = Memento_label(fz_malloc(ctx, bufsiz), "output_buf");
224
0
      out->wp = out->bp;
225
0
      out->ep = out->bp + bufsiz;
226
0
    }
227
0
  }
228
0
  fz_catch(ctx)
229
0
  {
230
0
    if (drop)
231
0
      drop(ctx, state);
232
0
    fz_free(ctx, out);
233
0
    fz_rethrow(ctx);
234
0
  }
235
0
  return out;
236
0
}
237
238
static void null_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
239
0
{
240
0
}
241
242
fz_output *
243
fz_new_output_with_path(fz_context *ctx, const char *filename, int append)
244
0
{
245
0
  FILE *file;
246
0
  fz_output *out;
247
248
0
  if (filename == NULL)
249
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "no output to write to");
250
251
0
  if (!strcmp(filename, "/dev/null") || !fz_strcasecmp(filename, "nul:"))
252
0
    return fz_new_output(ctx, 0, NULL, null_write, NULL, NULL);
253
254
  /* If <append> is false, we use fopen()'s 'x' flag to force an error if
255
   * some other process creates the file immediately after we have removed
256
   * it - this avoids vunerability where a less-privilege process can create
257
   * a link and get us to overwrite a different file. See:
258
   *  https://bugs.ghostscript.com/show_bug.cgi?id=701797
259
   *  http://www.open-std.org/jtc1/sc22//WG14/www/docs/n1339.pdf
260
   */
261
#ifdef _WIN32
262
  /* Ensure we create a brand new file. We don't want to clobber our old file. */
263
  if (!append)
264
  {
265
    if (fz_remove_utf8(filename) < 0)
266
      if (errno != ENOENT)
267
        fz_throw(ctx, FZ_ERROR_GENERIC, "cannot remove file '%s': %s", filename, strerror(errno));
268
  }
269
#if defined(__MINGW32__) || defined(__MINGW64__)
270
  file = fz_fopen_utf8(filename, append ? "rb+" : "wb+"); /* 'x' flag not suported. */
271
#else
272
  file = fz_fopen_utf8(filename, append ? "rb+" : "wb+x");
273
#endif
274
  if (append)
275
  {
276
    if (file == NULL)
277
      file = fz_fopen_utf8(filename, "wb+");
278
    else
279
      fseek(file, 0, SEEK_END);
280
  }
281
#else
282
  /* Ensure we create a brand new file. We don't want to clobber our old file. */
283
0
  if (!append)
284
0
  {
285
0
    if (remove(filename) < 0)
286
0
      if (errno != ENOENT)
287
0
        fz_throw(ctx, FZ_ERROR_GENERIC, "cannot remove file '%s': %s", filename, strerror(errno));
288
0
  }
289
0
  file = fopen(filename, append ? "rb+" : "wb+x");
290
0
  if (file == NULL && append)
291
0
    file = fopen(filename, "wb+");
292
0
#endif
293
0
  if (!file)
294
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno));
295
296
0
  setvbuf(file, NULL, _IONBF, 0); /* we do our own buffering */
297
0
  out = fz_new_output(ctx, 8192, file, file_write, NULL, file_drop);
298
0
  out->seek = file_seek;
299
0
  out->tell = file_tell;
300
0
  out->as_stream = file_as_stream;
301
0
  out->truncate = file_truncate;
302
303
0
  return out;
304
0
}
305
306
static void
307
buffer_write(fz_context *ctx, void *opaque, const void *data, size_t len)
308
0
{
309
0
  fz_buffer *buffer = opaque;
310
0
  fz_append_data(ctx, buffer, data, len);
311
0
}
312
313
static void
314
buffer_seek(fz_context *ctx, void *opaque, int64_t off, int whence)
315
0
{
316
0
  fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek in buffer: %s", strerror(errno));
317
0
}
318
319
static int64_t
320
buffer_tell(fz_context *ctx, void *opaque)
321
0
{
322
0
  fz_buffer *buffer = opaque;
323
0
  return (int64_t)buffer->len;
324
0
}
325
326
static void
327
buffer_drop(fz_context *ctx, void *opaque)
328
0
{
329
0
  fz_buffer *buffer = opaque;
330
0
  fz_drop_buffer(ctx, buffer);
331
0
}
332
333
fz_output *
334
fz_new_output_with_buffer(fz_context *ctx, fz_buffer *buf)
335
0
{
336
0
  fz_output *out = fz_new_output(ctx, 0, fz_keep_buffer(ctx, buf), buffer_write, NULL, buffer_drop);
337
0
  out->seek = buffer_seek;
338
0
  out->tell = buffer_tell;
339
0
  return out;
340
0
}
341
342
void
343
fz_close_output(fz_context *ctx, fz_output *out)
344
0
{
345
0
  if (out == NULL)
346
0
    return;
347
0
  fz_flush_output(ctx, out);
348
0
  if (out->close)
349
0
    out->close(ctx, out->state);
350
0
  out->close = NULL;
351
0
}
352
353
void
354
fz_drop_output(fz_context *ctx, fz_output *out)
355
0
{
356
0
  if (out)
357
0
  {
358
0
    if (out->close)
359
0
      fz_warn(ctx, "dropping unclosed output");
360
0
    if (out->drop)
361
0
      out->drop(ctx, out->state);
362
0
    fz_free(ctx, out->bp);
363
0
    if (out != &fz_stdout_global && out != &fz_stderr_global)
364
0
      fz_free(ctx, out);
365
0
  }
366
0
}
367
368
void
369
fz_seek_output(fz_context *ctx, fz_output *out, int64_t off, int whence)
370
0
{
371
0
  if (out->seek == NULL)
372
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot seek in unseekable output stream\n");
373
0
  fz_flush_output(ctx, out);
374
0
  out->seek(ctx, out->state, off, whence);
375
0
}
376
377
int64_t
378
fz_tell_output(fz_context *ctx, fz_output *out)
379
0
{
380
0
  if (out->tell == NULL)
381
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot tell in untellable output stream\n");
382
0
  if (out->bp)
383
0
    return out->tell(ctx, out->state) + (out->wp - out->bp);
384
0
  return out->tell(ctx, out->state);
385
0
}
386
387
fz_stream *
388
fz_stream_from_output(fz_context *ctx, fz_output *out)
389
0
{
390
0
  if (out->as_stream == NULL)
391
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot derive input stream from output stream");
392
0
  fz_flush_output(ctx, out);
393
0
  return out->as_stream(ctx, out->state);
394
0
}
395
396
void
397
fz_truncate_output(fz_context *ctx, fz_output *out)
398
0
{
399
0
  if (out->truncate == NULL)
400
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot truncate this output stream");
401
0
  fz_flush_output(ctx, out);
402
0
  out->truncate(ctx, out->state);
403
0
}
404
405
static void
406
fz_write_emit(fz_context *ctx, void *out, int c)
407
0
{
408
0
  fz_write_byte(ctx, out, c);
409
0
}
410
411
void
412
fz_write_vprintf(fz_context *ctx, fz_output *out, const char *fmt, va_list args)
413
0
{
414
0
  fz_format_string(ctx, out, fz_write_emit, fmt, args);
415
0
}
416
417
void
418
fz_write_printf(fz_context *ctx, fz_output *out, const char *fmt, ...)
419
0
{
420
0
  va_list args;
421
0
  va_start(args, fmt);
422
0
  fz_format_string(ctx, out, fz_write_emit, fmt, args);
423
0
  va_end(args);
424
0
}
425
426
void
427
fz_flush_output(fz_context *ctx, fz_output *out)
428
0
{
429
0
  fz_write_bits_sync(ctx, out);
430
0
  if (out->wp > out->bp)
431
0
  {
432
0
    out->write(ctx, out->state, out->bp, out->wp - out->bp);
433
0
    out->wp = out->bp;
434
0
  }
435
0
}
436
437
void
438
fz_write_byte(fz_context *ctx, fz_output *out, unsigned char x)
439
0
{
440
0
  if (out->bp)
441
0
  {
442
0
    if (out->wp == out->ep)
443
0
    {
444
0
      out->write(ctx, out->state, out->bp, out->wp - out->bp);
445
0
      out->wp = out->bp;
446
0
    }
447
0
    *out->wp++ = x;
448
0
  }
449
0
  else
450
0
  {
451
0
    out->write(ctx, out->state, &x, 1);
452
0
  }
453
0
}
454
455
void
456
fz_write_char(fz_context *ctx, fz_output *out, char x)
457
0
{
458
0
  fz_write_byte(ctx, out, (unsigned char)x);
459
0
}
460
461
void
462
fz_write_data(fz_context *ctx, fz_output *out, const void *data_, size_t size)
463
0
{
464
0
  const char *data = data_;
465
466
0
  if (out->bp)
467
0
  {
468
0
    if (size >= (size_t) (out->ep - out->bp)) /* too large for buffer */
469
0
    {
470
0
      if (out->wp > out->bp)
471
0
      {
472
0
        out->write(ctx, out->state, out->bp, out->wp - out->bp);
473
0
        out->wp = out->bp;
474
0
      }
475
0
      out->write(ctx, out->state, data, size);
476
0
    }
477
0
    else if (out->wp + size <= out->ep) /* fits in current buffer */
478
0
    {
479
0
      memcpy(out->wp, data, size);
480
0
      out->wp += size;
481
0
    }
482
0
    else /* fits if we flush first */
483
0
    {
484
0
      size_t n = out->ep - out->wp;
485
0
      memcpy(out->wp, data, n);
486
0
      out->write(ctx, out->state, out->bp, out->ep - out->bp);
487
0
      memcpy(out->bp, data + n, size - n);
488
0
      out->wp = out->bp + size - n;
489
0
    }
490
0
  }
491
0
  else
492
0
  {
493
0
    out->write(ctx, out->state, data, size);
494
0
  }
495
0
}
496
497
void
498
fz_write_buffer(fz_context *ctx, fz_output *out, fz_buffer *buf)
499
0
{
500
0
  fz_write_data(ctx, out, buf->data, buf->len);
501
0
}
502
503
void
504
fz_write_string(fz_context *ctx, fz_output *out, const char *s)
505
0
{
506
0
  fz_write_data(ctx, out, s, strlen(s));
507
0
}
508
509
void
510
fz_write_int32_be(fz_context *ctx, fz_output *out, int x)
511
0
{
512
0
  char data[4];
513
514
0
  data[0] = x>>24;
515
0
  data[1] = x>>16;
516
0
  data[2] = x>>8;
517
0
  data[3] = x;
518
519
0
  fz_write_data(ctx, out, data, 4);
520
0
}
521
522
void
523
fz_write_uint32_be(fz_context *ctx, fz_output *out, unsigned int x)
524
0
{
525
0
  fz_write_int32_be(ctx, out, (unsigned int)x);
526
0
}
527
528
void
529
fz_write_int32_le(fz_context *ctx, fz_output *out, int x)
530
0
{
531
0
  char data[4];
532
533
0
  data[0] = x;
534
0
  data[1] = x>>8;
535
0
  data[2] = x>>16;
536
0
  data[3] = x>>24;
537
538
0
  fz_write_data(ctx, out, data, 4);
539
0
}
540
541
void
542
fz_write_uint32_le(fz_context *ctx, fz_output *out, unsigned int x)
543
0
{
544
0
  fz_write_int32_le(ctx, out, (int)x);
545
0
}
546
547
void
548
fz_write_int16_be(fz_context *ctx, fz_output *out, int x)
549
0
{
550
0
  char data[2];
551
552
0
  data[0] = x>>8;
553
0
  data[1] = x;
554
555
0
  fz_write_data(ctx, out, data, 2);
556
0
}
557
558
void
559
fz_write_uint16_be(fz_context *ctx, fz_output *out, unsigned int x)
560
0
{
561
0
  fz_write_int16_be(ctx, out, (int)x);
562
0
}
563
564
void
565
fz_write_int16_le(fz_context *ctx, fz_output *out, int x)
566
0
{
567
0
  char data[2];
568
569
0
  data[0] = x;
570
0
  data[1] = x>>8;
571
572
0
  fz_write_data(ctx, out, data, 2);
573
0
}
574
575
void
576
fz_write_uint16_le(fz_context *ctx, fz_output *out, unsigned int x)
577
0
{
578
0
  fz_write_int16_le(ctx, out, (int)x);
579
0
}
580
581
void
582
fz_write_float_le(fz_context *ctx, fz_output *out, float f)
583
0
{
584
0
  union {float f; int32_t i;} u;
585
0
  u.f = f;
586
0
  fz_write_int32_le(ctx, out, u.i);
587
0
}
588
589
void
590
fz_write_float_be(fz_context *ctx, fz_output *out, float f)
591
0
{
592
0
  union {float f; int32_t i;} u;
593
0
  u.f = f;
594
0
  fz_write_int32_be(ctx, out, u.i);
595
0
}
596
597
void
598
fz_write_rune(fz_context *ctx, fz_output *out, int rune)
599
0
{
600
0
  char data[10];
601
0
  fz_write_data(ctx, out, data, fz_runetochar(data, rune));
602
0
}
603
604
void
605
fz_write_base64(fz_context *ctx, fz_output *out, const unsigned char *data, size_t size, int newline)
606
0
{
607
0
  static const char set[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
608
0
  size_t i;
609
0
  for (i = 0; i + 3 <= size; i += 3)
610
0
  {
611
0
    int c = data[i];
612
0
    int d = data[i+1];
613
0
    int e = data[i+2];
614
0
    if (newline && (i & 15) == 0)
615
0
      fz_write_byte(ctx, out, '\n');
616
0
    fz_write_byte(ctx, out, set[c>>2]);
617
0
    fz_write_byte(ctx, out, set[((c&3)<<4)|(d>>4)]);
618
0
    fz_write_byte(ctx, out, set[((d&15)<<2)|(e>>6)]);
619
0
    fz_write_byte(ctx, out, set[e&63]);
620
0
  }
621
0
  if (size - i == 2)
622
0
  {
623
0
    int c = data[i];
624
0
    int d = data[i+1];
625
0
    fz_write_byte(ctx, out, set[c>>2]);
626
0
    fz_write_byte(ctx, out, set[((c&3)<<4)|(d>>4)]);
627
0
    fz_write_byte(ctx, out, set[((d&15)<<2)]);
628
0
    fz_write_byte(ctx, out, '=');
629
0
  }
630
0
  else if (size - i == 1)
631
0
  {
632
0
    int c = data[i];
633
0
    fz_write_byte(ctx, out, set[c>>2]);
634
0
    fz_write_byte(ctx, out, set[((c&3)<<4)]);
635
0
    fz_write_byte(ctx, out, '=');
636
0
    fz_write_byte(ctx, out, '=');
637
0
  }
638
0
}
639
640
void
641
fz_write_base64_buffer(fz_context *ctx, fz_output *out, fz_buffer *buf, int newline)
642
0
{
643
0
  unsigned char *data;
644
0
  size_t size = fz_buffer_storage(ctx, buf, &data);
645
0
  fz_write_base64(ctx, out, data, size, newline);
646
0
}
647
648
void
649
fz_append_base64(fz_context *ctx, fz_buffer *out, const unsigned char *data, size_t size, int newline)
650
0
{
651
0
  static const char set[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
652
0
  size_t i;
653
0
  for (i = 0; i + 3 <= size; i += 3)
654
0
  {
655
0
    int c = data[i];
656
0
    int d = data[i+1];
657
0
    int e = data[i+2];
658
0
    if (newline && (i & 15) == 0)
659
0
      fz_append_byte(ctx, out, '\n');
660
0
    fz_append_byte(ctx, out, set[c>>2]);
661
0
    fz_append_byte(ctx, out, set[((c&3)<<4)|(d>>4)]);
662
0
    fz_append_byte(ctx, out, set[((d&15)<<2)|(e>>6)]);
663
0
    fz_append_byte(ctx, out, set[e&63]);
664
0
  }
665
0
  if (size - i == 2)
666
0
  {
667
0
    int c = data[i];
668
0
    int d = data[i+1];
669
0
    fz_append_byte(ctx, out, set[c>>2]);
670
0
    fz_append_byte(ctx, out, set[((c&3)<<4)|(d>>4)]);
671
0
    fz_append_byte(ctx, out, set[((d&15)<<2)]);
672
0
    fz_append_byte(ctx, out, '=');
673
0
  }
674
0
  else if (size - i == 1)
675
0
  {
676
0
    int c = data[i];
677
0
    fz_append_byte(ctx, out, set[c>>2]);
678
0
    fz_append_byte(ctx, out, set[((c&3)<<4)]);
679
0
    fz_append_byte(ctx, out, '=');
680
0
    fz_append_byte(ctx, out, '=');
681
0
  }
682
0
}
683
684
void
685
fz_append_base64_buffer(fz_context *ctx, fz_buffer *out, fz_buffer *buf, int newline)
686
0
{
687
0
  unsigned char *data;
688
0
  size_t size = fz_buffer_storage(ctx, buf, &data);
689
0
  fz_append_base64(ctx, out, data, size, newline);
690
0
}
691
692
693
void
694
fz_save_buffer(fz_context *ctx, fz_buffer *buf, const char *filename)
695
0
{
696
0
  fz_output *out = fz_new_output_with_path(ctx, filename, 0);
697
0
  fz_try(ctx)
698
0
  {
699
0
    fz_write_data(ctx, out, buf->data, buf->len);
700
0
    fz_close_output(ctx, out);
701
0
  }
702
0
  fz_always(ctx)
703
0
    fz_drop_output(ctx, out);
704
0
  fz_catch(ctx)
705
0
    fz_rethrow(ctx);
706
0
}
707
708
fz_band_writer *fz_new_band_writer_of_size(fz_context *ctx, size_t size, fz_output *out)
709
0
{
710
0
  fz_band_writer *writer = fz_calloc(ctx, size, 1);
711
0
  writer->out = out;
712
0
  return writer;
713
0
}
714
715
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)
716
0
{
717
0
  if (writer == NULL || writer->band == NULL)
718
0
    return;
719
720
0
  if (w <= 0 || h <= 0 || n <= 0 || alpha < 0 || alpha > 1)
721
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Invalid bandwriter header dimensions/setup");
722
723
0
  writer->w = w;
724
0
  writer->h = h;
725
0
  writer->s = fz_count_active_separations(ctx, seps);
726
0
  writer->n = n;
727
0
  writer->alpha = alpha;
728
0
  writer->xres = xres;
729
0
  writer->yres = yres;
730
0
  writer->pagenum = pagenum;
731
0
  writer->line = 0;
732
0
  writer->seps = fz_keep_separations(ctx, seps);
733
0
  writer->header(ctx, writer, cs);
734
0
}
735
736
void fz_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_height, const unsigned char *samples)
737
0
{
738
0
  if (writer == NULL || writer->band == NULL)
739
0
    return;
740
0
  if (writer->line + band_height > writer->h)
741
0
    band_height = writer->h - writer->line;
742
0
  if (band_height < 0) {
743
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Too much band data!");
744
0
  }
745
0
  if (band_height > 0) {
746
0
    writer->band(ctx, writer, stride, writer->line, band_height, samples);
747
0
    writer->line += band_height;
748
0
  }
749
0
  if (writer->line == writer->h && writer->trailer) {
750
0
    writer->trailer(ctx, writer);
751
    /* Protect against more band_height == 0 calls */
752
0
    writer->line++;
753
0
  }
754
0
}
755
756
void fz_close_band_writer(fz_context *ctx, fz_band_writer *writer)
757
0
{
758
0
  if (writer == NULL)
759
0
    return;
760
0
  if (writer->close != NULL)
761
0
    writer->close(ctx, writer);
762
0
  writer->close = NULL;
763
0
}
764
765
void fz_drop_band_writer(fz_context *ctx, fz_band_writer *writer)
766
0
{
767
0
  if (writer == NULL)
768
0
    return;
769
0
  if (writer->drop != NULL)
770
0
    writer->drop(ctx, writer);
771
0
  fz_drop_separations(ctx, writer->seps);
772
0
  fz_free(ctx, writer);
773
0
}
774
775
int fz_output_supports_stream(fz_context *ctx, fz_output *out)
776
0
{
777
0
  return out != NULL && out->as_stream != NULL;
778
0
}
779
780
void fz_write_bits(fz_context *ctx, fz_output *out, unsigned int data, int num_bits)
781
0
{
782
0
  while (num_bits)
783
0
  {
784
    /* How many bits will be left in the current byte after we
785
     * insert these bits? */
786
0
    int n = 8 - num_bits - out->buffered;
787
0
    if (n >= 0)
788
0
    {
789
      /* We can fit our data in. */
790
0
      out->bits |= data << n;
791
0
      out->buffered += num_bits;
792
0
      num_bits = 0;
793
0
    }
794
0
    else
795
0
    {
796
      /* There are 8 - out->buffered bits left to be filled. We have
797
       * num_bits to fill it with, which is more, so we need to throw
798
       * away the bottom 'num_bits - (8 - out->buffered)' bits. That's
799
       * num_bits + out->buffered - 8 = -(8 - num_bits - out_buffered) = -n */
800
0
      out->bits |= data >> -n;
801
0
      data &= ~(out->bits << -n);
802
0
      num_bits = -n;
803
0
      out->buffered = 8;
804
0
    }
805
0
    if (out->buffered == 8)
806
0
    {
807
0
      fz_write_byte(ctx, out, out->bits);
808
0
      out->buffered = 0;
809
0
      out->bits = 0;
810
0
    }
811
0
  }
812
813
0
}
814
815
void fz_write_bits_sync(fz_context *ctx, fz_output *out)
816
0
{
817
0
  if (out->buffered == 0)
818
0
    return;
819
0
  fz_write_bits(ctx, out, 0, 8 - out->buffered);
820
0
}