Coverage Report

Created: 2024-05-20 06:23

/src/mupdf/source/fitz/document.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2023 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
#include "mupdf/fitz.h"
24
25
#include <string.h>
26
27
enum
28
{
29
  FZ_DOCUMENT_HANDLER_MAX = 32
30
};
31
32
14
#define DEFW (450)
33
14
#define DEFH (600)
34
14
#define DEFEM (12)
35
36
struct fz_document_handler_context
37
{
38
  int refs;
39
  int count;
40
  const fz_document_handler *handler[FZ_DOCUMENT_HANDLER_MAX];
41
};
42
43
void fz_new_document_handler_context(fz_context *ctx)
44
15.6k
{
45
15.6k
  ctx->handler = fz_malloc_struct(ctx, fz_document_handler_context);
46
15.6k
  ctx->handler->refs = 1;
47
15.6k
}
48
49
fz_document_handler_context *fz_keep_document_handler_context(fz_context *ctx)
50
0
{
51
0
  if (!ctx || !ctx->handler)
52
0
    return NULL;
53
0
  return fz_keep_imp(ctx, ctx->handler, &ctx->handler->refs);
54
0
}
55
56
void fz_drop_document_handler_context(fz_context *ctx)
57
15.6k
{
58
15.6k
  if (!ctx)
59
0
    return;
60
61
15.6k
  if (fz_drop_imp(ctx, ctx->handler, &ctx->handler->refs))
62
15.6k
  {
63
15.6k
    fz_free(ctx, ctx->handler);
64
15.6k
    ctx->handler = NULL;
65
15.6k
  }
66
15.6k
}
67
68
void fz_register_document_handler(fz_context *ctx, const fz_document_handler *handler)
69
202k
{
70
202k
  fz_document_handler_context *dc;
71
202k
  int i;
72
73
202k
  if (!handler)
74
0
    return;
75
76
202k
  dc = ctx->handler;
77
202k
  if (dc == NULL)
78
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "Document handler list not found");
79
80
1.41M
  for (i = 0; i < dc->count; i++)
81
1.21M
    if (dc->handler[i] == handler)
82
0
      return;
83
84
202k
  if (dc->count >= FZ_DOCUMENT_HANDLER_MAX)
85
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "Too many document handlers");
86
87
202k
  dc->handler[dc->count++] = handler;
88
202k
}
89
90
const fz_document_handler *
91
fz_recognize_document_stream_content(fz_context *ctx, fz_stream *stream, const char *magic)
92
0
{
93
0
  return fz_recognize_document_stream_and_dir_content(ctx, stream, NULL, magic);
94
0
}
95
96
const fz_document_handler *
97
fz_recognize_document_stream_and_dir_content(fz_context *ctx, fz_stream *stream, fz_archive *dir, const char *magic)
98
15.6k
{
99
15.6k
  fz_document_handler_context *dc;
100
15.6k
  int i, best_score, best_i;
101
15.6k
  const char *ext;
102
103
15.6k
  dc = ctx->handler;
104
15.6k
  if (dc->count == 0)
105
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "No document handlers registered");
106
107
15.6k
  ext = strrchr(magic, '.');
108
15.6k
  if (ext)
109
0
    ext = ext + 1;
110
15.6k
  else
111
15.6k
    ext = magic;
112
113
15.6k
  best_score = 0;
114
15.6k
  best_i = -1;
115
116
15.6k
  if ((stream && stream->seek != NULL) || (stream == NULL && dir != NULL))
117
15.6k
  {
118
219k
    for (i = 0; i < dc->count; i++)
119
203k
    {
120
203k
      int score = 0;
121
122
203k
      if (dc->handler[i]->recognize_content)
123
141k
      {
124
141k
        if (stream)
125
141k
          fz_seek(ctx, stream, 0, SEEK_SET);
126
282k
        fz_try(ctx)
127
282k
        {
128
141k
          score = dc->handler[i]->recognize_content(ctx, stream, dir);
129
141k
        }
130
282k
        fz_catch(ctx)
131
756
        {
132
          /* in case of zip errors when recognizing EPUB/XPS/DOCX files */
133
756
          fz_rethrow_unless(ctx, FZ_ERROR_FORMAT);
134
756
          (void)fz_convert_error(ctx, NULL); /* ugly hack to silence the error message */
135
756
          score = 0;
136
756
        }
137
141k
      }
138
203k
      if (best_score < score)
139
12.0k
      {
140
12.0k
        best_score = score;
141
12.0k
        best_i = i;
142
12.0k
      }
143
203k
    }
144
15.6k
    if (stream)
145
15.6k
      fz_seek(ctx, stream, 0, SEEK_SET);
146
15.6k
  }
147
148
15.6k
  if (best_score < 100)
