Coverage Report

Created: 2023-06-07 06:20

/src/mupdf/source/html/epub-doc.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
#include "html-imp.h"
25
26
#include <string.h>
27
#include <math.h>
28
29
#include <zlib.h> /* for crc32 */
30
31
enum { T, R, B, L };
32
33
typedef struct epub_chapter epub_chapter;
34
typedef struct epub_page epub_page;
35
36
typedef struct
37
{
38
  int max_chapters;
39
  int num_chapters;
40
  float layout_w;
41
  float layout_h;
42
  float layout_em;
43
  uint32_t css_sum;
44
  int use_doc_css;
45
  int *pages_in_chapter;
46
} epub_accelerator;
47
48
typedef struct
49
{
50
  fz_document super;
51
  fz_archive *zip;
52
  fz_html_font_set *set;
53
  int count;
54
  epub_chapter *spine;
55
  fz_outline *outline;
56
  char *dc_title, *dc_creator;
57
  float layout_w, layout_h, layout_em;
58
  epub_accelerator *accel;
59
  uint32_t css_sum;
60
61
  /* A common pattern of use is for us to open a document,
62
   * load a page, draw it, drop it, load the next page,
63
   * draw it, drop it etc. This means that the HTML for
64
   * a chapter might get thrown away between the drop and
65
   * the the next load (if the chapter is large, and the
66
   * store size is low). Accordingly, we store a handle
67
   * to the most recently used html block here, thus
68
   * ensuring that the stored copy won't be evicted. */
69
  fz_html *most_recent_html;
70
} epub_document;
71
72
struct epub_chapter
73
{
74
  epub_document *doc;
75
  char *path;
76
  int number;
77
  epub_chapter *next;
78
};
79
80
struct epub_page
81
{
82
  fz_page super;
83
  epub_chapter *ch;
84
  int number;
85
  fz_html *html;
86
};
87
88
static uint32_t
89
user_css_sum(fz_context *ctx)
90
0
{
91
0
  uint32_t sum = 0;
92
0
  const char *css = fz_user_css(ctx);
93
0
  sum = crc32(0, NULL, 0);
94
0
  if (css)
95
0
    sum = crc32(sum, (Byte*)css, (int)strlen(css));
96
0
  return sum;
97
0
}
98
99
static int dummy = 1;
100
101
struct encrypted {
102
  fz_archive super;
103
  fz_archive *chain;
104
  fz_tree *info;
105
};
106
107
static int has_encrypted_entry(fz_context *ctx, fz_archive *arch_, const char *name)
108
0
{
109
0
  struct encrypted *arch = (struct encrypted *)arch_;
110
0
  return fz_has_archive_entry(ctx, arch->chain, name);
111
0
}
112
113
static fz_stream *open_encrypted_entry(fz_context *ctx, fz_archive *arch_, const char *name)
114
0
{
115
0
  struct encrypted *arch = (struct encrypted *)arch_;
116
0
  if (fz_tree_lookup(ctx, arch->info, name))
117
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open encrypted data");
118
0
  return fz_open_archive_entry(ctx, arch->chain, name);
119
0
}
120
121
static fz_buffer *read_encrypted_entry(fz_context *ctx, fz_archive *arch_, const char *name)
122
0
{
123
0
  struct encrypted *arch = (struct encrypted *)arch_;
124
0
  if (fz_tree_lookup(ctx, arch->info, name))
125
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "cannot read encrypted data");
126
0
  return fz_read_archive_entry(ctx, arch->chain, name);
