/source/cbz/mucbz.c

https://gitlab.com/zimumavo/mupdf · C · 244 lines · 208 code · 36 blank · 0 comment · 32 complexity · cf12f114d5d1a8197915ab49adec0503 MD5 · raw file

  1. #include "mupdf/fitz.h"
  2. #define DPI 72.0f
  3. typedef struct cbz_document_s cbz_document;
  4. typedef struct cbz_page_s cbz_page;
  5. static const char *cbz_ext_list[] = {
  6. ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tif", ".tiff", ".jpx", ".jp2", ".j2k", ".wdp", ".hdp", ".jxr", ".pbm", ".pgm", ".ppm", ".pam", ".pnm",
  7. NULL
  8. };
  9. struct cbz_page_s
  10. {
  11. fz_page super;
  12. fz_image *image;
  13. };
  14. struct cbz_document_s
  15. {
  16. fz_document super;
  17. fz_archive *arch;
  18. int page_count;
  19. const char **page;
  20. };
  21. static inline int cbz_isdigit(int c)
  22. {
  23. return c >= '0' && c <= '9';
  24. }
  25. static inline int cbz_toupper(int c)
  26. {
  27. if (c >= 'a' && c <= 'z')
  28. return c - 'a' + 'A';
  29. return c;
  30. }
  31. static inline int
  32. cbz_strnatcmp(const char *a, const char *b)
  33. {
  34. int x, y;
  35. while (*a || *b)
  36. {
  37. if (cbz_isdigit(*a) && cbz_isdigit(*b))
  38. {
  39. x = *a++ - '0';
  40. while (cbz_isdigit(*a))
  41. x = x * 10 + *a++ - '0';
  42. y = *b++ - '0';
  43. while (cbz_isdigit(*b))
  44. y = y * 10 + *b++ - '0';
  45. }
  46. else
  47. {
  48. x = cbz_toupper(*a++);
  49. y = cbz_toupper(*b++);
  50. }
  51. if (x < y)
  52. return -1;
  53. if (x > y)
  54. return 1;
  55. }
  56. return 0;
  57. }
  58. static int
  59. cbz_compare_page_names(const void *a, const void *b)
  60. {
  61. return cbz_strnatcmp(*(const char **)a, *(const char **)b);
  62. }
  63. static void
  64. cbz_create_page_list(fz_context *ctx, cbz_document *doc)
  65. {
  66. fz_archive *arch = doc->arch;
  67. int i, k, count;
  68. count = fz_count_archive_entries(ctx, arch);
  69. doc->page_count = 0;
  70. doc->page = fz_malloc_array(ctx, count, sizeof *doc->page);
  71. for (i = 0; i < count; i++)
  72. {
  73. for (k = 0; cbz_ext_list[k]; k++)
  74. {
  75. const char *name = fz_list_archive_entry(ctx, arch, i);
  76. const char *ext = name ? strrchr(name, '.') : NULL;
  77. if (ext && !fz_strcasecmp(ext, cbz_ext_list[k]))
  78. {
  79. doc->page[doc->page_count++] = name;
  80. break;
  81. }
  82. }
  83. }
  84. qsort((char **)doc->page, doc->page_count, sizeof *doc->page, cbz_compare_page_names);
  85. }
  86. static void
  87. cbz_drop_document(fz_context *ctx, cbz_document *doc)
  88. {
  89. fz_drop_archive(ctx, doc->arch);
  90. fz_free(ctx, (char **)doc->page);
  91. }
  92. static int
  93. cbz_count_pages(fz_context *ctx, cbz_document *doc)
  94. {
  95. return doc->page_count;
  96. }
  97. static fz_rect *
  98. cbz_bound_page(fz_context *ctx, cbz_page *page, fz_rect *bbox)
  99. {
  100. fz_image *image = page->image;
  101. int xres, yres;
  102. fz_image_resolution(image, &xres, &yres);
  103. bbox->x0 = bbox->y0 = 0;
  104. bbox->x1 = image->w * DPI / xres;
  105. bbox->y1 = image->h * DPI / yres;
  106. return bbox;
  107. }
  108. static void
  109. cbz_run_page(fz_context *ctx, cbz_page *page, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie)
  110. {
  111. fz_matrix local_ctm = *ctm;
  112. fz_image *image = page->image;
  113. int xres, yres;
  114. float w, h;
  115. fz_image_resolution(image, &xres, &yres);
  116. w = image->w * DPI / xres;
  117. h = image->h * DPI / yres;
  118. fz_pre_scale(&local_ctm, w, h);
  119. fz_fill_image(ctx, dev, image, &local_ctm, 1);
  120. }
  121. static void
  122. cbz_drop_page(fz_context *ctx, cbz_page *page)
  123. {
  124. fz_drop_image(ctx, page->image);
  125. }
  126. static cbz_page *
  127. cbz_load_page(fz_context *ctx, cbz_document *doc, int number)
  128. {
  129. cbz_page *page = NULL;
  130. fz_buffer *buf = NULL;
  131. if (number < 0 || number >= doc->page_count)
  132. return NULL;
  133. fz_var(page);
  134. if (doc->arch)
  135. buf = fz_read_archive_entry(ctx, doc->arch, doc->page[number]);
  136. if (!buf)
  137. fz_throw(ctx, FZ_ERROR_GENERIC, "cannot load cbz page");
  138. fz_try(ctx)
  139. {
  140. page = fz_new_page(ctx, sizeof *page);
  141. page->super.bound_page = (fz_page_bound_page_fn *)cbz_bound_page;
  142. page->super.run_page_contents = (fz_page_run_page_contents_fn *)cbz_run_page;
  143. page->super.drop_page = (fz_page_drop_page_fn *)cbz_drop_page;
  144. page->image = fz_new_image_from_buffer(ctx, buf);
  145. }
  146. fz_always(ctx)
  147. {
  148. fz_drop_buffer(ctx, buf);
  149. }
  150. fz_catch(ctx)
  151. {
  152. fz_drop_page(ctx, &page->super);
  153. fz_rethrow(ctx);
  154. }
  155. return page;
  156. }
  157. static int
  158. cbz_lookup_metadata(fz_context *ctx, cbz_document *doc, const char *key, char *buf, int size)
  159. {
  160. if (!strcmp(key, "format"))
  161. return (int) fz_strlcpy(buf, fz_archive_format(ctx, doc->arch), size);
  162. return -1;
  163. }
  164. static cbz_document *
  165. cbz_open_document_with_stream(fz_context *ctx, fz_stream *file)
  166. {
  167. cbz_document *doc;
  168. doc = fz_new_document(ctx, cbz_document);
  169. doc->super.drop_document = (fz_document_drop_fn *)cbz_drop_document;
  170. doc->super.count_pages = (fz_document_count_pages_fn *)cbz_count_pages;
  171. doc->super.load_page = (fz_document_load_page_fn *)cbz_load_page;
  172. doc->super.lookup_metadata = (fz_document_lookup_metadata_fn *)cbz_lookup_metadata;
  173. fz_try(ctx)
  174. {
  175. doc->arch = fz_open_archive_with_stream(ctx, file);
  176. cbz_create_page_list(ctx, doc);
  177. }
  178. fz_catch(ctx)
  179. {
  180. fz_drop_document(ctx, &doc->super);
  181. fz_rethrow(ctx);
  182. }
  183. return doc;
  184. }
  185. static int
  186. cbz_recognize(fz_context *ctx, const char *magic)
  187. {
  188. char *ext = strrchr(magic, '.');
  189. if ((ext && !fz_strcasecmp(ext, ".cbz")) || !strcmp(magic, "cbz") ||
  190. !strcmp(magic, "application/x-cbz"))
  191. return 100;
  192. if ((ext && !fz_strcasecmp(ext, ".zip")) || !strcmp(magic, "zip") ||
  193. !strcmp(magic, "application/zip"))
  194. return 100;
  195. if ((ext && !fz_strcasecmp(ext, ".tar")) || !strcmp(magic, "tar") ||
  196. !strcmp(magic, "application/x-tar"))
  197. return 100;
  198. if ((ext && !fz_strcasecmp(ext, ".cbt")) || !strcmp(magic, "cbt") ||
  199. !strcmp(magic, "application/x-cbt"))
  200. return 100;
  201. return 0;
  202. }
  203. fz_document_handler cbz_document_handler =
  204. {
  205. (fz_document_recognize_fn *)&cbz_recognize,
  206. (fz_document_open_fn *)NULL,
  207. (fz_document_open_with_stream_fn *)&cbz_open_document_with_stream
  208. };