Coverage Report

Created: 2026-03-31 07:17

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
3
{
208
3
  fz_rect rect;
209
3
  fz_irect bbox;
210
3
  fz_pixmap *pix;
211
3
  fz_device *dev = NULL;
212
213
3
  fz_var(dev);
214
215
3
  rect = fz_bound_page(ctx, page);
216
3
  rect = fz_transform_rect(rect, ctm);
217
3
  bbox = fz_round_rect(rect);
218
219
3
  pix = fz_new_pixmap_with_bbox(ctx, cs, bbox, seps, alpha);
220
221
6
  fz_try(ctx)
222
6
  {
223
3
    if (alpha)
224
0
      fz_clear_pixmap(ctx, pix);
225
3
    else
226
3
      fz_clear_pixmap_with_value(ctx, pix, 0xFF);
227
228
3
    dev = fz_new_draw_device(ctx, ctm, pix);
229
3
    fz_run_page(ctx, page, dev, fz_identity, NULL);
230
3
    fz_close_device(ctx, dev);
231
3
  }
232
6
  fz_always(ctx)
233
3
  {
234
3
    fz_drop_device(ctx, dev);
235
3
  }
236
3
  fz_catch(ctx)
237
2
  {
238
2
    fz_drop_pixmap(ctx, pix);
239
2
    fz_rethrow(ctx);
240
2
  }
241
242
1
  return pix;
243
3
}
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
3
{
248
3
  return fz_new_pixmap_from_page_number_with_separations(ctx, doc, number, ctm, cs, NULL, alpha);
249
3
}
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
3
{
254
3
  fz_page *page;
255
3
  fz_pixmap *pix = NULL;
256
257
3
  page = fz_load_page(ctx, doc, number);
258
6
  fz_try(ctx)
259
6
    pix = fz_new_pixmap_from_page_with_separations(ctx, page, ctm, cs, seps, alpha);
260
6
  fz_always(ctx)
261
3
    fz_drop_page(ctx, page);
262
3
  fz_catch(ctx)
263
2
    fz_rethrow(ctx);
264
1
  return pix;
265
3
}
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, ch }; \
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
701
0
        {
702
0
          if (!*ws)
703
0
            EMIT(NULL, ' ');
704
0
          *ws = 1;
705
0
        }
706
0
      }
707
708
0
      if (flatten & FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS)
709
0
      {
710
0
        EMIT(NULL, '\n');
711
0
        *ws = 1;
712
0
      }
713
0
      else if (!join_line)
714
0
      {
715
0
        EMIT(NULL, '\n');
716
0
        *ws = 1;
717
0
      }
718
0
    }
719
0
    else if (block->type == FZ_STEXT_BLOCK_STRUCT && block->u.s.down)
720
0
    {
721
0
      n += do_flatten(ctx, buf, map, page, block->u.s.down, block->u.s.down->first_block, flatten, ws);
722
0
    }
723
0
  }
724
725
0
  return n;