127
0
}
128
129
static void drop_encrypted_archive(fz_context *ctx, fz_archive *arch_)
130
0
{
131
0
  struct encrypted *arch = (struct encrypted *)arch_;
132
0
  fz_drop_tree(ctx, arch->info, NULL);
133
0
  fz_drop_archive(ctx, arch->chain);
134
0
}
135
136
static fz_archive *new_encrypted_archive(fz_context *ctx, fz_archive *chain, fz_tree *info)
137
0
{
138
0
  struct encrypted *arch;
139
140
0
  arch = fz_new_derived_archive(ctx, NULL, struct encrypted);
141
0
  arch->super.format = "encrypted";
142
0
  arch->super.has_entry = has_encrypted_entry;
143
0
  arch->super.read_entry = read_encrypted_entry;
144
0
  arch->super.open_entry = open_encrypted_entry;
145
0
  arch->super.drop_archive = drop_encrypted_archive;
146
0
  arch->chain = chain;
147
0
  arch->info = info;
148
149
0
  return &arch->super;
150
0
}
151
152
static void
153
epub_parse_encryption(fz_context *ctx, epub_document *doc, fz_xml *root)
154
0
{
155
0
  fz_tree *info = NULL;
156
0
  fz_xml *edata;
157
158
0
  for (edata = fz_xml_find_down(root, "EncryptedData"); edata; edata = fz_xml_find_next(edata, "EncryptedData"))
159
0
  {
160
0
    fz_xml *cdata = fz_xml_find_down(edata, "CipherData");
161
0
    fz_xml *cref = fz_xml_find_down(cdata, "CipherReference");
162
0
    char *uri = fz_xml_att(cref, "URI");
163
0
    if (uri)
164
0
    {
165
      // TODO: Support reading EncryptedKey and EncryptionMethod to decrypt content.
166
0
      info = fz_tree_insert(ctx, info, uri, &dummy);
167
0
    }
168
0
  }
169
170
0
  if (info)
171
0
  {
172
0
    doc->zip = new_encrypted_archive(ctx, doc->zip, info);
173
0
  }
174
0
}
175
176
static fz_html *epub_get_laid_out_html(fz_context *ctx, epub_document *doc, epub_chapter *ch);
177
178
static int count_laid_out_pages(fz_html *html)
179
0
{
180
0
  if (html->tree.root->s.layout.b > 0)
181
0
    return ceilf(html->tree.root->s.layout.b / html->page_h);
182
0
  return 1;
183
0
}
184
185
static void
186
invalidate_accelerator(fz_context *ctx, epub_accelerator *acc)
187
0
{
188
0
  int i;
189
190
0
  for (i = 0; i < acc->max_chapters; i++)
191
0
    acc->pages_in_chapter[i] = -1;
192
0
}
193
194
static int count_chapter_pages(fz_context *ctx, epub_document *doc, epub_chapter *ch)
195
0
{
196
0
  epub_accelerator *acc = doc->accel;
197
0
  int use_doc_css = fz_use_document_css(ctx);
198
199
0
  if (use_doc_css != acc->use_doc_css || doc->css_sum != acc->css_sum)
200
0
  {
201
0
    acc->use_doc_css = use_doc_css;
202
0
    acc->css_sum = doc->css_sum;
203
0
    invalidate_accelerator(ctx, acc);
204
0
  }
205
206
0
  if (ch->number < acc->num_chapters && acc->pages_in_chapter[ch->number] != -1)
207
0
    return acc->pages_in_chapter[ch->number];
208
209
0
  fz_drop_html(ctx, epub_get_laid_out_html(ctx, doc, ch));
210
0
  return acc->pages_in_chapter[ch->number];
211
0
}
212
213
static fz_link_dest
214
epub_resolve_link(fz_context *ctx, fz_document *doc_, const char *dest)
215
0
{
216
0
  epub_document *doc = (epub_document*)doc_;
217
0
  epub_chapter *ch;
218
0
  int i;
219
220
0
  const char *s = strchr(dest, '#');
221
0
  size_t n = s ? (size_t)(s - dest) : strlen(dest);
222
0
  if (s && s[1] == 0)
223
0
    s = NULL;
224
225
0
  for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next)
226
0
  {
227
0
    if (!strncmp(ch->path, dest, n) && ch->path[n] == 0)
228
0
    {
229
0
      if (s)
230
0
      {
231
0
        float y;
232
0
        fz_html *html = epub_get_laid_out_html(ctx, doc, ch);
233
0
        int ph = html->page_h;
234
235
        /* Search for a matching fragment */
236
0
        y = fz_find_html_target(ctx, html, s+1);
237
0
        fz_drop_html(ctx, html);
238
0
        if (y >= 0)
239
0
        {
240
0
          int page = y / ph;
241
0
          return fz_make_link_dest_xyz(i, page, 0, y - page * ph, 0);
242
0
        }
243
0
        return fz_make_link_dest_none();
244
0
      }
245
0
      return fz_make_link_dest_xyz(i, 0, 0, 0, 0);
246
0
    }
247
0
  }
248
249
0
  return fz_make_link_dest_none();
250
0
}
251
252
static void
253
epub_layout(fz_context *ctx, fz_document *doc_, float w, float h, float em)
254
0
{
255
0
  epub_document *doc = (epub_document*)doc_;
256
0
  uint32_t css_sum = user_css_sum(ctx);
257
0
  int use_doc_css = fz_use_document_css(ctx);
258
259
0
  if (doc->layout_w == w && doc->layout_h == h && doc->layout_em == em && doc->css_sum == css_sum)
260
0
    return;
261
0
  doc->layout_w = w;
262
0
  doc->layout_h = h;
263
0
  doc->layout_em = em;
264
265
0
  if (doc->accel == NULL)
266
0
    return;
267
268
  /* When we load the saved accelerator, doc->accel
269
   * can be populated with different values than doc.
270
   * This is really useful as doc starts out with the
271
   * values being 0. If we've got the right values
272
   * already, then don't bin the data! */
273
0
  if (doc->accel->layout_w == w &&
274
0
    doc->accel->layout_h == h &&
275
0
    doc->accel->layout_em == em &&
276
0
    doc->accel->use_doc_css == use_doc_css &&
277
0
    doc->accel->css_sum == css_sum)
278
0
    return;
279
280
0
  doc->accel->layout_w = w;
281
0
  doc->accel->layout_h = h;
282
0
  doc->accel->layout_em = em;
283
0
  doc->accel->use_doc_css = use_doc_css;
284
0
  doc->accel->css_sum = css_sum;
285
0
  invalidate_accelerator(ctx, doc->accel);
286
0
}
287
288
static int
289
epub_count_chapters(fz_context *ctx, fz_document *doc_)
290
0
{
291
0
  epub_document *doc = (epub_document*)doc_;
292
0
  epub_chapter *ch;
293
0
  int count = 0;
294
0
  for (ch = doc->spine; ch; ch = ch->next)
295
0
    ++count;
296
0
  return count;
297
0
}
298
299
static int
300
epub_count_pages(fz_context *ctx, fz_document *doc_, int chapter)
301
0
{
302
0
  epub_document *doc = (epub_document*)doc_;
303
0
  epub_chapter *ch;
304
0
  int i;
305
0
  for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next)
306
0
  {
307
0
    if (i == chapter)
308
0
    {
309
0
      return count_chapter_pages(ctx, doc, ch);
310
0
    }
311
0
  }
312
0
  return 0;
