Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/cbz/mucbz.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2024 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "mupdf/fitz.h" | |
| 24 | |
| 25 #include <string.h> | |
| 26 #include <stdlib.h> | |
| 27 | |
| 28 #define DPI 72.0f | |
| 29 | |
| 30 static const char *cbz_ext_list[] = { | |
| 31 ".bmp", | |
| 32 ".gif", | |
| 33 ".hdp", | |
| 34 ".j2k", | |
| 35 ".jb2", | |
| 36 ".jbig2", | |
| 37 ".jp2", | |
| 38 ".jpeg", | |
| 39 ".jpg", | |
| 40 ".jpx", | |
| 41 ".jxr", | |
| 42 ".pam", | |
| 43 ".pbm", | |
| 44 ".pgm", | |
| 45 ".pkm", | |
| 46 ".png", | |
| 47 ".pnm", | |
| 48 ".ppm", | |
| 49 ".tif", | |
| 50 ".tiff", | |
| 51 ".wdp", | |
| 52 NULL | |
| 53 }; | |
| 54 | |
| 55 typedef struct | |
| 56 { | |
| 57 fz_page super; | |
| 58 fz_image *image; | |
| 59 } cbz_page; | |
| 60 | |
| 61 typedef struct | |
| 62 { | |
| 63 fz_document super; | |
| 64 fz_archive *arch; | |
| 65 int page_count; | |
| 66 const char **page; | |
| 67 } cbz_document; | |
| 68 | |
| 69 static inline int cbz_isdigit(int c) | |
| 70 { | |
| 71 return c >= '0' && c <= '9'; | |
| 72 } | |
| 73 | |
| 74 static inline int cbz_toupper(int c) | |
| 75 { | |
| 76 if (c >= 'a' && c <= 'z') | |
| 77 return c - 'a' + 'A'; | |
| 78 return c; | |
| 79 } | |
| 80 | |
| 81 static inline int | |
| 82 cbz_strnatcmp(const char *a, const char *b) | |
| 83 { | |
| 84 int x, y; | |
| 85 | |
| 86 while (*a || *b) | |
| 87 { | |
| 88 if (cbz_isdigit(*a) && cbz_isdigit(*b)) | |
| 89 { | |
| 90 x = *a++ - '0'; | |
| 91 while (cbz_isdigit(*a)) | |
| 92 x = x * 10 + *a++ - '0'; | |
| 93 y = *b++ - '0'; | |
| 94 while (cbz_isdigit(*b)) | |
| 95 y = y * 10 + *b++ - '0'; | |
| 96 } | |
| 97 else | |
| 98 { | |
| 99 x = cbz_toupper(*a++); | |
| 100 y = cbz_toupper(*b++); | |
| 101 } | |
| 102 if (x < y) | |
| 103 return -1; | |
| 104 if (x > y) | |
| 105 return 1; | |
| 106 } | |
| 107 | |
| 108 return 0; | |
| 109 } | |
| 110 | |
| 111 static int | |
| 112 cbz_compare_page_names(const void *a, const void *b) | |
| 113 { | |
| 114 return cbz_strnatcmp(*(const char **)a, *(const char **)b); | |
| 115 } | |
| 116 | |
| 117 static void | |
| 118 cbz_create_page_list(fz_context *ctx, cbz_document *doc) | |
| 119 { | |
| 120 fz_archive *arch = doc->arch; | |
| 121 int i, k, count; | |
| 122 | |
| 123 count = fz_count_archive_entries(ctx, arch); | |
| 124 | |
| 125 doc->page_count = 0; | |
| 126 doc->page = fz_malloc_array(ctx, count, const char *); | |
| 127 | |
| 128 for (i = 0; i < count; i++) | |
| 129 { | |
| 130 const char *name = fz_list_archive_entry(ctx, arch, i); | |
| 131 const char *ext = name ? strrchr(name, '.') : NULL; | |
| 132 for (k = 0; cbz_ext_list[k]; k++) | |
| 133 { | |
| 134 if (ext && !fz_strcasecmp(ext, cbz_ext_list[k])) | |
| 135 { | |
| 136 doc->page[doc->page_count++] = name; | |
| 137 break; | |
| 138 } | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 qsort((char **)doc->page, doc->page_count, sizeof *doc->page, cbz_compare_page_names); | |
| 143 } | |
| 144 | |
| 145 static void | |
| 146 cbz_drop_document(fz_context *ctx, fz_document *doc_) | |
| 147 { | |
| 148 cbz_document *doc = (cbz_document*)doc_; | |
| 149 fz_drop_archive(ctx, doc->arch); | |
| 150 fz_free(ctx, (char **)doc->page); | |
| 151 } | |
| 152 | |
| 153 static int | |
| 154 cbz_count_pages(fz_context *ctx, fz_document *doc_, int chapter) | |
| 155 { | |
| 156 cbz_document *doc = (cbz_document*)doc_; | |
| 157 return doc->page_count; | |
| 158 } | |
| 159 | |
| 160 static fz_rect | |
| 161 cbz_bound_page(fz_context *ctx, fz_page *page_, fz_box_type box) | |
| 162 { | |
| 163 cbz_page *page = (cbz_page*)page_; | |
| 164 fz_image *image = page->image; | |
| 165 int xres, yres; | |
| 166 fz_rect bbox = fz_empty_rect; | |
| 167 uint8_t orientation; | |
| 168 | |
| 169 if (image) | |
| 170 { | |
| 171 fz_image_resolution(image, &xres, &yres); | |
| 172 bbox.x0 = bbox.y0 = 0; | |
| 173 orientation = fz_image_orientation(ctx, image); | |
| 174 if (orientation == 0 || (orientation & 1) == 1) | |
| 175 { | |
| 176 bbox.x1 = image->w * DPI / xres; | |
| 177 bbox.y1 = image->h * DPI / yres; | |
| 178 } | |
| 179 else | |
| 180 { | |
| 181 bbox.y1 = image->w * DPI / xres; | |
| 182 bbox.x1 = image->h * DPI / yres; | |
| 183 } | |
| 184 } | |
| 185 return bbox; | |
| 186 } | |
| 187 | |
| 188 static void | |
| 189 cbz_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) | |
| 190 { | |
| 191 cbz_page *page = (cbz_page*)page_; | |
| 192 fz_image *image = page->image; | |
| 193 int xres, yres; | |
| 194 float w, h; | |
| 195 uint8_t orientation; | |
| 196 fz_matrix immat; | |
| 197 | |
| 198 if (image) | |
| 199 { | |
| 200 fz_try(ctx) | |
| 201 { | |
| 202 fz_image_resolution(image, &xres, &yres); | |
| 203 orientation = fz_image_orientation(ctx, image); | |
| 204 if (orientation == 0 || (orientation & 1) == 1) | |
| 205 { | |
| 206 w = image->w * DPI / xres; | |
| 207 h = image->h * DPI / yres; | |
| 208 } | |
| 209 else | |
| 210 { | |
| 211 h = image->w * DPI / xres; | |
| 212 w = image->h * DPI / yres; | |
| 213 } | |
| 214 immat = fz_image_orientation_matrix(ctx, image); | |
| 215 immat = fz_post_scale(immat, w, h); | |
| 216 ctm = fz_concat(immat, ctm); | |
| 217 fz_fill_image(ctx, dev, image, ctm, 1, fz_default_color_params); | |
| 218 } | |
| 219 fz_catch(ctx) | |
| 220 { | |
| 221 fz_report_error(ctx); | |
| 222 fz_warn(ctx, "cannot render image on page"); | |
| 223 } | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 static void | |
| 228 cbz_drop_page(fz_context *ctx, fz_page *page_) | |
| 229 { | |
| 230 cbz_page *page = (cbz_page*)page_; | |
| 231 fz_drop_image(ctx, page->image); | |
| 232 } | |
| 233 | |
| 234 static fz_page * | |
| 235 cbz_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) | |
| 236 { | |
| 237 cbz_document *doc = (cbz_document*)doc_; | |
| 238 cbz_page *page = NULL; | |
| 239 fz_buffer *buf = NULL; | |
| 240 | |
| 241 if (number < 0 || number >= doc->page_count) | |
| 242 fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid page number %d", number); | |
| 243 | |
| 244 fz_var(page); | |
| 245 | |
| 246 page = fz_new_derived_page(ctx, cbz_page, doc_); | |
| 247 page->super.bound_page = cbz_bound_page; | |
| 248 page->super.run_page_contents = cbz_run_page; | |
| 249 page->super.drop_page = cbz_drop_page; | |
| 250 | |
| 251 fz_try(ctx) | |
| 252 { | |
| 253 buf = fz_read_archive_entry(ctx, doc->arch, doc->page[number]); | |
| 254 page->image = fz_new_image_from_buffer(ctx, buf); | |
| 255 } | |
| 256 fz_always(ctx) | |
| 257 { | |
| 258 fz_drop_buffer(ctx, buf); | |
| 259 } | |
| 260 fz_catch(ctx) | |
| 261 { | |
| 262 fz_report_error(ctx); | |
| 263 fz_warn(ctx, "cannot decode image on page, leaving it blank"); | |
| 264 } | |
| 265 | |
| 266 return (fz_page*)page; | |
| 267 } | |
| 268 | |
| 269 static int | |
| 270 cbz_lookup_metadata(fz_context *ctx, fz_document *doc_, const char *key, char *buf, size_t size) | |
| 271 { | |
| 272 cbz_document *doc = (cbz_document*)doc_; | |
| 273 if (!strcmp(key, FZ_META_FORMAT)) | |
| 274 return 1 + (int) fz_strlcpy(buf, fz_archive_format(ctx, doc->arch), size); | |
| 275 return -1; | |
| 276 } | |
| 277 | |
| 278 static fz_document * | |
| 279 cbz_open_document(fz_context *ctx, const fz_document_handler *handler, fz_stream *file, fz_stream *accel, fz_archive *dir, void *state) | |
| 280 { | |
| 281 cbz_document *doc = fz_new_derived_document(ctx, cbz_document); | |
| 282 | |
| 283 doc->super.drop_document = cbz_drop_document; | |
| 284 doc->super.count_pages = cbz_count_pages; | |
| 285 doc->super.load_page = cbz_load_page; | |
| 286 doc->super.lookup_metadata = cbz_lookup_metadata; | |
| 287 | |
| 288 fz_try(ctx) | |
| 289 { | |
| 290 if (file) | |
| 291 doc->arch = fz_open_archive_with_stream(ctx, file); | |
| 292 else | |
| 293 doc->arch = fz_keep_archive(ctx, dir); | |
| 294 cbz_create_page_list(ctx, doc); | |
| 295 } | |
| 296 fz_catch(ctx) | |
| 297 { | |
| 298 fz_drop_document(ctx, (fz_document*)doc); | |
| 299 fz_rethrow(ctx); | |
| 300 } | |
| 301 return (fz_document*)doc; | |
| 302 } | |
| 303 | |
| 304 static const char *cbz_extensions[] = | |
| 305 { | |
| 306 #ifdef HAVE_LIBARCHIVE | |
| 307 "cbr", | |
| 308 #endif | |
| 309 "cbt", | |
| 310 "cbz", | |
| 311 "tar", | |
| 312 "zip", | |
| 313 NULL | |
| 314 }; | |
| 315 | |
| 316 static const char *cbz_mimetypes[] = | |
| 317 { | |
| 318 #ifdef HAVE_LIBARCHIVE | |
| 319 "application/vnd.comicbook-rar", | |
| 320 #endif | |
| 321 "application/vnd.comicbook+zip", | |
| 322 #ifdef HAVE_LIBARCHIVE | |
| 323 "application/x-cbr", | |
| 324 #endif | |
| 325 "application/x-cbt", | |
| 326 "application/x-cbz", | |
| 327 "application/x-tar", | |
| 328 "application/zip", | |
| 329 NULL | |
| 330 }; | |
| 331 | |
| 332 static int | |
| 333 cbz_recognize_doc_content(fz_context *ctx, const fz_document_handler *handler, fz_stream *stream, fz_archive *dir, void **state, fz_document_recognize_state_free_fn **freestate) | |
| 334 { | |
| 335 fz_archive *arch = NULL; | |
| 336 int ret = 0; | |
| 337 int i, k, count; | |
| 338 | |
| 339 fz_var(arch); | |
| 340 fz_var(ret); | |
| 341 | |
| 342 fz_try(ctx) | |
| 343 { | |
| 344 if (stream == NULL) | |
| 345 arch = fz_keep_archive(ctx, dir); | |
| 346 else | |
| 347 { | |
| 348 arch = fz_try_open_archive_with_stream(ctx, stream); | |
| 349 if (arch == NULL) | |
| 350 break; | |
| 351 } | |
| 352 | |
| 353 /* If it's an archive, and we can find at least one plausible page | |
| 354 * then we can open it as a cbz. */ | |
| 355 count = fz_count_archive_entries(ctx, arch); | |
| 356 for (i = 0; i < count && ret == 0; i++) | |
| 357 { | |
| 358 const char *name = fz_list_archive_entry(ctx, arch, i); | |
| 359 const char *ext; | |
| 360 if (name == NULL) | |
| 361 continue; | |
| 362 ext = strrchr(name, '.'); | |
| 363 if (ext) | |
| 364 { | |
| 365 for (k = 0; cbz_ext_list[k]; k++) | |
| 366 { | |
| 367 if (!fz_strcasecmp(ext, cbz_ext_list[k])) | |
| 368 { | |
| 369 ret = 25; | |
| 370 break; | |
| 371 } | |
| 372 } | |
| 373 } | |
| 374 } | |
| 375 } | |
| 376 fz_always(ctx) | |
| 377 fz_drop_archive(ctx, arch); | |
| 378 fz_catch(ctx) | |
| 379 fz_rethrow(ctx); | |
| 380 | |
| 381 return ret; | |
| 382 } | |
| 383 | |
| 384 fz_document_handler cbz_document_handler = | |
| 385 { | |
| 386 NULL, | |
| 387 cbz_open_document, | |
| 388 cbz_extensions, | |
| 389 cbz_mimetypes, | |
| 390 cbz_recognize_doc_content | |
| 391 }; |