726
0
}
727
728
fz_buffer *
729
fz_new_buffer_from_flattened_stext_page(fz_context *ctx, fz_stext_page *page, fz_text_flatten flatten, fz_stext_position **mapp)
730
0
{
731
0
  fz_stext_position *map = NULL;
732
0
  fz_buffer *buf = NULL;
733
0
  int ws, len;
734
735
0
  fz_var(map);
736
0
  fz_var(buf);
737
738
0
  if (mapp)
739
0
  {
740
0
    ws = 0;
741
0
    len = do_flatten(ctx, NULL, NULL, page, NULL, page->first_block, flatten, &ws);
742
0
  }
743
744
0
  fz_try(ctx)
745
0
  {
746
0
    buf = fz_new_buffer(ctx, 256);
747
0
    if (mapp)
748
0
      map = *mapp = fz_malloc_array(ctx, len, fz_stext_position);
749
0
    ws = 0;
750
0
    do_flatten(ctx, buf, &map, page, NULL, page->first_block, flatten, &ws);
751
0
  }
752
0
  fz_catch(ctx)
753
0
  {
754
0
    if (mapp)
755
0
      fz_free(ctx, *mapp);
756
0
    fz_drop_buffer(ctx, buf);
757
0
    fz_rethrow(ctx);
758
0
  }
759
760
0
  return buf;
761
0
}
762
763
fz_buffer *
764
fz_new_buffer_from_display_list(fz_context *ctx, fz_display_list *list, const fz_stext_options *options)
765
0
{
766
0
  return fz_new_buffer_from_flattened_display_list(ctx, list, options, FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS);
767
0
}
768
769
fz_buffer *
770
fz_new_buffer_from_flattened_display_list(fz_context *ctx, fz_display_list *list, const fz_stext_options *options, fz_text_flatten flatten)
771
0
{
772
0
  fz_stext_page *text;
773
0
  fz_buffer *buf = NULL;
774
775
0
  text = fz_new_stext_page_from_display_list(ctx, list, options);
776
0
  fz_try(ctx)
777
0
    buf = fz_new_buffer_from_flattened_stext_page(ctx, text, flatten, NULL);
778
0
  fz_always(ctx)
779
0
    fz_drop_stext_page(ctx, text);
780
0
  fz_catch(ctx)
781
0
    fz_rethrow(ctx);
782
0
  return buf;
783
0
}
784
785
fz_buffer *
786
fz_new_buffer_from_page(fz_context *ctx, fz_page *page, const fz_stext_options *options)
787
0
{
788
0
  return fz_new_buffer_from_flattened_page(ctx, page, options, FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS);
789
0
}
790
791
fz_buffer *
792
fz_new_buffer_from_flattened_page(fz_context *ctx, fz_page *page, const fz_stext_options *options, fz_text_flatten flatten)
793
0
{
794
0
  fz_stext_page *text;
795
0
  fz_buffer *buf = NULL;
796
797
0
  text = fz_new_stext_page_from_page(ctx, page, options);
798
0
  fz_try(ctx)
799
0
    buf = fz_new_buffer_from_flattened_stext_page(ctx, text, flatten, NULL);
800
0
  fz_always(ctx)
801
0
    fz_drop_stext_page(ctx, text);
802
0
  fz_catch(ctx)
803
0
    fz_rethrow(ctx);
804
0
  return buf;
805
0
}
806
807
fz_buffer *
808
fz_new_buffer_from_page_number(fz_context *ctx, fz_document *doc, int number, const fz_stext_options *options)
809
0
{
810
0
  return fz_new_buffer_from_flattened_page_number(ctx, doc, number, options, FZ_TEXT_FLATTEN_KEEP_PARAGRAPHS);
811
0
}
812
813
fz_buffer *
814
fz_new_buffer_from_flattened_page_number(fz_context *ctx, fz_document *doc, int number, const fz_stext_options *options, fz_text_flatten flatten)
815
0
{
816
0
  fz_page *page;
817
0
  fz_buffer *buf = NULL;
818
819
0
  page = fz_load_page(ctx, doc, number);
820
0
  fz_try(ctx)
821
0
    buf = fz_new_buffer_from_flattened_page(ctx, page, options, flatten);
822
0
  fz_always(ctx)
823
0
    fz_drop_page(ctx, page);
824
0
  fz_catch(ctx)
825
0
    fz_rethrow(ctx);
826
0
  return buf;
827
0
}
828
829
void
830
fz_write_image_as_data_uri(fz_context *ctx, fz_output *out, fz_image *image)
831
0
{
832
0
  fz_compressed_buffer *cbuf;
833
0
  fz_buffer *buf;
834
835
0
  cbuf = fz_compressed_image_buffer(ctx, image);
836
837
0
  if (cbuf && cbuf->params.type == FZ_IMAGE_JPEG)
838
0
  {
839
0
    int type = fz_colorspace_type(ctx, image->colorspace);
840
0
    if (type == FZ_COLORSPACE_GRAY || type == FZ_COLORSPACE_RGB)
841
0
    {
842
0
      fz_write_string(ctx, out, "data:image/jpeg;base64,");
843
0
      fz_write_base64_buffer(ctx, out, cbuf->buffer, 1);
844
0
      return;
845
0
    }
846
0
  }
847
0
  if (cbuf && cbuf->params.type == FZ_IMAGE_PNG)
848
0
  {
849
0
    fz_write_string(ctx, out, "data:image/png;base64,");
850
0
    fz_write_base64_buffer(ctx, out, cbuf->buffer, 1);
851
0
    return;
852
0
  }
853
854
0
  buf = fz_new_buffer_from_image_as_png(ctx, image, fz_default_color_params);
855
0
  fz_try(ctx)
856
0
  {
857
0
    fz_write_string(ctx, out, "data:image/png;base64,");
858
0
    fz_write_base64_buffer(ctx, out, buf, 1);
859
0
  }
860
0
  fz_always(ctx)
861
0
    fz_drop_buffer(ctx, buf);
862
0
  fz_catch(ctx)
863
0
    fz_rethrow(ctx);
864
0
}
865
866
static uint32_t read16(const uint8_t *d, size_t *pos, size_t len, int order)
867
0
{
868
0
  size_t p = *pos;
869
0
  if (p+1 >= len)
870
0
    return *pos = len, 0;
871
0
  if (order)
872
0
    return *pos = p+2, fz_unpack_uint16(d+p);
873
0
  else
874
0
    return *pos = p+2, fz_unpack_uint16_le(d+p);
875
0
}
876
877
static uint32_t read32(const uint8_t *d, size_t *pos, size_t len, int order)
878
0
{
879
0
  size_t p = *pos;
880
0
  if (p+3 >= len)
881
0
    return *pos = len, 0;
882
0
  if (order)
883
0
    return *pos = p+4, fz_unpack_uint32(d+p);
884
0
  else
885
0
    return *pos = p+4, fz_unpack_uint32_le(d+p);
886
0
}
887
888
static void write16(uint8_t *d, size_t *pos, size_t len, int order, uint32_t v)
889
0
{
890
0
  size_t p = *pos;
891
0
  if (p+1 >= len)
892
0
  {
893
0
    *pos = len;
894
0
    return;
895
0
  }
896
0
  if (order)
897
0
    fz_pack_uint16(d+p, v);
898
0
  else
899
0
    fz_pack_uint16_le(d+p, v);
900
0
  *pos = p+2;
901
0
}
902
903
static void write32(uint8_t *d, size_t *pos, size_t len, int order, uint32_t v)
904
0
{
905
0
  size_t p = *pos;
906
0
  if (p+3 >= len)
907
0
  {
908
0
    *pos = len;
909
0
    return;
910
0
  }
911
0
  if (order)
912
0
    fz_pack_uint32(d+p, v);
913
0
  else
914
0
    fz_pack_uint32_le(d+p, v);
915
0
  *pos = p+4;
916
0
}
917
918
fz_buffer *
919
fz_sanitize_jpeg_buffer(fz_context *ctx, fz_buffer *in)
920
0
{
921
0
  fz_buffer *out = fz_clone_buffer(ctx, in);
922
0
  size_t len = out->len;
923
0
  size_t pos = 0;
924
0
  uint8_t *d = out->data;
925
926
  /* We need at least 4 data bytes. */
927
0
  while (pos+4 < len)
928
0
  {
929
0
    uint8_t m;
930
    /* We should be on a marker. If not, inch forwards until we are. */
931
0
    if (d[pos++] != 0xff)
932
0
      continue;
933
0
    m = d[pos++];
934
0
    if (m == 0xDA)
935
0
      break; /* Start Of Scan. All our rewriting happens before this. */
936
0
    if (m == 0xE1)
937
0
    {
938
0
      uint8_t order;
939
0
      uint32_t tmp;
940
0
      size_t body_start;
941
      /* APP1 tag. This is where the EXIF data lives. */
942
      /* Read and discard the marker length. We're not continuing after this anyway. */
943
0
      (void)read16(d, &pos, len, 0);
944
0
      tmp = read32(d, &pos, len, 0);
945
0
      if (tmp != 0x66697845) /* Exif */
946
0
        break; /* Not exif - nothing to rewrite. */
947
0
      tmp = read16(d, &pos, len, 0);
948
0
      if (tmp != 0) /* Terminator + Pad */
949
0
        break; /* Not exif - nothing to rewrite. */
950
      /* Now we're at the APP1 Body. */
951
0
      body_start = pos;
952
0
      tmp = read16(d, &pos, len, 0);
953
0
      if (tmp == 0x4949)
954
0
        order = 0; /* LE */
955
0
      else if (tmp == 0x4d4d)
956
0
        order = 1; /* BE */
957
0
      else
958
0
        break; /* Bad TIFF type. Bale. */
959
0
      tmp = read16(d, &pos, len, order);
960
0
      if (tmp != 0x002a) /* 42 */
961
0
        break; /* Bad version field.  Bale. */
962
0
      do
963
0
      {
964
0
        uint32_t i, n;
965
0
        tmp = read32(d, &pos, len, order);
966
0
        pos = body_start + tmp;
967
0
        if (tmp == 0 || pos >= len)
968
0
          break;
969
0
        n = read16(d, &pos, len, order);
970
0
        for (i = 0; i < n; i++)
971
0
        {
972
0
          if (read16(d, &pos, len, order) == 0x112)
973
0
          {
974
            /* Orientation tag! */
975
0
            write16(d, &pos, len, order, 3); /* 3 = short */
976
0
            write32(d, &pos, len, order, 1); /* Count = 1 */
977
0
            write16(d, &pos, len, order, 1); /* Value = 1 */
978
0
            write16(d, &pos, len, order, 0); /* padding */
979
0
            i = n;
980
0
            pos = len; /* Done! */
981
0
          }
982
0
          else
983
0
            pos += 10;
984
0
        }
985
0
      }
986
0
      while (pos+4 < len);
987
0
      break;
988
0
    }
989
0
    else if (m >= 0xD0 && m <= 0xD7)
990
0
    {
991
      /* RSTm - no length code. But we shouldn't hit this! */
992
0
    }
993
0
    else if (m == 0x01)
994
0
    {
995
      /* TEM - temporary private use in arithmetic coding - shouldn't hit this either. */
996
0
    }
997
0
    else if (m == 0xD8)
998
0
    {
999
      /* SOI - start of image. */
1000
0
    }
1001
0
    else if (m == 0x01)
1002
0
    {
1003
      /* EOI - end of image. */
1004
0
    }
1005
0
    else
1006
0
    {
1007
      /* All other markers have a length. */
1008
0
      size_t marker_len = d[pos]*256 + d[pos+1];
1009
0
      pos += marker_len; /* The 2 length bytes are included in the marker_len */
1010
0
    }
1011
0
  }
1012
1013
0
  return out;
1014
0
}
1015
1016
void
1017
fz_append_image_as_data_uri(fz_context *ctx, fz_buffer *out, fz_image *image)
1018
0
{
1019
0
  fz_compressed_buffer *cbuf;
1020
0
  fz_buffer *buf;
1021
0
  const char *mime;
1022
1023
0
  cbuf = fz_compressed_image_buffer(ctx, image);
1024
1025
0
  if (cbuf && cbuf->params.type == FZ_IMAGE_JPEG)
1026
0
  {
1027
0
    int type = fz_colorspace_type(ctx, image->colorspace);
1028
0
    if (type == FZ_COLORSPACE_GRAY || type == FZ_COLORSPACE_RGB)
1029
0
    {
1030
0
      fz_buffer *new_buf = fz_sanitize_jpeg_buffer(ctx, cbuf->buffer);
1031
0
      fz_append_string(ctx, out, "data:image/jpeg;base64,");
1032
0
      fz_try(ctx)
1033
0
        fz_append_base64_buffer(ctx, out, new_buf, 1);
1034
0
      fz_always(ctx)
1035
0
        fz_drop_buffer(ctx, new_buf);
1036
0
      fz_catch(ctx)
1037
0
        fz_rethrow(ctx);
1038
0
      return;
1039
0
    }
1040
0
  }
1041
1042
0
  if (cbuf && cbuf->params.type == FZ_IMAGE_PNG)
1043
0
  {
1044
0
    fz_append_string(ctx, out, "data:image/png;base64,");
1045
0
    fz_append_base64_buffer(ctx, out, cbuf->buffer, 1);
1046
0
    return;
1047
0
  }
1048
1049
0
  if (fz_is_lossy_image(ctx, image))
1050
0
  {
1051
    /* Convert lossy image formats to JPEG */
1052
0
    buf = fz_new_buffer_from_image_as_jpeg(ctx, image, fz_default_color_params, 90, 0);
1053
0
    mime = "data:image/jpeg;base64,";
1054
0
  }
1055
0
  else
1056
0
  {
1057
0
    buf = fz_new_buffer_from_image_as_png(ctx, image, fz_default_color_params);
1058
0
    mime = "data:image/png;base64,";
1059
0
  }
1060
1061
0
  fz_try(ctx)
1062
0
  {
1063
0
    fz_append_string(ctx, out, mime);
1064
0
    fz_append_base64_buffer(ctx, out, buf, 1);
1065
0
  }
1066
0
  fz_always(ctx)
1067
0
    fz_drop_buffer(ctx, buf);
1068
0
  fz_catch(ctx)
1069
0
    fz_rethrow(ctx);
1070
0
}
1071
1072
void
1073
fz_write_pixmap_as_data_uri(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
1074
0
{
1075
0
  fz_buffer *buf = fz_new_buffer_from_pixmap_as_png(ctx, pixmap, fz_default_color_params);
1076
0
  fz_try(ctx)
1077
0
  {
1078
0
    fz_write_string(ctx, out, "data:image/png;base64,");
1079
0
    fz_write_base64_buffer(ctx, out, buf, 1);
1080
0
  }
1081
0
  fz_always(ctx)
1082
0
    fz_drop_buffer(ctx, buf);
1083
0
  fz_catch(ctx)
1084
0
    fz_rethrow(ctx);
1085
0
}
1086
1087
void
1088
fz_append_pixmap_as_data_uri(fz_context *ctx, fz_buffer *out, fz_pixmap *pixmap)
1089
0
{
1090
0
  fz_buffer *buf = fz_new_buffer_from_pixmap_as_png(ctx, pixmap, fz_default_color_params);
1091
0
  fz_try(ctx)
1092
0
  {
1093
0
    fz_append_string(ctx, out, "data:image/png;base64,");
1094
0
    fz_append_base64_buffer(ctx, out, buf, 1);
1095
0
  }
1096
0
  fz_always(ctx)
1097
0
    fz_drop_buffer(ctx, buf);
1098
0
  fz_catch(ctx)
1099
0
    fz_rethrow(ctx);
1100
0
}
1101
1102
fz_document *
1103
fz_new_xhtml_document_from_document(fz_context *ctx, fz_document *old_doc, const fz_stext_options *opts)
1104
0
{
1105
0
  fz_stext_options default_opts = { FZ_STEXT_PRESERVE_IMAGES | FZ_STEXT_DEHYPHENATE };
1106
0
  fz_document *new_doc;
1107
0
  fz_buffer *buf = NULL;
1108
0
  fz_output *out = NULL;
1109
0
  fz_stream *stm = NULL;
1110
0
  fz_stext_page *text = NULL;
1111
0
  int i;
1112
1113
0
  fz_var(buf);
1114
0
  fz_var(out);
1115
0
  fz_var(stm);
1116
0
  fz_var(text);
1117
1118
0
  if (!opts)
1119
0
    opts = &default_opts;
1120
1121
0
  fz_try(ctx)
1122
0
  {
1123
0
    buf = fz_new_buffer(ctx, 8192);
1124
0
    out = fz_new_output_with_buffer(ctx, buf);
1125
0
    fz_print_stext_header_as_xhtml(ctx, out);
1126
1127
0
    for (i = 0; i < fz_count_pages(ctx, old_doc); ++i)
1128
0
    {
1129
0
      text = fz_new_stext_page_from_page_number(ctx, old_doc, i, opts);
1130
0
      fz_print_stext_page_as_xhtml(ctx, out, text, i+1);
1131
0
      fz_drop_stext_page(ctx, text);
1132
0
      text = NULL;
1133
0
    }
1134
1135
0
    fz_print_stext_trailer_as_xhtml(ctx, out);
1136
0
    fz_close_output(ctx, out);
1137
0
    fz_terminate_buffer(ctx, buf);
1138
1139
0
    stm = fz_open_buffer(ctx, buf);
1140
0
    new_doc = fz_open_document_with_stream(ctx, "application/xhtml+xml", stm);
1141
0
  }
1142
0
  fz_always(ctx)
1143
0
  {
1144
0
    fz_drop_stream(ctx, stm);
1145
0
    fz_drop_buffer(ctx, buf);
1146
0
    fz_drop_output(ctx, out);
1147
0
    fz_drop_stext_page(ctx, text);
1148
0
  }
1149
0
  fz_catch(ctx)
1150
0
    fz_rethrow(ctx);
1151
1152
0
  return new_doc;
1153
0
}
1154
1155
fz_buffer *
1156
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)
1157
0
{
1158
0
  fz_buffer *buf = NULL;
1159
0
  fz_output *out;
1160
0
  fz_document_writer *writer = NULL;
1161
0
  fz_device *dev = NULL;
1162
1163
0
  fz_var(buf);
1164
0
  fz_var(writer);
1165
0
  fz_var(dev);
1166
1167
0
  fz_try(ctx)
1168
0
  {
1169
0
    buf = fz_new_buffer(ctx, 0);
1170
0
    out = fz_new_output_with_buffer(ctx, buf);
1171
0
    writer = fz_new_document_writer_with_output(ctx, out, format, options);
1172
0
    dev = fz_begin_page(ctx, writer, fz_bound_page(ctx, page));
1173
0
    fz_run_page(ctx, page, dev, transform, cookie);
1174
0
    fz_end_page(ctx, writer);
1175
0
    fz_close_document_writer(ctx, writer);
1176
0
  }
1177
0
  fz_always(ctx)
1178
0
    fz_drop_document_writer(ctx, writer);
1179
0
  fz_catch(ctx)
1180
0
  {
1181
0
    fz_drop_buffer(ctx, buf);
1182
0
    fz_rethrow(ctx);
1183
0
  }
1184
0
  return buf;
1185
0
}