313
0
}
314
315
0
#define MAGIC_ACCELERATOR 0xacce1e7a
316
0
#define MAGIC_ACCEL_EPUB  0x62755065
317
0
#define ACCEL_VERSION     0x00010001
318
319
static void epub_load_accelerator(fz_context *ctx, epub_document *doc, fz_stream *accel)
320
0
{
321
0
  int v;
322
0
  float w, h, em;
323
0
  int num_chapters;
324
0
  epub_accelerator *acc = NULL;
325
0
  uint32_t css_sum;
326
0
  int use_doc_css;
327
0
  int make_new = (accel == NULL);
328
329
0
  fz_var(acc);
330
331
0
  if (accel)
332
0
  {
333
    /* Try to read the accelerator data. If we fail silently give up. */
334
0
    fz_try(ctx)
335
0
    {
336
0
      v = fz_read_int32_le(ctx, accel);
337
0
      if (v != (int32_t)MAGIC_ACCELERATOR)
338
0
      {
339
0
        make_new = 1;
340
0
        break;
341
0
      }
342
343
0
      v = fz_read_int32_le(ctx, accel);
344
0
      if (v != MAGIC_ACCEL_EPUB)
345
0
      {
346
0
        make_new = 1;
347
0
        break;
348
0
      }
349
350
0
      v = fz_read_int32_le(ctx, accel);
351
0
      if (v != ACCEL_VERSION)
352
0
      {
353
0
        make_new = 1;
354
0
        break;
355
0
      }
356
357
0
      w = fz_read_float_le(ctx, accel);
358
0
      h = fz_read_float_le(ctx, accel);
359
0
      em = fz_read_float_le(ctx, accel);
360
0
      css_sum = fz_read_uint32_le(ctx, accel);
361
0
      use_doc_css = fz_read_int32_le(ctx, accel);
362
363
0
      num_chapters = fz_read_int32_le(ctx, accel);
364
0
      if (num_chapters <= 0)
365
0
      {
366
0
        make_new = 1;
367
0
        break;
368
0
      }
369
370
0
      acc = fz_malloc_struct(ctx, epub_accelerator);
371
0
      acc->pages_in_chapter = Memento_label(fz_malloc_array(ctx, num_chapters, int), "accel_pages_in_chapter");
372
0
      acc->max_chapters = acc->num_chapters = num_chapters;
373
0
      acc->layout_w = w;
374
0
      acc->layout_h = h;
375
0
      acc->layout_em = em;
376
0
      acc->css_sum = css_sum;
377
0
      acc->use_doc_css = use_doc_css;
378
379
0
      for (v = 0; v < num_chapters; v++)
380
0
        acc->pages_in_chapter[v] = fz_read_int32_le(ctx, accel);
381
0
    }
382
0
    fz_catch(ctx)
383
0
    {
384
0
      if (acc)
385
0
        fz_free(ctx, acc->pages_in_chapter);
386
0
      fz_free(ctx, acc);
387
      /* Swallow the error and run unaccelerated */
388
0
      make_new = 1;
389
0
    }
390
0
  }
391
392
  /* If we aren't given an accelerator to load (or the one we're given
393
   * is bad) create a blank stub and we can fill it out as we go. */
394
0
  if (make_new)
395
0
  {
396
0
    acc = fz_malloc_struct(ctx, epub_accelerator);
397
0
    acc->css_sum = doc->css_sum;
398
0
    acc->use_doc_css = fz_use_document_css(ctx);
399
0
  }
400
401
0
  doc->accel = acc;
402
0
}
403
404
static void
405
accelerate_chapter(fz_context *ctx, epub_document *doc, epub_chapter *ch, fz_html *html)
406
0
{
407
0
  epub_accelerator *acc = doc->accel;
408
0
  int p = count_laid_out_pages(html);
409
410
0
  if (ch->number < acc->num_chapters)
411
0
  {
412
0
    if (acc->pages_in_chapter[ch->number] != p && acc->pages_in_chapter[ch->number] != -1)
413
0
    {
414
0
      fz_warn(ctx, "Invalidating stale accelerator data.");
415
0
      invalidate_accelerator(ctx, doc->accel);
416
0
    }
417
0
    acc->pages_in_chapter[ch->number] = p;
418
0
    return;
419
0
  }
420
421
0
  if (ch->number >= acc->max_chapters)
422
0
  {
423
0
    int n = acc->max_chapters;
424
0
    int i;
425
0
    if (n == 0)
426
0
      n = 4;
427
0
    while (n <= ch->number)
428
0
      n *= 2;
429
430
0
    acc->pages_in_chapter = fz_realloc_array(ctx, acc->pages_in_chapter, n, int);
431
0
    for (i = acc->max_chapters; i < n; i++)
432
0
      acc->pages_in_chapter[i] = -1;
433
0
    acc->max_chapters = n;
434
0
  }
435
0
  acc->pages_in_chapter[ch->number] = p;
436
0
  if (acc->num_chapters < ch->number+1)
437
0
    acc->num_chapters = ch->number+1;
438
0
}
439
440
static void
441
epub_drop_page(fz_context *ctx, fz_page *page_)
442
0
{
443
0
  epub_page *page = (epub_page *)page_;
444
0
  fz_drop_html(ctx, page->html);
445
0
}
446
447
static epub_chapter *
448
epub_load_chapter(fz_context *ctx, epub_document *doc, const char *path, int i)
449
0
{
450
0
  epub_chapter *ch;
451
452
0
  ch = fz_malloc_struct(ctx, epub_chapter);
453
0
  fz_try(ctx)
454
0
  {
455
0
    ch->path = Memento_label(fz_strdup(ctx, path), "chapter_path");
456
0
    ch->number = i;
457
0
  }
458
0
  fz_catch(ctx)
459
0
  {
460
0
    fz_free(ctx, ch);
461
0
    fz_rethrow(ctx);
462
0
  }
463
464
0
  return ch;
465
0
}
466
467
static fz_html *
468
epub_parse_chapter(fz_context *ctx, epub_document *doc, epub_chapter *ch)
469
0
{
470
0
  fz_archive *zip = doc->zip;
471
0
  fz_buffer *buf;
472
0
  char base_uri[2048];
473
0
  fz_html *html;
474
475
  /* Look for one we made earlier */
476
0
  html = fz_find_html(ctx, doc, ch->number);
477
0
  if (html)
478
0
    return html;
479
480
0
  fz_dirname(base_uri, ch->path, sizeof base_uri);
481
482
0
  buf = fz_read_archive_entry(ctx, zip, ch->path);
483
0
  fz_try(ctx)
484
0
    html = fz_parse_xhtml(ctx, doc->set, zip, base_uri, buf, fz_user_css(ctx));
485
0
  fz_always(ctx)
486
0
    fz_drop_buffer(ctx, buf);
487
0
  fz_catch(ctx)
488
0
    fz_rethrow(ctx);
489
490
0
  return fz_store_html(ctx, html, doc, ch->number);
491
0
}
492
493
static fz_html *
494
epub_get_laid_out_html(fz_context *ctx, epub_document *doc, epub_chapter *ch)
495
0
{
496
0
  fz_html *html = epub_parse_chapter(ctx, doc, ch);
497
0
  fz_try(ctx)
498
0
  {
499
0
    fz_layout_html(ctx, html, doc->layout_w, doc->layout_h, doc->layout_em);
500
0
    accelerate_chapter(ctx, doc, ch, html);
501
0
  }
502
0
  fz_catch(ctx)
503
0
  {
504
0
    fz_drop_html(ctx, html);
505
0
    fz_rethrow(ctx);
506
0
  }
507
508
0
  fz_drop_html(ctx, doc->most_recent_html);
509
0
  doc->most_recent_html = fz_keep_html(ctx, html);
510
511
0
  return html;
512
0
}
513
514
static fz_rect
515
epub_bound_page(fz_context *ctx, fz_page *page_)
516
0
{
517
0
  epub_document *doc = (epub_document*)page_->doc;
518
0
  epub_page *page = (epub_page*)page_;
519
0
  epub_chapter *ch = page->ch;
520
0
  fz_rect bbox;
521
0
  fz_html *html = epub_get_laid_out_html(ctx, doc, ch);
522
523
0
  bbox.x0 = 0;
524
0
  bbox.y0 = 0;
525
0
  bbox.x1 = html->page_w + html->page_margin[L] + html->page_margin[R];
526
0
  bbox.y1 = html->page_h + html->page_margin[T] + html->page_margin[B];
527
0
  fz_drop_html(ctx, html);
528
0
  return bbox;
529
0
}
530
531
static void
532
epub_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
533
0
{
534
0
  epub_page *page = (epub_page*)page_;
535
536
0
  fz_draw_html(ctx, dev, ctm, page->html, page->number);
537
0
}
538
539
static fz_link *
540
epub_load_links(fz_context *ctx, fz_page *page_)
541
0
{
542
0
  epub_page *page = (epub_page*)page_;
543
0
  epub_chapter *ch = page->ch;
544
545
0
  return fz_load_html_links(ctx, page->html, page->number, ch->path);
546
0
}
547
548
static fz_bookmark
549
epub_make_bookmark(fz_context *ctx, fz_document *doc_, fz_location loc)
550
0
{
551
0
  epub_document *doc = (epub_document*)doc_;
552
0
  epub_chapter *ch;
553
0
  int i;
554
555
0
  for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next)
