Coverage Report

Created: 2025-12-03 07:00

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