149
3.61k
  {
150
50.5k
    for (i = 0; i < dc->count; i++)
151
46.9k
    {
152
46.9k
      int score = 0;
153
46.9k
      const char **entry;
154
155
46.9k
      if (dc->handler[i]->recognize)
156
3.61k
        score = dc->handler[i]->recognize(ctx, magic);
157
158
209k
      for (entry = &dc->handler[i]->mimetypes[0]; *entry; entry++)
159
162k
        if (!fz_strcasecmp(magic, *entry) && score < 100)
160
0
        {
161
0
          score = 100;
162
0
          break;
163
0
        }
164
165
46.9k
      if (ext)
166
46.9k
      {
167
227k
        for (entry = &dc->handler[i]->extensions[0]; *entry; entry++)
168
184k
          if (!fz_strcasecmp(ext, *entry) && score < 100)
169
3.53k
          {
170
3.53k
            score = 100;
171
3.53k
            break;
172
3.53k
          }
173
46.9k
      }
174
175
46.9k
      if (best_score < score)
176
3.53k
      {
177
3.53k
        best_score = score;
178
3.53k
        best_i = i;
179
3.53k
      }
180
46.9k
    }
181
3.61k
  }
182
183
15.6k
  if (best_i < 0)
184
76
    return NULL;
185
186
15.6k
  return dc->handler[best_i];
187
15.6k
}
188
189
const fz_document_handler *fz_recognize_document_content(fz_context *ctx, const char *filename)
190
0
{
191
0
  fz_stream *stream = NULL;
192
0
  const fz_document_handler *handler = NULL;
193
0
  fz_archive *zip = NULL;
194
195
0
  if (fz_is_directory(ctx, filename))
196
0
    zip = fz_open_directory(ctx, filename);
197
0
  else
198
0
    stream  = fz_open_file(ctx, filename);
199
200
0
  fz_try(ctx)
201
0
    handler = fz_recognize_document_stream_and_dir_content(ctx, stream, zip, filename);
202
0
  fz_always(ctx)
203
0
  {
204
0
    fz_drop_stream(ctx, stream);
205
0
    fz_drop_archive(ctx, zip);
206
0
  }
207
0
  fz_catch(ctx)
208
0
    fz_rethrow(ctx);
209
210
0
  return handler;
211
0
}
212
213
const fz_document_handler *
214
fz_recognize_document(fz_context *ctx, const char *magic)
215
0
{
216
0
  return fz_recognize_document_stream_and_dir_content(ctx, NULL, NULL, magic);
217
0
}
218
219
#if FZ_ENABLE_PDF
220
extern fz_document_handler pdf_document_handler;
221
#endif
222
223
fz_document *
224
fz_open_accelerated_document_with_stream_and_dir(fz_context *ctx, const char *magic, fz_stream *stream, fz_stream *accel, fz_archive *dir)
225
15.6k
{
226
15.6k
  const fz_document_handler *handler;
227
228
15.6k
  if (stream == NULL && dir == NULL)
229
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "no document to open");
230
15.6k
  if (magic == NULL)
231
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "missing file type");
232
233
15.6k
  handler = fz_recognize_document_stream_and_dir_content(ctx, stream, dir, magic);
234
15.6k
  if (!handler)
235
76
    fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot find document handler for file type: '%s'", magic);
236
15.6k
  return handler->open(ctx, stream, accel, dir);
237
15.6k
}
238
239
fz_document *
240
fz_open_accelerated_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream, fz_stream *accel)
241
15.6k
{
242
15.6k
  return fz_open_accelerated_document_with_stream_and_dir(ctx, magic, stream, accel, NULL);
243
15.6k
}
244
245
fz_document *
246
fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream)
247
15.6k
{
248
15.6k
  return fz_open_accelerated_document_with_stream(ctx, magic, stream, NULL);
249
15.6k
}
250
251
fz_document *
252
fz_open_document_with_stream_and_dir(fz_context *ctx, const char *magic, fz_stream *stream, fz_archive *dir)
253
0
{
254
0
  return fz_open_accelerated_document_with_stream_and_dir(ctx, magic, stream, NULL, dir);
255
0
}
256
257
fz_document *
258
fz_open_document_with_buffer(fz_context *ctx, const char *magic, fz_buffer *buffer)
259
76
{
260
76
  fz_document *doc;
261
76
  fz_stream *stream = fz_open_buffer(ctx, buffer);
262
152
  fz_try(ctx)
263
152
    doc = fz_open_document_with_stream(ctx, magic, stream);
264
152
  fz_always(ctx)
265
76
    fz_drop_stream(ctx, stream);
266
76
  fz_catch(ctx)
267
76
    fz_rethrow(ctx);
268
0
  return doc;
269
76
}
270
271
fz_document *
272
fz_open_accelerated_document(fz_context *ctx, const char *filename, const char *accel)
273
0
{
274
0
  const fz_document_handler *handler;
275
0
  fz_stream *file;
276
0
  fz_stream *afile = NULL;
277
0
  fz_document *doc = NULL;
278
0
  fz_archive *dir = NULL;
279
0
  char dirname[PATH_MAX];
280
281
0
  fz_var(afile);
282
283
0
  if (filename == NULL)
284
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "no document to open");
285
286
0
  handler = fz_recognize_document_content(ctx, filename);
