Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/util.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
#include "mupdf/fitz.h"
24
25
#include <float.h>
26
27
fz_display_list *
28
fz_new_display_list_from_page(fz_context *ctx, fz_page *page)
29
0
{
30
0
  fz_display_list *list;
31
0
  fz_device *dev = NULL;
32
33
0
  fz_var(dev);
34
35
0
  list = fz_new_display_list(ctx, fz_bound_page(ctx, page));
36
0
  fz_try(ctx)
37
0
  {
38
0
    dev = fz_new_list_device(ctx, list);
39
0
    fz_run_page(ctx, page, dev, fz_identity, NULL);
40
0
    fz_close_device(ctx, dev);
41
0
  }
42
0
  fz_always(ctx)
43
0
  {
44
0
    fz_drop_device(ctx, dev);
45
0
  }
46
0
  fz_catch(ctx)
47
0
  {
48
0
    fz_drop_display_list(ctx, list);
49
0
    fz_rethrow(ctx);
50
0
  }
51
52
0
  return list;
53
0
}
54
55
fz_display_list *
56
fz_new_display_list_from_page_number(fz_context *ctx, fz_document *doc, int number)
57
0
{
58
0
  fz_page *page;
59
0
  fz_display_list *list = NULL;
60
61
0
  page = fz_load_page(ctx, doc, number);
62
0
  fz_try(ctx)
63
0
    list = fz_new_display_list_from_page(ctx, page);
64
0
  fz_always(ctx)
65
0
    fz_drop_page(ctx, page);
66
0
  fz_catch(ctx)
67
0
    fz_rethrow(ctx);
68
0
  return list;
69
0
}
70
71
fz_display_list *
72
fz_new_display_list_from_page_contents(fz_context *ctx, fz_page *page)
73
0
{
74
0
  fz_display_list *list;
75
0
  fz_device *dev = NULL;
76
77
0
  fz_var(dev);
78
79
0
  list = fz_new_display_list(ctx, fz_bound_page(ctx, page));
80
0
  fz_try(ctx)
81
0
  {
82
0
    dev = fz_new_list_device(ctx, list);
83
0
    fz_run_page_contents(ctx, page, dev, fz_identity, NULL);
84
0
    fz_close_device(ctx, dev);
85
0
  }
86
0
  fz_always(ctx)
87
0
  {
88
0
    fz_drop_device(ctx, dev);
89
0
  }
90
0
  fz_catch(ctx)
91
0
  {
92
0
    fz_drop_display_list(ctx, list);
93
0
    fz_rethrow(ctx);
94
0
  }
95
96
0
  return list;
97
0
}
98
99
fz_pixmap *
100
fz_new_pixmap_from_display_list(fz_context *ctx, fz_display_list *list, fz_matrix ctm, fz_colorspace *cs, int alpha)
101
0
{
102
0
  return fz_new_pixmap_from_display_list_with_separations(ctx, list, ctm, cs, NULL, alpha);
103
0
}
104
105
fz_pixmap *
106
fz_new_pixmap_from_display_list_with_separations(fz_context *ctx, fz_display_list *list, fz_matrix ctm, fz_colorspace *cs, fz_separations *seps, int alpha)
107
0
{
108
0
  fz_rect rect;
109
0
  fz_irect bbox;
110
0
  fz_pixmap *pix;
111
112
0
  rect = fz_bound_display_list(ctx, list);
113
0
  rect = fz_transform_rect(rect, ctm);
114
0
  bbox = fz_round_rect(rect);
115
116
0
  pix = fz_new_pixmap_with_bbox(ctx, cs, bbox, seps, alpha);
117
0
  if (alpha)
118
0
    fz_clear_pixmap(ctx, pix);
119
0
  else
120
0
    fz_clear_pixmap_with_value(ctx, pix, 0xFF);
121
122
0
  fz_try(ctx)
123
0
    fz_fill_pixmap_from_display_list(ctx, list, ctm, pix);
124
0
  fz_catch(ctx)
125
0
  {
126
0
    fz_drop_pixmap(ctx, pix);
127
0
    fz_rethrow(ctx);
128
0
  }
129
130
0
  return pix;
131
0
}
132
133
fz_pixmap *
134
fz_fill_pixmap_from_display_list(fz_context *ctx, fz_display_list *list, fz_matrix ctm, fz_pixmap *pix)
135
0
{
136
0
  fz_device *dev = NULL;
137
138
0
  fz_var(dev);
139
140
0
  fz_try(ctx)
141
0
  {
142
0
    dev = fz_new_draw_device(ctx, ctm, pix);
143
0
    fz_run_display_list(ctx, list, dev, fz_identity, fz_infinite_rect, NULL);
144
0
    fz_close_device(ctx, dev);
145
0
  }
146
0
  fz_always(ctx)
147
0
    fz_drop_device(ctx, dev);
148
0
  fz_catch(ctx)
149
0
    fz_rethrow(ctx);
150
151
0
  return pix;
152
0
}
153
154
fz_pixmap *
155
fz_new_pixmap_from_page_contents(fz_context *ctx, fz_page *page, fz_matrix ctm, fz_colorspace *cs, int alpha)
156
0
{
157
0
  return fz_new_pixmap_from_page_contents_with_separations(ctx, page, ctm, cs, NULL, alpha);
158
0
}
159
160
fz_pixmap *
161
fz_new_pixmap_from_page_contents_with_separations(fz_context *ctx, fz_page *page, fz_matrix ctm, fz_colorspace *cs, fz_separations *seps, int alpha)
162
0
{
163
0
  fz_rect rect;
164
0
  fz_irect bbox;
165
0
  fz_pixmap *pix;
166
0
  fz_device *dev = NULL;
167
168
0
  fz_var(dev);
169
170
0
  rect = fz_bound_page(ctx, page);
171
0
  rect = fz_transform_rect(rect, ctm);
172
0
  bbox = fz_round_rect(rect);
173
174
0
  pix = fz_new_pixmap_with_bbox(ctx, cs, bbox, seps, alpha);
175
0
  if (alpha)
176
0
    fz_clear_pixmap(ctx, pix);
177
0
  else
178
0
    fz_clear_pixmap_with_value(ctx, pix, 0xFF);
179
180
0
  fz_try(ctx)
181
0
  {
182
0
    dev = fz_new_draw_device(ctx, ctm, pix);
183
0
    fz_run_page_contents(ctx, page, dev, fz_identity, NULL);
184
0
    fz_close_device(ctx, dev);
185
0
  }
186
0
  fz_always(ctx)
187
0
  {
188
0
    fz_drop_device(ctx, dev);
189
0
  }
190
0
  fz_catch(ctx)
191
0
  {
192
0
    fz_drop_pixmap(ctx, pix);
193
0
    fz_rethrow(ctx);
194
0
  }
195
196
0
  return pix;
197
0
}
198
199
fz_pixmap *
200
fz_new_pixmap_from_page(fz_context *ctx, fz_page *page, fz_matrix ctm, fz_colorspace *cs, int alpha)
201
0
{
202
0
  return fz_new_pixmap_from_page_with_separations(ctx, page, ctm, cs, NULL, alpha);
203
0
}
204
205
fz_pixmap *
206
fz_new_pixmap_from_page_with_separations(fz_context *ctx, fz_page *page, fz_matrix ctm, fz_colorspace *cs, fz_separations *seps, int alpha)
207
356
{
208
356
  fz_rect rect;
209
356
  fz_irect bbox;
210
356
  fz_pixmap *pix;
211
356
  fz_device *dev = NULL;
212
213
356
  fz_var(dev);
214
215
356
  rect = fz_bound_page(ctx, page);
216
356
  rect = fz_transform_rect(rect, ctm);
217
356
  bbox = fz_round_rect(rect);
218
219
356
  pix = fz_new_pixmap_with_bbox(ctx, cs, bbox, seps, alpha);
220
221
712
  fz_try(ctx)
222
712
  {
223
356
    if (alpha)
224
0
      fz_clear_pixmap(ctx, pix);
225
356
    else
226
356
      fz_clear_pixmap_with_value(ctx, pix, 0xFF);
227
228
356
    dev = fz_new_draw_device(ctx, ctm, pix);
229
356
    fz_run_page(ctx, page, dev, fz_identity, NULL);
230
356
    fz_close_device(ctx, dev);
231
356
  }
232
712
  fz_always(ctx)
233
356
  {
234
356
    fz_drop_device(ctx, dev);
235
356
  }
236
356
  fz_catch(ctx)
237
6
  {
238
6
    fz_drop_pixmap(ctx, pix);
239
6
    fz_rethrow(ctx);
240
6
  }
241
242
350
  return pix;
243
356
}
244
245
fz_pixmap *
246
fz_new_pixmap_from_page_number(fz_context *ctx, fz_document *doc, int number, fz_matrix ctm, fz_colorspace *cs, int alpha)
247
356
{
248
356
  return fz_new_pixmap_from_page_number_with_separations(ctx, doc, number, ctm, cs, NULL, alpha);
249
356
}
250
251
fz_pixmap *
252
fz_new_pixmap_from_page_number_with_separations(fz_context *ctx, fz_document *doc, int number, fz_matrix ctm, fz_colorspace *cs, fz_separations *seps, int alpha)
253
356
{
254
356
  fz_page *page;
255
356
  fz_pixmap *pix = NULL;
256
257
356
  page = fz_load_page(ctx, doc, number);
258
712
  fz_try(ctx)
259
712
    pix = fz_new_pixmap_from_page_with_separations(ctx, page, ctm, cs, seps, alpha);
260
712
  fz_always(ctx)
261
356
    fz_drop_page(ctx, page);
262
356
  fz_catch(ctx)
263
6
    fz_rethrow(ctx);
264
350
  return pix;
265
356
}
266
267
fz_stext_page *
268
fz_new_stext_page_from_display_list(fz_context *ctx, fz_display_list *list, const fz_stext_options *options)
269
0
{
270
0
  fz_stext_page *text;
271
0
  fz_device *dev = NULL;
272
273
0
  fz_var(dev);
274
275
0
  if (list == NULL)
276
0
    return NULL;
277
278
0
  text = fz_new_stext_page(ctx, fz_bound_display_list(ctx, list));
279
0
  fz_try(ctx)
280
0
  {
281
0
    dev = fz_new_stext_device(ctx, text, options);
282
0
    fz_run_display_list(ctx, list, dev, fz_identity, fz_infinite_rect, NULL);
283
0
    fz_close_device(ctx, dev);
284
0
  }
285
0
  fz_always(ctx)
286
0
  {
287
0
    fz_drop_device(ctx, dev);
288
0
  }
289
0
  fz_catch(ctx)
290
0
  {
291
0
    fz_drop_stext_page(ctx, text);
292
0
    fz_rethrow(ctx);
293
0
  }
294
295
0
  return text;
296
0
}
297
298
fz_stext_page *
299
fz_new_stext_page_from_page_with_cookie(fz_context *ctx, fz_page *page, const fz_stext_options *options, fz_cookie *cookie)
300
0
{
301
0
  fz_stext_page *text;
302
0
  fz_device *dev = NULL;
303
304
0
  fz_var(dev);
305
306
0
  if (page == NULL)
307
0
    return NULL;
308
309
0
  text = fz_new_stext_page(ctx, fz_bound_page(ctx, page));
310
0
  fz_try(ctx)
311
0
  {
312
0
    dev = fz_new_stext_device(ctx, text, options);
313
0
    fz_run_page_contents(ctx, page, dev, fz_identity, cookie);
314
0
    fz_close_device(ctx, dev);
315
0
  }
316
0
  fz_always(ctx)
317
0
  {
318
0
    fz_drop_device(ctx, dev);
319
0
  }
320
0
  fz_catch(ctx)
321
0
  {
322
0
    fz_drop_stext_page(ctx, text);
323
0
    fz_rethrow(ctx);
324
0
  }
325
326
0
  return text;
327
0
}
328
329
fz_stext_page *
330
fz_new_stext_page_from_page(fz_context *ctx, fz_page *page, const fz_stext_options *options)
331
0
{
332
0
  return fz_new_stext_page_from_page_with_cookie(ctx, page, options, NULL);
333
0
}
334
335
fz_stext_page *
336
fz_new_stext_page_from_page_number(fz_context *ctx, fz_document *doc, int number, const fz_stext_options *options)
337
0
{
338
0
  fz_page *page;
339
0
  fz_stext_page *text = NULL;
340
341
0
  page = fz_load_page(ctx, doc, number);
342
0
  fz_try(ctx)
343
0
    text = fz_new_stext_page_from_page(ctx, page, options);
344
0
  fz_always(ctx)
345
0
    fz_drop_page(ctx, page);
346
0
  fz_catch(ctx)
347
0
    fz_rethrow(ctx);
348
0
  return text;
349
0
}
350
351
fz_stext_page *
352
fz_new_stext_page_from_chapter_page_number(fz_context *ctx, fz_document *doc, int chapter, int number, const fz_stext_options *options)
353
0
{
354
0
  fz_page *page;
355
0
  fz_stext_page *text = NULL;
356
357
0
  page = fz_load_chapter_page(ctx, doc, chapter, number);
358
0
  fz_try(ctx)
359
0
    text = fz_new_stext_page_from_page(ctx, page, options);
360
0
  fz_always(ctx)
361
0
    fz_drop_page(ctx, page);
362
0
  fz_catch(ctx)
363
0
    fz_rethrow(ctx);
364
0
  return text;
365
0
}
366
367
int
368
fz_search_display_list(fz_context *ctx, fz_display_list *list, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max)
369
0
{
370
0
  fz_stext_page *text;
371
0
  int count = 0;
372
373
0
  text = fz_new_stext_page_from_display_list(ctx, list, NULL);
374
0
  fz_try(ctx)
375
0
    count = fz_search_stext_page(ctx, text, needle, hit_mark, hit_bbox, hit_max);
376
0
  fz_always(ctx)
377
0
    fz_drop_stext_page(ctx, text);
378
0
  fz_catch(ctx)
379
0
    fz_rethrow(ctx);
380
0
  return count;
381
0
}
382
383
int
384
fz_search_display_list_cb(fz_context *ctx, fz_display_list *list, const char *needle, fz_search_callback_fn *cb, void *opaque)
385
0
{
386
0
  fz_stext_page *text;
387
0
  int count = 0;
388
389
0
  text = fz_new_stext_page_from_display_list(ctx, list, NULL);
390
0
  fz_try(ctx)
391
0
    count = fz_search_stext_page_cb(ctx, text, needle, cb, opaque);
392
0
  fz_always(ctx)
393
0
    fz_drop_stext_page(ctx, text);
394
0
  fz_catch(ctx)
395
0
    fz_rethrow(ctx);
396
0
  return count;
397
0
}
398
399
int
400
fz_search_page(fz_context *ctx, fz_page *page, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max)
401
0
{
402
0
  fz_stext_options opts = { FZ_STEXT_DEHYPHENATE };
403
0
  fz_stext_page *text;
404
0
  int count = 0;
405
406
0
  text = fz_new_stext_page_from_page(ctx, page, &opts);
407
0
  fz_try(ctx)
408
0
    count = fz_search_stext_page(ctx, text, needle, hit_mark, hit_bbox, hit_max);
409
0
  fz_always(ctx)
410
0
    fz_drop_stext_page(ctx, text);
411
0
  fz_catch(ctx)
412
0
    fz_rethrow(ctx);
413
0
  return count;
414
0
}
415
416
int
417
fz_search_page_cb(fz_context *ctx, fz_page *page, const char *needle, fz_search_callback_fn *cb, void *opaque)
418
0
{
419
0
  fz_stext_options opts = { FZ_STEXT_DEHYPHENATE };
420
0
  fz_stext_page *text;
421
0
  int count = 0;
422
423
0
  text = fz_new_stext_page_from_page(ctx, page, &opts);
424
0
  fz_try(ctx)
425
0
    count = fz_search_stext_page_cb(ctx, text, needle, cb, opaque);
426
0
  fz_always(ctx)
427
0
    fz_drop_stext_page(ctx, text);
428
0
  fz_catch(ctx)
429
0
    fz_rethrow(ctx);
430
0
  return count;
431
0
}
432
433
int
434
fz_search_page_number(fz_context *ctx, fz_document *doc, int number, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max)
435
0
{
436
0
  fz_page *page;
437
0
  int count = 0;
438
439
0
  page = fz_load_page(ctx, doc, number);
440
0
  fz_try(ctx)
441
0
    count = fz_search_page(ctx, page, needle, hit_mark, hit_bbox, hit_max);
442
0
  fz_always(ctx)
443
0
    fz_drop_page(ctx, page);
444
0
  fz_catch(ctx)
445
0
    fz_rethrow(ctx);
446
0
  return count;
447
0
}
448
449
int
450
fz_search_page_number_cb(fz_context *ctx, fz_document *doc, int number, const char *needle, fz_search_callback_fn *cb, void *opaque)
451
0
{
452
0
  fz_page *page;
453
0
  int count = 0;
454
455
0
  page = fz_load_page(ctx, doc, number);
456
0
  fz_try(ctx)
457
0
    count = fz_search_page_cb(ctx, page, needle, cb, opaque);
458
0
  fz_always(ctx)
459
0
    fz_drop_page(ctx, page);
460
0
  fz_catch(ctx)
461
0
    fz_rethrow(ctx);
462
0
  return count;
463
0
}
464
465
int
466
fz_search_chapter_page_number(fz_context *ctx, fz_document *doc, int chapter, int number, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max)
467
0
{
468
0
  fz_page *page;
469
0
  int count = 0;
470
471
0
  page = fz_load_chapter_page(ctx, doc, chapter, number);
472
0
  fz_try(ctx)
473
0
    count = fz_search_page(ctx, page, needle, hit_mark, hit_bbox, hit_max);
474
0
  fz_always(ctx)
475
0
    fz_drop_page(ctx, page);
476
0
  fz_catch(ctx)
477
0
    fz_rethrow(ctx);
478
0
  return count;
479
0
}
480
481
int
482
fz_search_chapter_page_number_cb(fz_context *ctx, fz_document *doc, int chapter, int number, const char *needle, fz_search_callback_fn *cb, void *opaque)
483
0
{
484
0
  fz_page *page;
485
0
  int count = 0;
486
487
0
  page = fz_load_chapter_page(ctx, doc, chapter, number);
488
0
  fz_try(ctx)
489
0
    count = fz_search_page_cb(ctx, page, needle, cb, opaque);
490
0
  fz_always(ctx)
491
0
    fz_drop_page(ctx, page);
492
0
  fz_catch(ctx)
493
0
    fz_rethrow(ctx);
494
0
  return count;
495
0
}
496
497
int
498
fz_match_display_list(fz_context *ctx, fz_display_list *list, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max, fz_search_options options)
499
0
{
500
0
  fz_stext_page *text;
501
0
  int count = 0;
502
503
0
  text = fz_new_stext_page_from_display_list(ctx, list, NULL);
504
0
  fz_try(ctx)
505
0
    count = fz_match_stext_page(ctx, text, needle, hit_mark, hit_bbox, hit_max, options);
506
0
  fz_always(ctx)
507
0
    fz_drop_stext_page(ctx, text);
508
0
  fz_catch(ctx)
509
0
    fz_rethrow(ctx);
510
0
  return count;
511
0
}
512
513
int
514
fz_match_display_list_cb(fz_context *ctx, fz_display_list *list, const char *needle, fz_match_callback_fn *cb, void *opaque, fz_search_options options)
515
0
{
516
0
  fz_stext_page *text;
517
0
  int count = 0;
518
519
0
  text = fz_new_stext_page_from_display_list(ctx, list, NULL);
520
0
  fz_try(ctx)
521
0
    count = fz_match_stext_page_cb(ctx, text, needle, cb, opaque, options);
522
0
  fz_always(ctx)
523
0
    fz_drop_stext_page(ctx, text);
524
0
  fz_catch(ctx)
525
0
    fz_rethrow(ctx);
526
0
  return count;
527
0
}
528
529
int
530
fz_match_page(fz_context *ctx, fz_page *page, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max, fz_search_options options)
531
0
{
532
0
  fz_stext_options opts = { FZ_STEXT_DEHYPHENATE };
533
0
  fz_stext_page *text;
534
0
  int count = 0;
535
536
0
  text = fz_new_stext_page_from_page(ctx, page, &opts);
537
0
  fz_try(ctx)
538
0
    count = fz_match_stext_page(ctx, text, needle, hit_mark, hit_bbox, hit_max, options);
539
0
  fz_always(ctx)
540
0
    fz_drop_stext_page(ctx, text);
541
0
  fz_catch(ctx)
542
0
    fz_rethrow(ctx);
543
0
  return count;
544
0
}
545
546
int
547
fz_match_page_cb(fz_context *ctx, fz_page *page, const char *needle, fz_match_callback_fn *cb, void *opaque, fz_search_options options)
548
0
{
549
0
  fz_stext_options opts = { FZ_STEXT_DEHYPHENATE };
550
0
  fz_stext_page *text;
551
0
  int count = 0;
552
553
0
  text = fz_new_stext_page_from_page(ctx, page, &opts);
554
0
  fz_try(ctx)
555
0
    count = fz_match_stext_page_cb(ctx, text, needle, cb, opaque, options);
556
0
  fz_always(ctx)
557
0
    fz_drop_stext_page(ctx, text);
558
0
  fz_catch(ctx)
559
0
    fz_rethrow(ctx);
560
0
  return count;
561
0
}
562
563
int
564
fz_match_page_number(fz_context *ctx, fz_document *doc, int number, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max, fz_search_options options)
565
0
{
566
0
  fz_page *page;
567
0
  int count = 0;
568
569
0
  page = fz_load_page(ctx, doc, number);
570
0
  fz_try(ctx)
571
0
    count = fz_match_page(ctx, page, needle, hit_mark, hit_bbox, hit_max, options);
572
0
  fz_always(ctx)
573
0
    fz_drop_page(ctx, page);
574
0
  fz_catch(ctx)
575
0
    fz_rethrow(ctx);
576
0
  return count;
577
0
}
578
579
int
580
fz_match_page_number_cb(fz_context *ctx, fz_document *doc, int number, const char *needle, fz_match_callback_fn *cb, void *opaque, fz_search_options options)
581
0
{
582
0
  fz_page *page;
583
0
  int count = 0;
584
585
0
  page = fz_load_page(ctx, doc, number);
586
0
  fz_try(ctx)
587
0
    count = fz_match_page_cb(ctx, page, needle, cb, opaque, options);
588
0
  fz_always(ctx)
589
0
    fz_drop_page(ctx, page);
590
0
  fz_catch(ctx)
591
0
    fz_rethrow(ctx);
592
0
  return count;
593
0
}
594
595
int
596
fz_match_chapter_page_number(fz_context *ctx, fz_document *doc, int chapter, int number, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max, fz_search_options options)
597
0
{
598
0
  fz_page *page;
599
0
  int count = 0;
600
601
0
  page = fz_load_chapter_page(ctx, doc, chapter, number);
602
0
  fz_try(ctx)
603
0
    count = fz_match_page(ctx, page, needle, hit_mark, hit_bbox, hit_max, options);
604
0
  fz_always(ctx)
605
0
    fz_drop_page(ctx, page);
606
0
  fz_catch(ctx)
607
0
    fz_rethrow(ctx);
608
0
  return count;
609
0
}
610
611
int
612
fz_match_chapter_page_number_cb(fz_context *ctx, fz_document *doc, int chapter, int number, const char *needle, fz_match_callback_fn *cb, void *opaque, fz_search_options options)
613
0
{
614
0
  fz_page *page;
615
0
  int count = 0;
616
617
0
  page = fz_load_chapter_page(ctx, doc, chapter, number);
618
0
  fz_try(ctx)
619
0
    count = fz_match_page_cb(ctx, page, needle, cb, opaque, options);
620
0
  fz_always(ctx)
621
0
    fz_drop_page(ctx, page);
622
0
  fz_catch(ctx)
623
0
    fz_rethrow(ctx);
624
0
  return count;
625
0
}
626
627
fz_buffer *
628
fz_new_buffer_from_stext_page(fz_context *ctx, fz_stext_page *page)
629
0
{
630
0
  return fz_new_buffer_from_flattened_stext_page(ctx, page, FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS, NULL);
631
0
}
632
633
static int
634
inhibit_space_after_line_break(int c)
635
0
{
636
0
  return fz_is_unicode_whitespace(c) || fz_is_unicode_hyphen(c);
637
0
}
638
639
static int
640
do_flatten(fz_context *ctx, fz_buffer *buf, fz_stext_position **map, fz_stext_page *page, fz_stext_struct *parent, fz_stext_block *block, fz_text_flatten flatten, int *ws)
641
0
{
642
0
  fz_stext_line *line;
643
0
  fz_stext_char *ch;
644
0
  int n = 0;
645
646
0
  #define EMIT(X,Y) \
647
0
  { \
648
0
    if (map && *map) *(*map)++ = (fz_stext_position){ page, parent, block, line, X }; \
649
0
    if (buf) fz_append_rune(ctx, buf, Y); \
650
0
    ++n; \
651
0
  }
652
653
0
  for (; block != NULL; block = block->next)
654
0
  {
655
0
    if (block->type == FZ_STEXT_BLOCK_TEXT)
656
0
    {
657
0
      int join_line = 0;
658
0
      for (line = block->u.t.first_line; line; line = line->next)
659
0
      {
660
0
        join_line = 0;
661
0
        for (ch = line->first_char; ch; ch = ch->next)
662
0
        {
663
          /* Last character of a line where we aren't keeping hyphens; check for dehyphenation. */
664
0
          if (ch == line->last_char && (flatten & FZ_TEXT_FLATTEN_KEEP_HYPHENS) == 0)
665
0
          {
666
            /* Soft hyphens are always removed. */
667
0
            if (ch->c == 0xad)
668
0
            {
669
0
              join_line = 1;
670
0
              continue;
671
0
            }
672
            /* Non-soft hyphens are only broken if we extracted with dehyphenation. */
673
0
            if ((line->flags & FZ_STEXT_LINE_FLAGS_JOINED) != 0 && fz_is_unicode_hyphen(ch->c))
674
0
            {
675
0
              join_line = 1;
676
0
              continue;
677
0
            }
678
0
          }
679
680
          /* Soft hyphens at the beginning or in the middle of a line are always removed. */
681
0
          if (ch != line->last_char && ch->c == 0xad)
682
0
          {
683
0
            continue;
684
0
          }
685
686
0
          EMIT(ch, ch->c);
687
688
0
          *ws = inhibit_space_after_line_break(ch->c);
689
0
        }
690
691
0
        if (join_line)
692
0
        {
693
          /* No whitespace, no linebreak. */
694
0
        }
695
0
        else if (flatten & FZ_TEXT_FLATTEN_KEEP_LINES)
696
0
        {
697
0
          EMIT(NULL, '\n');
698
0
          *ws = 1;
699
0
        }
700
0
        else if (line->next)
701
0
        {
702
          /* join lines in the same block with a space */
703
0
          if (!*ws)
704
0
            EMIT(NULL, ' ');
705
0
          *ws = 1;
706
0
        }
707
0
      }
708
709
0
      if (join_line)
710
0
      {
711
        /* No whitespace, no linebreak. */
712
0
      }
713
0
      else if (flatten & FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS)
714
0
      {
715
0
        EMIT(NULL, '\n');
716
0
        *ws = 1;
717
0
      }
718
0
      else
719
0
      {
720
        /* terminate all paragraphs with a space */
721
0
        if (!*ws)
722
0
          EMIT(NULL, ' ');
723
0
        *ws = 1;
724
0
      }
725
0
    }
726
0
    else if (block->type == FZ_STEXT_BLOCK_STRUCT && block->u.s.down)
727
0
    {
728
0
      n += do_flatten(ctx, buf, map, page, block->u.s.down, block->u.s.down->first_block, flatten, ws);
729
0
    }
730
0
  }
731
732
0
  return n;
733
0
}
734
735
fz_buffer *
736
fz_new_buffer_from_flattened_stext_page(fz_context *ctx, fz_stext_page *page, fz_text_flatten flatten, fz_stext_position **mapp)
737
0
{
738
0
  fz_stext_position *map = NULL;
739
0
  fz_buffer *buf = NULL;
740
0
  int ws, len;
741
742
0
  fz_var(map);
743
0
  fz_var(buf);
744
745
0
  if (mapp)
746
0
  {
747
0
    ws = 0;
748
0
    len = do_flatten(ctx, NULL, NULL, page, NULL, page->first_block, flatten, &ws);
749
0
  }
750
751
0
  fz_try(ctx)
752
0
  {
753
0
    buf = fz_new_buffer(ctx, 256);
754
0
    if (mapp)
755
0
      map = *mapp = fz_malloc_array(ctx, len, fz_stext_position);
756
0
    ws = 0;
757
0
    do_flatten(ctx, buf, &map, page, NULL, page->first_block, flatten, &ws);
758
0
  }
759
0
  fz_catch(ctx)
760
0
  {
761
0
    if (mapp)
762
0
      fz_free(ctx, *mapp);
763
0
    fz_drop_buffer(ctx, buf);
764
0
    fz_rethrow(ctx);
765
0
  }
766
767
0
  return buf;
768
0
}
769
770
fz_buffer *
771
fz_new_buffer_from_display_list(fz_context *ctx, fz_display_list *list, const fz_stext_options *options)
772
0
{
773
0
  return fz_new_buffer_from_flattened_display_list(ctx, list, options, FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS);
774
0
}
775
776
fz_buffer *
777
fz_new_buffer_from_flattened_display_list(fz_context *ctx, fz_display_list *list, const fz_stext_options *options, fz_text_flatten flatten)
778
0
{
779
0
  fz_stext_page *text;
780
0
  fz_buffer *buf = NULL;
781
782
0
  text = fz_new_stext_page_from_display_list(ctx, list, options);
783
0
  fz_try(ctx)
784
0
    buf = fz_new_buffer_from_flattened_stext_page(ctx, text, flatten, NULL);
785
0
  fz_always(ctx)
786
0
    fz_drop_stext_page(ctx, text);
787
0
  fz_catch(ctx)
788
0
    fz_rethrow(ctx);
789
0
  return buf;
790
0
}
791
792
fz_buffer *
793
fz_new_buffer_from_page(fz_context *ctx, fz_page *page, const fz_stext_options *options)
794
0
{
795
0
  return fz_new_buffer_from_flattened_page(ctx, page, options, FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS);
796
0
}
797
798
fz_buffer *
799
fz_new_buffer_from_flattened_page(fz_context *ctx, fz_page *page, const fz_stext_options *options, fz_text_flatten flatten)
800
0
{
801
0
  fz_stext_page *text;
802
0
  fz_buffer *buf = NULL;
803
804
0
  text = fz_new_stext_page_from_page(ctx, page, options);
805
0
  fz_try(ctx)
806
0
    buf = fz_new_buffer_from_flattened_stext_page(ctx, text, flatten, NULL);
807
0
  fz_always(ctx)
808
0
    fz_drop_stext_page(ctx, text);
809
0
  fz_catch(ctx)
810
0
    fz_rethrow(ctx);
811
0
  return buf;
812
0
}
813
814
fz_buffer *
815
fz_new_buffer_from_page_number(fz_context *ctx, fz_document *doc, int number, const fz_stext_options *options)
816
0
{
817
0
  return fz_new_buffer_from_flattened_page_number(ctx, doc, number, options, FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS);
818
0
}
819
820
fz_buffer *
821
fz_new_buffer_from_flattened_page_number(fz_context *ctx, fz_document *doc, int number, const fz_stext_options *options, fz_text_flatten flatten)
822
0
{
823
0
  fz_page *page;
824
0
  fz_buffer *buf = NULL;
825
826
0
  page = fz_load_page(ctx, doc, number);
827
0
  fz_try(ctx)
828
0
    buf = fz_new_buffer_from_flattened_page(ctx, page, options, flatten);
829
0
  fz_always(ctx)
830
0
    fz_drop_page(ctx, page);
831
0
  fz_catch(ctx)
832
0
    fz_rethrow(ctx);
833
0
  return buf;
834
0
}
835
836
void
837
fz_write_image_as_data_uri(fz_context *ctx, fz_output *out, fz_image *image)
838
0
{
839
0
  fz_compressed_buffer *cbuf;
840
0
  fz_buffer *buf;
841
842
0
  cbuf = fz_compressed_image_buffer(ctx, image);
843
844
0
  if (cbuf && cbuf->params.type == FZ_IMAGE_JPEG)
845
0
  {
846
0
    int type = fz_colorspace_type(ctx, image->colorspace);
847
0
    if (type == FZ_COLORSPACE_GRAY || type == FZ_COLORSPACE_RGB)
848
0
    {
849
0
      fz_write_string(ctx, out, "data:image/jpeg;base64,");
850
0
      fz_write_base64_buffer(ctx, out, cbuf->buffer, 1);
851
0
      return;
852
0
    }
853
0
  }
854
0
  if (cbuf && cbuf->params.type == FZ_IMAGE_PNG)
855
0
  {
856
0
    fz_write_string(ctx, out, "data:image/png;base64,");
857
0
    fz_write_base64_buffer(ctx, out, cbuf->buffer, 1);
858
0
    return;
859
0
  }
860
861
0
  buf = fz_new_buffer_from_image_as_png(ctx, image, fz_default_color_params);
862
0
  fz_try(ctx)
863
0
  {
864
0
    fz_write_string(ctx, out, "data:image/png;base64,");
865
0
    fz_write_base64_buffer(ctx, out, buf, 1);
866
0
  }
867
0
  fz_always(ctx)
868
0
    fz_drop_buffer(ctx, buf);
869
0
  fz_catch(ctx)
870
0
    fz_rethrow(ctx);
871
0
}
872
873
static uint32_t read16(const uint8_t *d, size_t *pos, size_t len, int order)
874
0
{
875
0
  size_t p = *pos;
876
0
  if (p+1 >= len)
877
0
    return *pos = len, 0;
878
0
  if (order)
879
0
    return *pos = p+2, fz_unpack_uint16(d+p);
880
0
  else
881
0
    return *pos = p+2, fz_unpack_uint16_le(d+p);
882
0
}
883
884
static uint32_t read32(const uint8_t *d, size_t *pos, size_t len, int order)
885
0
{
886
0
  size_t p = *pos;
887
0
  if (p+3 >= len)
888
0
    return *pos = len, 0;
889
0
  if (order)
890
0
    return *pos = p+4, fz_unpack_uint32(d+p);
891
0
  else
892
0
    return *pos = p+4, fz_unpack_uint32_le(d+p);
893
0
}
894
895
static void write16(uint8_t *d, size_t *pos, size_t len, int order, uint32_t v)
896
0
{
897
0
  size_t p = *pos;
898
0
  if (p+1 >= len)
899
0
  {
900
0
    *pos = len;
901
0
    return;
902
0
  }
903
0
  if (order)
904
0
    fz_pack_uint16(d+p, v);
905
0
  else
906
0
    fz_pack_uint16_le(d+p, v);
907
0
  *pos = p+2;
908
0
}
909
910
static void write32(uint8_t *d, size_t *pos, size_t len, int order, uint32_t v)
911
0
{
912
0
  size_t p = *pos;
913
0
  if (p+3 >= len)
914
0
  {
915
0
    *pos = len;
916
0
    return;
917
0
  }
918
0
  if (order)
919
0
    fz_pack_uint32(d+p, v);
920
0
  else
921
0
    fz_pack_uint32_le(d+p, v);
922
0
  *pos = p+4;
923
0
}
924
925
fz_buffer *
926
fz_sanitize_jpeg_buffer(fz_context *ctx, fz_buffer *in)
927
0
{
928
0
  fz_buffer *out = fz_clone_buffer(ctx, in);
929
0
  size_t len = out->len;
930
0
  size_t pos = 0;
931
0
  uint8_t *d = out->data;
932
933
  /* We need at least 4 data bytes. */
934
0
  while (pos+4 < len)
935
0
  {
936
0
    uint8_t m;
937
    /* We should be on a marker. If not, inch forwards until we are. */
938
0
    if (d[pos++] != 0xff)
939
0
      continue;
940
0
    m = d[pos++];
941
0
    if (m == 0xDA)
942
0
      break; /* Start Of Scan. All our rewriting happens before this. */
943
0
    if (m == 0xE1)
944
0
    {
945
0
      uint8_t order;
946
0
      uint32_t tmp;
947
0
      size_t body_start;
948
      /* APP1 tag. This is where the EXIF data lives. */
949
      /* Read and discard the marker length. We're not continuing after this anyway. */
950
0
      (void)read16(d, &pos, len, 0);
951
0
      tmp = read32(d, &pos, len, 0);
952
0
      if (tmp != 0x66697845) /* Exif */
953
0
        break; /* Not exif - nothing to rewrite. */
954
0
      tmp = read16(d, &pos, len, 0);
955
0
      if (tmp != 0) /* Terminator + Pad */
956
0
        break; /* Not exif - nothing to rewrite. */
957
      /* Now we're at the APP1 Body. */
958
0
      body_start = pos;
959
0
      tmp = read16(d, &pos, len, 0);
960
0
      if (tmp == 0x4949)
961
0
        order = 0; /* LE */
962
0
      else if (tmp == 0x4d4d)
963
0
        order = 1; /* BE */
964
0
      else
965
0
        break; /* Bad TIFF type. Bale. */
966
0
      tmp = read16(d, &pos, len, order);
967
0
      if (tmp != 0x002a) /* 42 */
968
0
        break; /* Bad version field.  Bale. */
969
0
      do
970
0
      {
971
0
        uint32_t i, n;
972
0
        tmp = read32(d, &pos, len, order);
973
0
        pos = body_start + tmp;
974
0
        if (tmp == 0 || pos >= len)
975
0
          break;
976
0
        n = read16(d, &pos, len, order);
977
0
        for (i = 0; i < n; i++)
978
0
        {
979
0
          if (read16(d, &pos, len, order) == 0x112)
980
0
          {
981
            /* Orientation tag! */
982
0
            write16(d, &pos, len, order, 3); /* 3 = short */
983
0
            write32(d, &pos, len, order, 1); /* Count = 1 */
984
0
            write16(d, &pos, len, order, 1); /* Value = 1 */
985
0
            write16(d, &pos, len, order, 0); /* padding */
986
0
            i = n;
987
0
            pos = len; /* Done! */
988
0
          }
989
0
          else
990
0
            pos += 10;
991
0
        }
992
0
      }
993
0
      while (pos+4 < len);
994
0
      break;
995
0
    }
996
0
    else if (m >= 0xD0 && m <= 0xD7)
997
0
    {
998
      /* RSTm - no length code. But we shouldn't hit this! */
999
0
    }
1000
0
    else if (m == 0x01)
1001
0
    {
1002
      /* TEM - temporary private use in arithmetic coding - shouldn't hit this either. */
1003
0
    }
1004
0
    else if (m == 0xD8)
1005
0
    {
1006
      /* SOI - start of image. */
1007
0
    }
1008
0
    else if (m == 0x01)
1009
0
    {
1010
      /* EOI - end of image. */
1011
0
    }
1012
0
    else
1013
0
    {
1014
      /* All other markers have a length. */
1015
0
      size_t marker_len = d[pos]*256 + d[pos+1];
1016
0
      pos += marker_len; /* The 2 length bytes are included in the marker_len */
1017
0
    }
1018
0
  }
1019
1020
0
  return out;
1021
0
}
1022
1023
void
1024
fz_append_image_as_data_uri(fz_context *ctx, fz_buffer *out, fz_image *image)
1025
0
{
1026
0
  fz_compressed_buffer *cbuf;
1027
0
  fz_buffer *buf;
1028
0
  const char *mime;
1029
1030
0
  cbuf = fz_compressed_image_buffer(ctx, image);
1031
1032
0
  if (cbuf && cbuf->params.type == FZ_IMAGE_JPEG)
1033
0
  {
1034
0
    int type = fz_colorspace_type(ctx, image->colorspace);
1035
0
    if (type == FZ_COLORSPACE_GRAY || type == FZ_COLORSPACE_RGB)
1036
0
    {
1037
0
      fz_buffer *new_buf = fz_sanitize_jpeg_buffer(ctx, cbuf->buffer);
1038
0
      fz_append_string(ctx, out, "data:image/jpeg;base64,");
1039
0
      fz_try(ctx)
1040
0
        fz_append_base64_buffer(ctx, out, new_buf, 1);
1041
0
      fz_always(ctx)
1042
0
        fz_drop_buffer(ctx, new_buf);
1043
0
      fz_catch(ctx)
1044
0
        fz_rethrow(ctx);
1045
0
      return;
1046
0
    }
1047
0
  }
1048
1049
0
  if (cbuf && cbuf->params.type == FZ_IMAGE_PNG)
1050
0
  {
1051
0
    fz_append_string(ctx, out, "data:image/png;base64,");
1052
0
    fz_append_base64_buffer(ctx, out, cbuf->buffer, 1);
1053
0
    return;
1054
0
  }
1055
1056
0
  if (fz_is_lossy_image(ctx, image))
1057
0
  {
1058
    /* Convert lossy image formats to JPEG */
1059
0
    buf = fz_new_buffer_from_image_as_jpeg(ctx, image, fz_default_color_params, 90, 0);
1060
0
    mime = "data:image/jpeg;base64,";
1061
0
  }
1062
0
  else
1063
0
  {
1064
0
    buf = fz_new_buffer_from_image_as_png(ctx, image, fz_default_color_params);
1065
0
    mime = "data:image/png;base64,";
1066
0
  }
1067
1068
0
  fz_try(ctx)
1069
0
  {
1070
0
    fz_append_string(ctx, out, mime);
1071
0
    fz_append_base64_buffer(ctx, out, buf, 1);
1072
0
  }
1073
0
  fz_always(ctx)
1074
0
    fz_drop_buffer(ctx, buf);
1075
0
  fz_catch(ctx)
1076
0
    fz_rethrow(ctx);
1077
0
}
1078
1079
void
1080
fz_write_pixmap_as_data_uri(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
1081
0
{
1082
0
  fz_buffer *buf = fz_new_buffer_from_pixmap_as_png(ctx, pixmap, fz_default_color_params);
1083
0
  fz_try(ctx)
1084
0
  {
1085
0
    fz_write_string(ctx, out, "data:image/png;base64,");
1086
0
    fz_write_base64_buffer(ctx, out, buf, 1);
1087
0
  }
1088
0
  fz_always(ctx)
1089
0
    fz_drop_buffer(ctx, buf);
1090
0
  fz_catch(ctx)
1091
0
    fz_rethrow(ctx);
1092
0
}
1093
1094
void
1095
fz_append_pixmap_as_data_uri(fz_context *ctx, fz_buffer *out, fz_pixmap *pixmap)
1096
0
{
1097
0
  fz_buffer *buf = fz_new_buffer_from_pixmap_as_png(ctx, pixmap, fz_default_color_params);
1098
0
  fz_try(ctx)
1099
0
  {
1100
0
    fz_append_string(ctx, out, "data:image/png;base64,");
1101
0
    fz_append_base64_buffer(ctx, out, buf, 1);
1102
0
  }
1103
0
  fz_always(ctx)
1104
0
    fz_drop_buffer(ctx, buf);
1105
0
  fz_catch(ctx)
1106
0
    fz_rethrow(ctx);
1107
0
}
1108
1109
fz_document *
1110
fz_new_xhtml_document_from_document(fz_context *ctx, fz_document *old_doc, const fz_stext_options *opts)
1111
0
{
1112
0
  fz_stext_options default_opts = { FZ_STEXT_PRESERVE_IMAGES | FZ_STEXT_DEHYPHENATE };
1113
0
  fz_document *new_doc;
1114
0
  fz_buffer *buf = NULL;
1115
0
  fz_output *out = NULL;
1116
0
  fz_stream *stm = NULL;
1117
0
  fz_stext_page *text = NULL;
1118
0
  int i;
1119
1120
0
  fz_var(buf);
1121
0
  fz_var(out);
1122
0
  fz_var(stm);
1123
0
  fz_var(text);
1124
1125
0
  if (!opts)
1126
0
    opts = &default_opts;
1127
1128
0
  fz_try(ctx)
1129
0
  {
1130
0
    buf = fz_new_buffer(ctx, 8192);
1131
0
    out = fz_new_output_with_buffer(ctx, buf);
1132
0
    fz_print_stext_header_as_xhtml(ctx, out);
1133
1134
0
    for (i = 0; i < fz_count_pages(ctx, old_doc); ++i)
1135
0
    {
1136
0
      text = fz_new_stext_page_from_page_number(ctx, old_doc, i, opts);
1137
0
      fz_print_stext_page_as_xhtml(ctx, out, text, i+1);
1138
0
      fz_drop_stext_page(ctx, text);
1139
0
      text = NULL;
1140
0
    }
1141
1142
0
    fz_print_stext_trailer_as_xhtml(ctx, out);
1143
0
    fz_close_output(ctx, out);
1144
0
    fz_terminate_buffer(ctx, buf);
1145
1146
0
    stm = fz_open_buffer(ctx, buf);
1147
0
    new_doc = fz_open_document_with_stream(ctx, "application/xhtml+xml", stm);
1148
0
  }
1149
0
  fz_always(ctx)
1150
0
  {
1151
0
    fz_drop_stream(ctx, stm);
1152
0
    fz_drop_buffer(ctx, buf);
1153
0
    fz_drop_output(ctx, out);
1154
0
    fz_drop_stext_page(ctx, text);
1155
0
  }
1156
0
  fz_catch(ctx)
1157
0
    fz_rethrow(ctx);
1158
1159
0
  return new_doc;
1160
0
}
1161
1162
fz_buffer *
1163
fz_new_buffer_from_page_with_format(fz_context *ctx, fz_page *page, const char *format, const char *options, fz_matrix transform, fz_cookie *cookie)
1164
0
{
1165
0
  fz_buffer *buf = NULL;
1166
0
  fz_output *out;
1167
0
  fz_document_writer *writer = NULL;
1168
0
  fz_device *dev = NULL;
1169
1170
0
  fz_var(buf);
1171
0
  fz_var(writer);
1172
0
  fz_var(dev);
1173
1174
0
  fz_try(ctx)
1175
0
  {
1176
0
    buf = fz_new_buffer(ctx, 0);
1177
0
    out = fz_new_output_with_buffer(ctx, buf);
1178
0
    writer = fz_new_document_writer_with_output(ctx, out, format, options);
1179
0
    dev = fz_begin_page(ctx, writer, fz_bound_page(ctx, page));
1180
0
    fz_run_page(ctx, page, dev, transform, cookie);
1181
0
    fz_end_page(ctx, writer);
1182
0
    fz_close_document_writer(ctx, writer);
1183
0
  }
1184
0
  fz_always(ctx)
1185
0
    fz_drop_document_writer(ctx, writer);
1186
0
  fz_catch(ctx)
1187
0
  {
1188
0
    fz_drop_buffer(ctx, buf);
1189
0
    fz_rethrow(ctx);
1190
0
  }
1191
0
  return buf;
1192
0
}