556
0
  {
557
0
    if (i == loc.chapter)
558
0
    {
559
0
      fz_html *html = epub_get_laid_out_html(ctx, doc, ch);
560
0
      fz_bookmark mark = fz_make_html_bookmark(ctx, html, loc.page);
561
0
      fz_drop_html(ctx, html);
562
0
      return mark;
563
0
    }
564
0
  }
565
566
0
  return 0;
567
0
}
568
569
static fz_location
570
epub_lookup_bookmark(fz_context *ctx, fz_document *doc_, fz_bookmark mark)
571
0
{
572
0
  epub_document *doc = (epub_document*)doc_;
573
0
  epub_chapter *ch;
574
0
  int i;
575
576
0
  for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next)
577
0
  {
578
0
    fz_html *html = epub_get_laid_out_html(ctx, doc, ch);
579
0
    int p = fz_lookup_html_bookmark(ctx, html, mark);
580
0
    fz_drop_html(ctx, html);
581
0
    if (p != -1)
582
0
      return fz_make_location(i, p);
583
0
  }
584
0
  return fz_make_location(-1, -1);
585
0
}
586
587
static fz_page *
588
epub_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number)
589
0
{
590
0
  epub_document *doc = (epub_document*)doc_;
591
0
  epub_chapter *ch;
592
0
  int i;
593
0
  for (i = 0, ch = doc->spine; ch; ++i, ch = ch->next)
594
0
  {
595
0
    if (i == chapter)
596
0
    {
597
0
      epub_page *page = fz_new_derived_page(ctx, epub_page, doc_);
598
0
      page->super.bound_page = epub_bound_page;
599
0
      page->super.run_page_contents = epub_run_page;
600
0
      page->super.load_links = epub_load_links;
601
0
      page->super.drop_page = epub_drop_page;
602
0
      page->ch = ch;
603
0
      page->number = number;
604
0
      page->html = epub_get_laid_out_html(ctx, doc, ch);
605
0
      return (fz_page*)page;
606
0
    }
607
0
  }
608
0
  return NULL;
609
0
}
610
611
static void
612
epub_page_label(fz_context *ctx, fz_document *doc_, int chapter, int number, char *buf, int size)
613
0
{
614
0
  fz_snprintf(buf, size, "ch. %d, p. %d", chapter+1, number+1);
615
0
}
616
617
static void
618
epub_drop_accelerator(fz_context *ctx, epub_accelerator *acc)
619
0
{
620
0
  if (acc == NULL)
621
0
    return;
622
623
0
  fz_free(ctx, acc->pages_in_chapter);
624
0
  fz_free(ctx, acc);
625
0
}
626
627
static void
628
epub_drop_document(fz_context *ctx, fz_document *doc_)
629
0
{
630
0
  epub_document *doc = (epub_document*)doc_;
631
0
  epub_chapter *ch, *next;
632
0
  ch = doc->spine;
633
0
  while (ch)
634
0
  {
635
0
    next = ch->next;
636
0
    fz_free(ctx, ch->path);
637
0
    fz_free(ctx, ch);
638
0
    ch = next;
639
0
  }
640
0
  epub_drop_accelerator(ctx, doc->accel);
641
0
  fz_drop_archive(ctx, doc->zip);
642
0
  fz_drop_html_font_set(ctx, doc->set);
643
0
  fz_drop_outline(ctx, doc->outline);
644
0
  fz_free(ctx, doc->dc_title);
645
0
  fz_free(ctx, doc->dc_creator);
646
0
  fz_drop_html(ctx, doc->most_recent_html);
647
0
  fz_purge_stored_html(ctx, doc);
648
0
}
649
650
static const char *
651
rel_path_from_idref(fz_xml *manifest, const char *idref)
652
0
{
653
0
  fz_xml *item;
654
0
  if (!idref)
655
0
    return NULL;
656
0
  item = fz_xml_find_down(manifest, "item");
657
0
  while (item)
658
0
  {
659
0
    const char *id = fz_xml_att(item, "id");
660
0
    if (id && !strcmp(id, idref))
661
0
      return fz_xml_att(item, "href");
662
0
    item = fz_xml_find_next(item, "item");
663
0
  }
664
0
  return NULL;
665
0
}
666
667
static const char *
668
path_from_idref(char *path, fz_xml *manifest, const char *base_uri, const char *idref, int n)
669
0
{
670
0
  const char *rel_path = rel_path_from_idref(manifest, idref);
671
0
  if (!rel_path)
672
0
  {
673
0
    path[0] = 0;
674
0
    return NULL;
675
0
  }
676
0
  fz_strlcpy(path, base_uri, n);
677
0
  fz_strlcat(path, "/", n);
678
0
  fz_strlcat(path, rel_path, n);
679
0
  return fz_cleanname(fz_urldecode(path));
680
0
}
681
682
static fz_outline *
683
epub_parse_ncx_imp(fz_context *ctx, epub_document *doc, fz_xml *node, char *base_uri)
684
0
{
685
0
  char path[2048];
686
0
  fz_outline *outline, *head, **tailp;
687
688
0
  head = NULL;
689
0
  tailp = &head;
690
691
0
  node = fz_xml_find_down(node, "navPoint");
692
0
  while (node)
693
0
  {
694
0
    char *text = fz_xml_text(fz_xml_down(fz_xml_find_down(fz_xml_find_down(node, "navLabel"), "text")));
695
0
    char *content = fz_xml_att(fz_xml_find_down(node, "content"), "src");
696
0
    if (text && content)
697
0
    {
698
0
      fz_strlcpy(path, base_uri, sizeof path);
699
0
      fz_strlcat(path, "/", sizeof path);
700
0
      fz_strlcat(path, content, sizeof path);
701
0
      fz_urldecode(path);
702
0
      fz_cleanname(path);
703
704
0
      fz_try(ctx)
705
0
      {
706
0
        *tailp = outline = fz_new_outline(ctx);
707
0
        tailp = &(*tailp)->next;
708
0
        outline->title = Memento_label(fz_strdup(ctx, text), "outline_title");
709
0
        outline->uri = Memento_label(fz_strdup(ctx, path), "outline_uri");
710
0
        outline->page = fz_make_location(-1, -1);
711
0
        outline->down = epub_parse_ncx_imp(ctx, doc, node, base_uri);
712
0
        outline->is_open = 1;
713
0
      }
714
0
      fz_catch(ctx)
715
0
      {
716
0
        fz_drop_outline(ctx, head);
717
0
        fz_rethrow(ctx);
718
0
      }
719
0
    }
720
0
    node = fz_xml_find_next(node, "navPoint");
721
0
  }
722
723
0
  return head;
724
0
}
725
726
static void
727
epub_parse_ncx(fz_context *ctx, epub_document *doc, const char *path)
728
0
{
729
0
  fz_archive *zip = doc->zip;
730
0
  fz_buffer *buf = NULL;
731
0
  fz_xml_doc *ncx = NULL;
732
0
  char base_uri[2048];
733
734
0
  fz_var(buf);
735
0
  fz_var(ncx);
736
737
0
  fz_try(ctx)
738
0
  {
739
0
    fz_dirname(base_uri, path, sizeof base_uri);
740
0
    buf = fz_read_archive_entry(ctx, zip, path);
741
0
    ncx = fz_parse_xml(ctx, buf, 0);
742
0
    doc->outline = epub_parse_ncx_imp(ctx, doc, fz_xml_find_down(fz_xml_root(ncx), "navMap"), base_uri);
743
0
  }
744
0
  fz_always(ctx)
745
0
  {
746
0
    fz_drop_buffer(ctx, buf);
747
0
    fz_drop_xml(ctx, ncx);
748
0
  }
749
0
  fz_catch(ctx)
750
0
    fz_rethrow(ctx);
751
0
}
752
753
static char *
754
find_metadata(fz_context *ctx, fz_xml *metadata, char *key)
755
0
{
756
0
  char *text = fz_xml_text(fz_xml_down(fz_xml_find_down(metadata, key)));
757
0
  if (text)
758
0
    return fz_strdup(ctx, text);
759
0
  return NULL;
760
0
}
761
762
static fz_buffer *
763
read_container_and_prefix(fz_context *ctx, fz_archive *zip, char *prefix, size_t prefix_len)
764
0
{
765
0
  int n = fz_count_archive_entries(ctx, zip);
766
0
  int i;
767
768
0
  prefix[0] = 0;
769
770
  /* First off, look for the container.xml at the top level. */
771
0
  for (i = 0; i < n; i++)
772
0
  {
773
0
    const char *p = fz_list_archive_entry(ctx, zip, i);
774
775
0
    if (!strcmp(p, "META-INF/container.xml"))
776
0
      return fz_read_archive_entry(ctx, zip, "META-INF/container.xml");
777
0
  }
778
779
  /* If that failed, look for the first such file in a subdirectory. */
780
0
  for (i = 0; i < n; i++)
781
0
  {
782
0
    const char *p = fz_list_archive_entry(ctx, zip, i);
783
0
    size_t z = strlen(p);
784
0
    size_t z0 = sizeof("META-INF/container.xml")-1;
785
786
0
    if (z < z0)
787
0
      continue;
788
0
    if (!strcmp(p + z - z0, "META-INF/container.xml"))
789
0
    {
790
0
      if (z - z0 >= prefix_len)
791
0
      {
792
0
        fz_warn(ctx, "Ignoring %s as path too long.", p);
793
0
        continue;
794
0
      }
795
0
      memcpy(prefix, p, z-z0);
796
0
      prefix[z-z0] = 0;
797
0
      return fz_read_archive_entry(ctx, zip, p);
798
0
    }
799
0
  }
800
801
0
  return fz_read_archive_entry(ctx, zip, "META-INF/container.xml");
802
0
}
803
804
static void
805
epub_parse_header(fz_context *ctx, epub_document *doc)
806
0
{
807
0
  fz_archive *zip = doc->zip;
808
0
  fz_buffer *buf = NULL;
809
0
  fz_xml_doc *encryption_xml = NULL;
810
0
  fz_xml_doc *container_xml = NULL;
811
0
  fz_xml_doc *content_opf = NULL;
812
0
  fz_xml *container, *rootfiles, *rootfile;
813
0
  fz_xml *package, *manifest, *spine, *itemref, *metadata;
814
0
  char base_uri[2048];
815
0
  const char *full_path;
816
0
  const char *version;
817
0
  char ncx[2048], s[2048];
818
0
  char *prefixed_full_path = NULL;
819
0
  size_t prefix_len;
820
0
  epub_chapter **tailp;
821
0
  int i;
822
823
0
  fz_var(buf);
824
0
  fz_var(encryption_xml);
825
0
  fz_var(container_xml);
826
0
  fz_var(content_opf);
827
0
  fz_var(prefixed_full_path);
828
829
0
  fz_try(ctx)
830
0
  {
831
    /* parse META-INF/encryption.xml to figure out which entries are encrypted */
832
833
    /* parse META-INF/container.xml to find OPF */
834
    /* Reuse base_uri to read the prefix. */
835
0
    buf = read_container_and_prefix(ctx, zip, base_uri, sizeof(base_uri));
836
0
    container_xml = fz_parse_xml(ctx, buf, 0);
837
0
    fz_drop_buffer(ctx, buf);
838
0
    buf = NULL;
839
840
    /* Some epub files can be prefixed by a directory name. This (normally
841
     * empty!) will be in base_uri. */
842
0
    prefix_len = strlen(base_uri);
843
0
    {
844
      /* Further abuse base_uri to hold a temporary name. */
845
0
      const size_t z0 = sizeof("META-INF/encryption.xml")-1;
846
0
      if (sizeof(base_uri) <= prefix_len + z0)
847
0
        fz_throw(ctx, FZ_ERROR_GENERIC, "Prefix too long in epub");
848
0
      strcpy(base_uri + prefix_len, "META-INF/encryption.xml");
849
0
      if (fz_has_archive_entry(ctx, zip, base_uri))
850
0
      {
851
0
        fz_warn(ctx, "EPUB may be locked by DRM");
852
853
0
        buf = fz_read_archive_entry(ctx, zip, base_uri);
854
0
        encryption_xml = fz_parse_xml(ctx, buf, 0);
855
0
        fz_drop_buffer(ctx, buf);
856
0
        buf = NULL;
857
858
0
        epub_parse_encryption(ctx, doc, fz_xml_find(fz_xml_root(encryption_xml), "encryption"));
859
0
        zip = doc->zip;
860
0
      }
861
0
    }
862
863
0
    container = fz_xml_find(fz_xml_root(container_xml), "container");
864
0
    rootfiles = fz_xml_find_down(container, "rootfiles");
865
0
    rootfile = fz_xml_find_down(rootfiles, "rootfile");
866
0
    full_path = fz_xml_att(rootfile, "full-path");
867
0
    if (!full_path)
868
0
      fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find root file in EPUB");
869
870
0
    fz_dirname(base_uri+prefix_len, full_path, sizeof(base_uri) - prefix_len);
871
872
0
    prefixed_full_path = fz_malloc(ctx, strlen(full_path) + prefix_len + 1);
873
0
    memcpy(prefixed_full_path, base_uri, prefix_len);
874
0
    strcpy(prefixed_full_path + prefix_len, full_path);
875
876
    /* parse OPF to find NCX and spine */
877
878
0
    buf = fz_read_archive_entry(ctx, zip, prefixed_full_path);
879
0
    content_opf = fz_parse_xml(ctx, buf, 0);
880
0
    fz_drop_buffer(ctx, buf);
881
0
    buf = NULL;
882
883
0
    package = fz_xml_find(fz_xml_root(content_opf), "package");
884
0
    version = fz_xml_att(package, "version");
885
0
    if (!version || strcmp(version, "2.0"))
886
0
      fz_warn(ctx, "unknown epub version: %s", version ? version : "<none>");
887
888
0
    metadata = fz_xml_find_down(package, "metadata");
889
0
    if (metadata)
890
0
    {
891
0
      doc->dc_title = Memento_label(find_metadata(ctx, metadata, "title"), "epub_title");
892
0
      doc->dc_creator = Memento_label(find_metadata(ctx, metadata, "creator"), "epub_creator");
893
0
    }
894
895
0
    manifest = fz_xml_find_down(package, "manifest");
896
0
    spine = fz_xml_find_down(package, "spine");
897
898
0
    if (path_from_idref(ncx, manifest, base_uri, fz_xml_att(spine, "toc"), sizeof ncx))
899
0
    {
900
0
      epub_parse_ncx(ctx, doc, ncx);
901
0
    }
902
903
0
    doc->spine = NULL;
904
0
    tailp = &doc->spine;
905
0
    itemref = fz_xml_find_down(spine, "itemref");
906
0
    i = 0;
907
0
    while (itemref)
908
0
    {
909
0
      if (path_from_idref(s, manifest, base_uri, fz_xml_att(itemref, "idref"), sizeof s))
910
0
      {
911
0
        fz_try(ctx)
912
0
        {
913
0
          *tailp = epub_load_chapter(ctx, doc, s, i);
914
0
          tailp = &(*tailp)->next;
915
0
          i++;
916
0
        }
917
0
        fz_catch(ctx)
918
0
        {
919
0
          fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
920
0
          fz_warn(ctx, "ignoring chapter %s", s);
921
0
        }
922
0
      }
923
0
      itemref = fz_xml_find_next(itemref, "itemref");
924
0
    }
925
0
  }
926
0
  fz_always(ctx)
927
0
  {
928
0
    fz_drop_xml(ctx, content_opf);
929
0
    fz_drop_xml(ctx, container_xml);
930
0
    fz_drop_xml(ctx, encryption_xml);
931
0
    fz_drop_buffer(ctx, buf);
932
0
    fz_free(ctx, prefixed_full_path);
933
0
  }
934
0
  fz_catch(ctx)
935
0
    fz_rethrow(ctx);
936
0
}
937
938
static fz_outline *
939
epub_load_outline(fz_context *ctx, fz_document *doc_)
940
0
{
941
0
  epub_document *doc = (epub_document*)doc_;
942
0
  return fz_keep_outline(ctx, doc->outline);
943
0
}
944
945
static int
946
epub_lookup_metadata(fz_context *ctx, fz_document *doc_, const char *key, char *buf, int size)
947
0
{
948
0
  epub_document *doc = (epub_document*)doc_;
949
0
  if (!strcmp(key, FZ_META_FORMAT))
950
0
    return 1 + (int)fz_strlcpy(buf, "EPUB", size);
951
0
  if (!strcmp(key, FZ_META_INFO_TITLE) && doc->dc_title)
952
0
    return 1 + (int)fz_strlcpy(buf, doc->dc_title, size);
953
0
  if (!strcmp(key, FZ_META_INFO_AUTHOR) && doc->dc_creator)
954
0
    return 1 + (int)fz_strlcpy(buf, doc->dc_creator, size);
955
0
  return -1;
956
0
}
957
958
static void
959
epub_output_accelerator(fz_context *ctx, fz_document *doc_, fz_output *out)
960
0
{
961
0
  epub_document *doc = (epub_document*)doc_;
962
0
  int i;
963
964
0
  fz_try(ctx)
965
0
  {
966
0
    if (doc->accel == NULL)
967
0
      fz_throw(ctx, FZ_ERROR_GENERIC, "No accelerator data to write");
968
969
0
    fz_write_int32_le(ctx, out, MAGIC_ACCELERATOR);
970
0
    fz_write_int32_le(ctx, out, MAGIC_ACCEL_EPUB);
971
0
    fz_write_int32_le(ctx, out, ACCEL_VERSION);
972
0
    fz_write_float_le(ctx, out, doc->accel->layout_w);
973
0
    fz_write_float_le(ctx, out, doc->accel->layout_h);
974
0
    fz_write_float_le(ctx, out, doc->accel->layout_em);
975
0
    fz_write_uint32_le(ctx, out, doc->accel->css_sum);
976
0
    fz_write_int32_le(ctx, out, doc->accel->use_doc_css);
977
0
    fz_write_int32_le(ctx, out, doc->accel->num_chapters);
978
0
    for (i = 0; i < doc->accel->num_chapters; i++)
979
0
      fz_write_int32_le(ctx, out, doc->accel->pages_in_chapter[i]);
980
981
0
    fz_close_output(ctx, out);
982
0
  }
983
0
  fz_always(ctx)
984
0
    fz_drop_output(ctx, out);
985
0
  fz_catch(ctx)
986
0
    fz_rethrow(ctx);
987
0
}
988
989
static fz_document *
990
epub_init(fz_context *ctx, fz_archive *zip, fz_stream *accel)
991
0
{
992
0
  epub_document *doc = NULL;
993
994
0
  fz_var(doc);
995
0
  fz_var(zip);
996
997
0
  fz_try(ctx)
998
0
  {
999
0
    doc = fz_new_derived_document(ctx, epub_document);
1000
0
    doc->zip = zip;
1001
0
    zip = NULL;
1002
1003
0
    doc->super.drop_document = epub_drop_document;
1004
0
    doc->super.layout = epub_layout;
1005
0
    doc->super.load_outline = epub_load_outline;
1006
0
    doc->super.resolve_link_dest = epub_resolve_link;
1007
0
    doc->super.make_bookmark = epub_make_bookmark;
1008
0
    doc->super.lookup_bookmark = epub_lookup_bookmark;
1009
0
    doc->super.count_chapters = epub_count_chapters;
1010
0
    doc->super.count_pages = epub_count_pages;
1011
0
    doc->super.load_page = epub_load_page;
1012
0
    doc->super.page_label = epub_page_label;
1013
0
    doc->super.lookup_metadata = epub_lookup_metadata;
1014
0
    doc->super.output_accelerator = epub_output_accelerator;
1015
0
    doc->super.is_reflowable = 1;
1016
1017
0
    doc->set = fz_new_html_font_set(ctx);
1018
0
    doc->css_sum = user_css_sum(ctx);
1019
0
    epub_load_accelerator(ctx, doc, accel);
1020
0
    epub_parse_header(ctx, doc);
1021
0
  }
1022
0
  fz_catch(ctx)
1023
0
  {
1024
0
    fz_drop_archive(ctx, zip);
1025
0
    fz_drop_document(ctx, &doc->super);
1026
0
    fz_rethrow(ctx);
1027
0
  }
1028
1029
0
  return (fz_document*)doc;
1030
0
}
1031
1032
static fz_document *
1033
epub_open_accel_document_with_stream(fz_context *ctx, fz_stream *file, fz_stream *accel)
1034
0
{
1035
0
  return epub_init(ctx, fz_open_zip_archive_with_stream(ctx, file), accel);
1036
0
}
1037
1038
static fz_document *
1039
epub_open_accel_document(fz_context *ctx, const char *filename, const char *accel)
1040
0
{
1041
0
  fz_stream *afile = NULL;
1042
0
  fz_document *doc;
1043
1044
0
  if (accel)
1045
0
    afile = fz_open_file(ctx, accel);
1046
1047
0
  fz_try(ctx)
1048
0
  {
1049
0
    if (strstr(filename, "META-INF/container.xml") || strstr(filename, "META-INF\\container.xml"))
1050
0
    {
1051
0
      char dirname[2048], *p;
1052
0
      fz_strlcpy(dirname, filename, sizeof dirname);
1053
0
      p = strstr(dirname, "META-INF");
1054
0
      *p = 0;
1055
0
      if (!dirname[0])
1056
0
        fz_strlcpy(dirname, ".", sizeof dirname);
1057
0
      doc = epub_init(ctx, fz_open_directory(ctx, dirname), afile);
1058
0
    }
1059
0
    else
1060
0
      doc = epub_init(ctx, fz_open_zip_archive(ctx, filename), afile);
1061
0
  }
1062
0
  fz_always(ctx)
1063
0
    fz_drop_stream(ctx, afile);
1064
0
  fz_catch(ctx)
1065
0
    fz_rethrow(ctx);
1066
1067
0
  return doc;
1068
0
}
1069
1070
static fz_document *
1071
epub_open_document_with_stream(fz_context *ctx, fz_stream *file)
1072
0
{
1073
0
  return epub_init(ctx, fz_open_zip_archive_with_stream(ctx, file), NULL);
1074
0
}
1075
1076
static fz_document *
1077
epub_open_document(fz_context *ctx, const char *filename)
1078
0
{
1079
0
  return epub_open_accel_document(ctx, filename, NULL);
1080
0
}
1081
1082
static int
1083
epub_recognize(fz_context *doc, const char *magic)
1084
7.04k
{
1085
7.04k
  if (strstr(magic, "META-INF/container.xml") || strstr(magic, "META-INF\\container.xml"))
1086
0
    return 200;
1087
7.04k
  return 0;
1088
7.04k
}
1089
1090
static const char *epub_extensions[] =
1091
{
1092
  "epub",
1093
  NULL
1094
};
1095
1096
static const char *epub_mimetypes[] =
1097
{
1098
  "application/epub+zip",
1099
  NULL
1100
};
1101
1102
fz_document_handler epub_document_handler =
1103
{
1104
  epub_recognize,
1105
  epub_open_document,
1106
  epub_open_document_with_stream,
1107
  epub_extensions,
1108
  epub_mimetypes,
1109
  epub_open_accel_document,
1110
  epub_open_accel_document_with_stream
1111
};