287
0
  if (!handler)
288
0
    fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot find document handler for file: %s", filename);
289
290
0
  if (fz_is_directory(ctx, filename))
291
0
  {
292
    /* Cannot accelerate directories, currently. */
293
0
    dir = fz_open_directory(ctx, filename);
294
295
0
    fz_try(ctx)
296
0
      doc = fz_open_accelerated_document_with_stream_and_dir(ctx, filename, NULL, NULL, dir);
297
0
    fz_always(ctx)
298
0
      fz_drop_archive(ctx, dir);
299
0
    fz_catch(ctx)
300
0
      fz_rethrow(ctx);
301
302
0
    return doc;
303
0
  }
304
305
0
  file = fz_open_file(ctx, filename);
306
307
0
  fz_try(ctx)
308
0
  {
309
0
    if (accel)
310
0
      afile = fz_open_file(ctx, accel);
311
0
    if (handler->wants_dir)
312
0
    {
313
0
      fz_dirname(dirname, filename, sizeof dirname);
314
0
      dir = fz_open_directory(ctx, dirname);
315
0
    }
316
0
    doc = handler->open(ctx, file, afile, dir);
317
0
  }
318
0
  fz_always(ctx)
319
0
  {
320
0
    fz_drop_archive(ctx, dir);
321
0
    fz_drop_stream(ctx, afile);
322
0
    fz_drop_stream(ctx, file);
323
0
  }
324
0
  fz_catch(ctx)
325
0
    fz_rethrow(ctx);
326
327
0
  return doc;
328
0
}
329
330
fz_document *
331
fz_open_document(fz_context *ctx, const char *filename)
332
0
{
333
0
  return fz_open_accelerated_document(ctx, filename, NULL);
334
0
}
335
336
void fz_save_accelerator(fz_context *ctx, fz_document *doc, const char *accel)
337
0
{
338
0
  if (doc == NULL)
339
0
    return;
340
0
  if (doc->output_accelerator == NULL)
341
0
    return;
342
343
0
  fz_output_accelerator(ctx, doc, fz_new_output_with_path(ctx, accel, 0));
344
0
}
345
346
void fz_output_accelerator(fz_context *ctx, fz_document *doc, fz_output *accel)
347
0
{
348
0
  if (doc == NULL || accel == NULL)
349
0
    return;
350
0
  if (doc->output_accelerator == NULL)
351
0
  {
352
0
    fz_drop_output(ctx, accel);
353
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "Document does not support writing an accelerator");
354
0
  }
355
356
0
  doc->output_accelerator(ctx, doc, accel);
357
0
}
358
359
int fz_document_supports_accelerator(fz_context *ctx, fz_document *doc)
360
0
{
361
0
  if (doc == NULL)
362
0
    return 0;
363
0
  return (doc->output_accelerator) != NULL;
364
0
}
365
366
void *
367
fz_new_document_of_size(fz_context *ctx, int size)
368
15.5k
{
369
15.5k
  fz_document *doc = fz_calloc(ctx, 1, size);
370
15.5k
  doc->refs = 1;
371
15.5k
  return doc;
372
15.5k
}
373
374
fz_document *
375
fz_keep_document(fz_context *ctx, fz_document *doc)
376
42.3k
{
377
42.3k
  return fz_keep_imp(ctx, doc, &doc->refs);
378
42.3k
}
379
380
void
381
fz_drop_document(fz_context *ctx, fz_document *doc)
382
58.5k
{
383
58.5k
  if (fz_drop_imp(ctx, doc, &doc->refs))
384
15.5k
  {
385
15.5k
    if (doc->open)
386
0
      fz_warn(ctx, "There are still open pages in the document!");
387
15.5k
    if (doc->drop_document)
388
15.5k
      doc->drop_document(ctx, doc);
389
15.5k
    fz_free(ctx, doc);
390
15.5k
  }
391
58.5k
}
392
393
static void
394
fz_ensure_layout(fz_context *ctx, fz_document *doc)
395
108k
{
396
108k
  if (doc && doc->layout && !doc->did_layout)
397
14
  {
398
14
    doc->layout(ctx, doc, DEFW, DEFH, DEFEM);
399
14
    doc->did_layout = 1;
400
14
  }
401
108k
}
402
403
int
404
fz_is_document_reflowable(fz_context *ctx, fz_document *doc)
405
0
{
406
0
  return doc ? doc->is_reflowable : 0;
407
0
}
408
409
fz_bookmark fz_make_bookmark(fz_context *ctx, fz_document *doc, fz_location loc)
410
0
{
411
0
  if (doc && doc->make_bookmark)
412
0
    return doc->make_bookmark(ctx, doc, loc);
413
0
  return (loc.chapter<<16) + loc.page;
414
0
}
415
416
fz_location fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark mark)
417
0
{
418
0
  if (doc && doc->lookup_bookmark)
419
0
    return doc->lookup_bookmark(ctx, doc, mark);
420
0
  return fz_make_location((mark>>16) & 0xffff, mark & 0xffff);
421
0
}
422
423
int
424
fz_needs_password(fz_context *ctx, fz_document *doc)
425
0
{
426
0
  if (doc && doc->needs_password)
427
0
    return doc->needs_password(ctx, doc);
428
0
  return 0;
429
0
}
430
431
int
432
fz_authenticate_password(fz_context *ctx, fz_document *doc, const char *password)
433
0
{
434
0
  if (doc && doc->authenticate_password)
435
0
    return doc->authenticate_password(ctx, doc, password);
436
0
  return 1;
437
0
}
438
439
int
440
fz_has_permission(fz_context *ctx, fz_document *doc, fz_permission p)
441
0
{
442
0
  if (doc && doc->has_permission)
443
0
    return doc->has_permission(ctx, doc, p);
444
0
  return 1;
445
0
}
446
447
fz_outline *
448
fz_load_outline(fz_context *ctx, fz_document *doc)
449
0
{
450
0
  if (doc == NULL)
451
0
    return NULL;
452
0
  fz_ensure_layout(ctx, doc);
453
0
  if (doc->load_outline)
454
0
    return doc->load_outline(ctx, doc);
455
0
  if (doc->outline_iterator == NULL)
456
0
    return NULL;
457
0
  return fz_load_outline_from_iterator(ctx, doc->outline_iterator(ctx, doc));
458
0
}
459
460
fz_outline_iterator *
461
fz_new_outline_iterator(fz_context *ctx, fz_document *doc)
462
0
{
463
0
  if (doc == NULL)
464
0
    return NULL;
465
0
  if (doc->outline_iterator)
466
0
    return doc->outline_iterator(ctx, doc);
467
0
  if (doc->load_outline == NULL)
468
0
    return NULL;
469
0
  return fz_outline_iterator_from_outline(ctx, fz_load_outline(ctx, doc));
470
0
}
471
472
fz_link_dest
473
fz_resolve_link_dest(fz_context *ctx, fz_document *doc, const char *uri)
474
0
{
475
0
  fz_ensure_layout(ctx, doc);
476
0
  if (doc && doc->resolve_link_dest)
477
0
    return doc->resolve_link_dest(ctx, doc, uri);
478
0
  return fz_make_link_dest_none();
479
0
}
480
481
char *
482
fz_format_link_uri(fz_context *ctx, fz_document *doc, fz_link_dest dest)
483
0
{
484
0
  if (doc && doc->format_link_uri)
485
0
    return doc->format_link_uri(ctx, doc, dest);
486
0
  fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create internal links for this document type");
487
0
}
488
489
fz_location
490
fz_resolve_link(fz_context *ctx, fz_document *doc, const char *uri, float *xp, float *yp)
491
0
{
492
0
  fz_link_dest dest = fz_resolve_link_dest(ctx, doc, uri);
493
0
  if (xp) *xp = dest.x;
494
0
  if (yp) *yp = dest.y;
495
0
  return dest.loc;
496
0
}
497
498
void
499
fz_layout_document(fz_context *ctx, fz_document *doc, float w, float h, float em)
500
0
{
501
0
  if (doc && doc->layout)
502
0
  {
503
0
    doc->layout(ctx, doc, w, h, em);
504
0
    doc->did_layout = 1;
505
0
  }
506
0
}
507
508
int
509
fz_count_chapters(fz_context *ctx, fz_document *doc)
510
46.0k
{
511
46.0k
  fz_ensure_layout(ctx, doc);
512
46.0k
  if (doc && doc->count_chapters)
513
0
    return doc->count_chapters(ctx, doc);
514
46.0k
  return 1;
515
46.0k
}
516
517
int
518
fz_count_chapter_pages(fz_context *ctx, fz_document *doc, int chapter)
519
46.0k
{
520
46.0k
  fz_ensure_layout(ctx, doc);
521
46.0k
  if (doc && doc->count_pages)
522
46.0k
    return doc->count_pages(ctx, doc, chapter);
523
0
  return 0;
524
46.0k
}
525
526
int
527
fz_count_pages(fz_context *ctx, fz_document *doc)
528
29.7k
{
529
29.7k
  int i, c, n = 0;
530
29.7k
  c = fz_count_chapters(ctx, doc);
531
59.5k
  for (i = 0; i < c; ++i)
532
29.7k
    n += fz_count_chapter_pages(ctx, doc, i);
533
29.7k
  return n;
534
29.7k
}
535
536
fz_page *
537
fz_load_page(fz_context *ctx, fz_document *doc, int number)
538
16.2k
{
539
16.2k
  int i, n = fz_count_chapters(ctx, doc);
540
16.2k
  int start = 0;
541
16.2k
  for (i = 0; i < n; ++i)
542
16.2k
  {
543
16.2k
    int m = fz_count_chapter_pages(ctx, doc, i);
544
16.2k
    if (number < start + m)
545
16.2k
      return fz_load_chapter_page(ctx, doc, i, number - start);
546
0
    start += m;
547
0
  }
548
0
  fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid page number: %d", number+1);
549
16.2k
}
550
551
fz_location fz_last_page(fz_context *ctx, fz_document *doc)
552
0
{
553
0
  int nc = fz_count_chapters(ctx, doc);
554
0
  int np = fz_count_chapter_pages(ctx, doc, nc-1);
555
0
  return fz_make_location(nc-1, np-1);
556
0
}
557
558
fz_location fz_next_page(fz_context *ctx, fz_document *doc, fz_location loc)
559
0
{
560
0
  int nc = fz_count_chapters(ctx, doc);
561
0
  int np = fz_count_chapter_pages(ctx, doc, loc.chapter);
562
0
  if (loc.page + 1 == np)
563
0
  {
564
0
    if (loc.chapter + 1 < nc)
565
0
    {
566
0
      return fz_make_location(loc.chapter + 1, 0);
567
0
    }
568
0
  }
569
0
  else
570
0
  {
571
0
    return fz_make_location(loc.chapter, loc.page + 1);
572
0
  }
573
0
  return loc;
574
0
}
575
576
fz_location fz_previous_page(fz_context *ctx, fz_document *doc, fz_location loc)
577
0
{
578
0
  if (loc.page == 0)
579
0
  {
580
0
    if (loc.chapter > 0)
581
0
    {
582
0
      int np = fz_count_chapter_pages(ctx, doc, loc.chapter - 1);
583
0
      return fz_make_location(loc.chapter - 1, np - 1);
584
0
    }
585
0
  }
586
0
  else
587
0
  {
588
0
    return fz_make_location(loc.chapter, loc.page - 1);
589
0
  }
590
0
  return loc;
591
0
}
592
593
fz_location fz_clamp_location(fz_context *ctx, fz_document *doc, fz_location loc)
594
0
{
595
0
  int nc = fz_count_chapters(ctx, doc);
596
0
  int np;
597
0
  if (loc.chapter < 0) loc.chapter = 0;
598
0
  if (loc.chapter >= nc) loc.chapter = nc - 1;
599
0
  np = fz_count_chapter_pages(ctx, doc, loc.chapter);
600
0
  if (loc.page < 0) loc.page = 0;
601
0
  if (loc.page >= np) loc.page = np - 1;
602
0
  return loc;
603
0
}
604
605
fz_location fz_location_from_page_number(fz_context *ctx, fz_document *doc, int number)
606
0
{
607
0
  int i, m = 0, n = fz_count_chapters(ctx, doc);
608
0
  int start = 0;
609
0
  if (number < 0)
610
0
    number = 0;
611
0
  for (i = 0; i < n; ++i)
612
0
  {
613
0
    m = fz_count_chapter_pages(ctx, doc, i);
614
0
    if (number < start + m)
615
0
      return fz_make_location(i, number - start);
616
0
    start += m;
617
0
  }
618
0
  return fz_make_location(i-1, m-1);
619
0
}
620
621
int fz_page_number_from_location(fz_context *ctx, fz_document *doc, fz_location loc)
622
0
{
623
0
  int i, n, start = 0;
624
0
  n = fz_count_chapters(ctx, doc);
625
0
  for (i = 0; i < n; ++i)
626
0
  {
627
0
    if (i == loc.chapter)
628
0
      return start + loc.page;
629
0
    start += fz_count_chapter_pages(ctx, doc, i);
630
0
  }
631
0
  return -1;
632
0
}
633
634
int
635
fz_lookup_metadata(fz_context *ctx, fz_document *doc, const char *key, char *buf, int size)
636
0
{
637
0
  if (buf && size > 0)
638
0
    buf[0] = 0;
639
0
  if (doc && doc->lookup_metadata)
640
0
    return doc->lookup_metadata(ctx, doc, key, buf, size);
641
0
  return -1;
642
0
}
643
644
void
645
fz_set_metadata(fz_context *ctx, fz_document *doc, const char *key, const char *value)
646
0
{
647
0
  if (doc && doc->set_metadata)
648
0
    doc->set_metadata(ctx, doc, key, value);
649
0
}
650
651
fz_colorspace *
652
fz_document_output_intent(fz_context *ctx, fz_document *doc)
653
0
{
654
0
  if (doc && doc->get_output_intent)
655
0
    return doc->get_output_intent(ctx, doc);
656
0
  return NULL;
657
0
}
658
659
fz_page *
660
fz_load_chapter_page(fz_context *ctx, fz_document *doc, int chapter, int number)
661
16.2k
{
662
16.2k
  fz_page *page;
663
664
16.2k
  if (doc == NULL)
665
0
    return NULL;
666
667
16.2k
  fz_ensure_layout(ctx, doc);
668
669
  /* Protect modifications to the page list to cope with
670
   * destruction of pages on other threads. */
671
16.2k
  fz_lock(ctx, FZ_LOCK_ALLOC);
672
16.2k
  for (page = doc->open; page; page = page->next)
673
0
    if (page->chapter == chapter && page->number == number)
674
0
    {
675
0
      fz_keep_page_locked(ctx, page);
676
0
      fz_unlock(ctx, FZ_LOCK_ALLOC);
677
0
      return page;
678
0
    }
679
16.2k
  fz_unlock(ctx, FZ_LOCK_ALLOC);
680
681
16.2k
  if (doc->load_page)
682
16.2k
  {
683
16.2k
    page = doc->load_page(ctx, doc, chapter, number);
684
16.2k
    page->chapter = chapter;
685
16.2k
    page->number = number;
686
687
    /* Insert new page at the head of the list of open pages. */
688
16.2k
    if (!page->incomplete)
689
14.9k
    {
690
14.9k
      fz_lock(ctx, FZ_LOCK_ALLOC);
691
14.9k
      if ((page->next = doc->open) != NULL)
692
0
        doc->open->prev = &page->next;
693
14.9k
      doc->open = page;
694
14.9k
      page->prev = &doc->open;
695
14.9k
      fz_unlock(ctx, FZ_LOCK_ALLOC);
696
14.9k
    }
697
16.2k
    return page;
698
16.2k
  }
699
700
0
  return NULL;
701
16.2k
}
702
703
fz_link *
704
fz_load_links(fz_context *ctx, fz_page *page)
705
0
{
706
0
  if (page && page->load_links)
707
0
    return page->load_links(ctx, page);
708
0
  return NULL;
709
0
}
710
711
fz_rect
712
fz_bound_page(fz_context *ctx, fz_page *page)
713
14.9k
{
714
14.9k
  if (page && page->bound_page)
715
14.9k
    return page->bound_page(ctx, page, FZ_CROP_BOX);
716
0
  return fz_empty_rect;
717
14.9k
}
718
719
fz_rect
720
fz_bound_page_box(fz_context *ctx, fz_page *page, fz_box_type box)
721
0
{
722
0
  if (page && page->bound_page)
723
0
    return page->bound_page(ctx, page, box);
724
0
  return fz_empty_rect;
725
0
}
726
727
void
728
fz_run_document_structure(fz_context *ctx, fz_document *doc, fz_device *dev, fz_cookie *cookie)
729
0
{
730
0
  if (doc && doc->run_structure)
731
0
  {
732
0
    fz_try(ctx)
733
0
    {
734
0
      doc->run_structure(ctx, doc, dev, cookie);
735
0
    }
736
0
    fz_catch(ctx)
737
0
    {
738
0
      dev->close_device = NULL; /* aborted run, don't warn about unclosed device */
739
0
      fz_rethrow_unless(ctx, FZ_ERROR_ABORT);
740
0
      fz_ignore_error(ctx);
741
0
    }
742
0
  }
743
0
}
744
745
void
746
fz_run_page_contents(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie)
747
14.9k
{
748
14.9k
  if (page && page->run_page_contents)
749
14.9k
  {
750
29.9k
    fz_try(ctx)
751
29.9k
    {
752
14.9k
      page->run_page_contents(ctx, page, dev, transform, cookie);
753
14.9k
    }
754
29.9k
    fz_catch(ctx)
755
186
    {
756
186
      dev->close_device = NULL; /* aborted run, don't warn about unclosed device */
757
186
      fz_rethrow_unless(ctx, FZ_ERROR_ABORT);
758
186
      fz_ignore_error(ctx);
759
186
    }
760
14.9k
  }
761
14.9k
}
762
763
void
764
fz_run_page_annots(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie)
765
14.7k
{
766
14.7k
  if (page && page->run_page_annots)
767
12.5k
  {
768
25.1k
    fz_try(ctx)
769
25.1k
    {
770
12.5k
      page->run_page_annots(ctx, page, dev, transform, cookie);
771
12.5k
    }
772
25.1k
    fz_catch(ctx)
773
1
    {
774
1
      dev->close_device = NULL; /* aborted run, don't warn about unclosed device */
775
1
      fz_rethrow_unless(ctx, FZ_ERROR_ABORT);
776
1
      fz_ignore_error(ctx);
777
1
    }
778
12.5k
  }
779
14.7k
}
780
781
void
782
fz_run_page_widgets(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie)
783
14.7k
{
784
14.7k
  if (page && page->run_page_widgets)
785
12.5k
  {
786
25.1k
    fz_try(ctx)
787
25.1k
    {
788
12.5k
      page->run_page_widgets(ctx, page, dev, transform, cookie);
789
12.5k
    }
790
25.1k
    fz_catch(ctx)
791
1
    {
792
1
      dev->close_device = NULL; /* aborted run, don't warn about unclosed device */
793
1
      fz_rethrow_unless(ctx, FZ_ERROR_ABORT);
794
1
      fz_ignore_error(ctx);
795
1
    }
796
12.5k
  }
797
14.7k
}
798
799
void
800
fz_run_page(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie)
801
14.9k
{
802
14.9k
  fz_run_page_contents(ctx, page, dev, transform, cookie);
803
14.9k
  fz_run_page_annots(ctx, page, dev, transform, cookie);
804
14.9k
  fz_run_page_widgets(ctx, page, dev, transform, cookie);
805
14.9k
}
806
807
fz_page *
808
fz_new_page_of_size(fz_context *ctx, int size, fz_document *doc)
809
14.9k
{
810
14.9k
  fz_page *page = Memento_label(fz_calloc(ctx, 1, size), "fz_page");
811
14.9k
  page->refs = 1;
812
14.9k
  page->doc = fz_keep_document(ctx, doc);
813
14.9k
  return page;
814
14.9k
}
815
816
fz_page *
817
fz_keep_page(fz_context *ctx, fz_page *page)
818
0
{
819
0
  return fz_keep_imp(ctx, page, &page->refs);
820
0
}
821
822
fz_page *
823
fz_keep_page_locked(fz_context *ctx, fz_page *page)
824
0
{
825
0
  return fz_keep_imp_locked(ctx, page, &page->refs);
826
0
}
827
828
void
829
fz_drop_page(fz_context *ctx, fz_page *page)
830
18.6k
{
831
18.6k
  if (fz_drop_imp(ctx, page, &page->refs))
832
14.9k
  {
833
    /* Remove page from the list of open pages */
834
14.9k
    fz_lock(ctx, FZ_LOCK_ALLOC);
835
14.9k
    if (page->next != NULL)
836
0
      page->next->prev = page->prev;
837
14.9k
    if (page->prev != NULL)
838
14.9k
      *page->prev = page->next;
839
14.9k
    fz_unlock(ctx, FZ_LOCK_ALLOC);
840
841
14.9k
    if (page->drop_page)
842
14.9k
      page->drop_page(ctx, page);
843
844
14.9k
    fz_drop_document(ctx, page->doc);
845
846
14.9k
    fz_free(ctx, page);
847
14.9k
  }
848
18.6k
}
849
850
fz_transition *
851
fz_page_presentation(fz_context *ctx, fz_page *page, fz_transition *transition, float *duration)
852
0
{
853
0
  float dummy;
854
0
  if (duration)
855
0
    *duration = 0;
856
0
  else
857
0
    duration = &dummy;
858
0
  if (page && page->page_presentation && page)
859
0
    return page->page_presentation(ctx, page, transition, duration);
860
0
  return NULL;
861
0
}
862
863
fz_separations *
864
fz_page_separations(fz_context *ctx, fz_page *page)
865
0
{
866
0
  if (page && page->separations)
867
0
    return page->separations(ctx, page);
868
0
  return NULL;
869
0
}
870
871
int fz_page_uses_overprint(fz_context *ctx, fz_page *page)
872
0
{
873
0
  if (page && page->overprint)
874
0
    return page->overprint(ctx, page);
875
0
  return 0;
876
0
}
877
878
fz_link *fz_create_link(fz_context *ctx, fz_page *page, fz_rect bbox, const char *uri)
879
0
{
880
0
  if (page == NULL || uri == NULL)
881
0
    return NULL;
882
0
  if (page->create_link == NULL)
883
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "This format of document does not support creating links");
884
0
  return page->create_link(ctx, page, bbox, uri);
885
0
}
886
887
void fz_delete_link(fz_context *ctx, fz_page *page, fz_link *link)
888
0
{
889
0
  if (page == NULL || link == NULL)
890
0
    return;
891
0
  if (page->delete_link == NULL)
892
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "This format of document does not support deleting links");
893
0
  page->delete_link(ctx, page, link);
894
0
}
895
896
void fz_set_link_rect(fz_context *ctx, fz_link *link, fz_rect rect)
897
0
{
898
0
  if (link == NULL)
899
0
    return;
900
0
  if (link->set_rect_fn == NULL)
901
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "This format of document does not support updating link bounds");
902
0
  link->set_rect_fn(ctx, link, rect);
903
0
}
904
905
void fz_set_link_uri(fz_context *ctx, fz_link *link, const char *uri)
906
0
{
907
0
  if (link == NULL)
908
0
    return;
909
0
  if (link->set_uri_fn == NULL)
910
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "This format of document does not support updating link uri");
911
0
  link->set_uri_fn(ctx, link, uri);
912
0
}
913
914
void *
915
fz_process_opened_pages(fz_context *ctx, fz_document *doc, fz_process_opened_page_fn *process_opened_page, void *state)
916
1.81k
{
917
1.81k
  fz_page *page;
918
1.81k
  fz_page *kept = NULL;
919
1.81k
  fz_page *dropme = NULL;
920
1.81k
  void *ret = NULL;
921
922
1.81k
  fz_var(kept);
923
1.81k
  fz_var(dropme);
924
1.81k
  fz_var(page);
925
3.63k
  fz_try(ctx)
926
3.63k
  {
927
    /* We can only walk the page list while the alloc lock is taken, so gymnastics are required. */
928
    /* Loop invariant: at any point where we might throw, kept != NULL iff we are unlocked. */
929
1.81k
    fz_lock(ctx, FZ_LOCK_ALLOC);
930
1.81k
    for (page = doc->open; ret == NULL && page != NULL; page = page->next)
931
0
    {
932
      /* Keep an extra reference to the page so that no other thread can remove it. */
933
0
      kept = fz_keep_page_locked(ctx, page);
934
0
      fz_unlock(ctx, FZ_LOCK_ALLOC);
935
      /* Drop any extra reference we might still have to a previous page. */
936
0
      fz_drop_page(ctx, dropme);
937
0
      dropme = NULL;
938
939
0
      ret = process_opened_page(ctx, page, state);
940
941
      /* We can't drop kept here, because that would give us a race condition with
942
       * us taking the lock and hoping that 'page' would still be valid. So remember it
943
       * for dropping later. */
944
0
      dropme = kept;
945
0
      kept = NULL;
946
0
      fz_lock(ctx, FZ_LOCK_ALLOC);
947
0
    }
948
    /* unlock (and final drop of dropme) happens in the always. */
949
1.81k
  }
950
3.63k
  fz_always(ctx)
951
1.81k
  {
952
1.81k
    if (kept == NULL)
953
1.81k
      fz_unlock(ctx, FZ_LOCK_ALLOC);
954
1.81k
    fz_drop_page(ctx, kept);
955
1.81k
    fz_drop_page(ctx, dropme);
956
1.81k
  }
957
1.81k
  fz_catch(ctx)
958
0
  {
959
0
    fz_rethrow(ctx);
960
0
  }
961
962
1.81k
  return ret;
963
1.81k
}
964
965
const char *
966
fz_page_label(fz_context *ctx, fz_page *page, char *buf, int size)
967
0
{
968
0
  fz_document *doc = page->doc;
969
0
  if (doc->page_label)
970
0
    doc->page_label(ctx, page->doc, page->chapter, page->number, buf, size);
971
0
  else if (fz_count_chapters(ctx, page->doc) > 1)
972
0
    fz_snprintf(buf, size, "%d/%d", page->chapter + 1, page->number + 1);
973
0
  else
974
0
    fz_snprintf(buf, size, "%d", page->number + 1);
975
0
  return buf;
976
0
}
977
978
979
fz_box_type fz_box_type_from_string(const char *name)
980
0
{
981
0
  if (!fz_strcasecmp(name, "MediaBox"))
982
0
    return FZ_MEDIA_BOX;
983
0
  if (!fz_strcasecmp(name, "CropBox"))
984
0
    return FZ_CROP_BOX;
985
0
  if (!fz_strcasecmp(name, "BleedBox"))
986
0
    return FZ_BLEED_BOX;
987
0
  if (!fz_strcasecmp(name, "TrimBox"))
988
0
    return FZ_TRIM_BOX;
989
0
  if (!fz_strcasecmp(name, "ArtBox"))
990
0
    return FZ_ART_BOX;
991
0
  return FZ_UNKNOWN_BOX;
992
0
}
993
994
const char *fz_string_from_box_type(fz_box_type box)
995
0
{
996
0
  switch (box)
997
0
  {
998
0
  case FZ_MEDIA_BOX: return "MediaBox";
999
0
  case FZ_CROP_BOX: return "CropBox";
1000
0
  case FZ_BLEED_BOX: return "BleedBox";
1001
0
  case FZ_TRIM_BOX: return "TrimBox";
1002
0
  case FZ_ART_BOX: return "ArtBox";
1003
0
  default: return "UnknownBox";
1004
0
  }
1005
0
}