Mercurial > hgrepos > Python2 > PyMuPDF
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/cbz/mucbz.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,391 @@ +// Copyright (C) 2004-2024 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" + +#include <string.h> +#include <stdlib.h> + +#define DPI 72.0f + +static const char *cbz_ext_list[] = { + ".bmp", + ".gif", + ".hdp", + ".j2k", + ".jb2", + ".jbig2", + ".jp2", + ".jpeg", + ".jpg", + ".jpx", + ".jxr", + ".pam", + ".pbm", + ".pgm", + ".pkm", + ".png", + ".pnm", + ".ppm", + ".tif", + ".tiff", + ".wdp", + NULL +}; + +typedef struct +{ + fz_page super; + fz_image *image; +} cbz_page; + +typedef struct +{ + fz_document super; + fz_archive *arch; + int page_count; + const char **page; +} cbz_document; + +static inline int cbz_isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +static inline int cbz_toupper(int c) +{ + if (c >= 'a' && c <= 'z') + return c - 'a' + 'A'; + return c; +} + +static inline int +cbz_strnatcmp(const char *a, const char *b) +{ + int x, y; + + while (*a || *b) + { + if (cbz_isdigit(*a) && cbz_isdigit(*b)) + { + x = *a++ - '0'; + while (cbz_isdigit(*a)) + x = x * 10 + *a++ - '0'; + y = *b++ - '0'; + while (cbz_isdigit(*b)) + y = y * 10 + *b++ - '0'; + } + else + { + x = cbz_toupper(*a++); + y = cbz_toupper(*b++); + } + if (x < y) + return -1; + if (x > y) + return 1; + } + + return 0; +} + +static int +cbz_compare_page_names(const void *a, const void *b) +{ + return cbz_strnatcmp(*(const char **)a, *(const char **)b); +} + +static void +cbz_create_page_list(fz_context *ctx, cbz_document *doc) +{ + fz_archive *arch = doc->arch; + int i, k, count; + + count = fz_count_archive_entries(ctx, arch); + + doc->page_count = 0; + doc->page = fz_malloc_array(ctx, count, const char *); + + for (i = 0; i < count; i++) + { + const char *name = fz_list_archive_entry(ctx, arch, i); + const char *ext = name ? strrchr(name, '.') : NULL; + for (k = 0; cbz_ext_list[k]; k++) + { + if (ext && !fz_strcasecmp(ext, cbz_ext_list[k])) + { + doc->page[doc->page_count++] = name; + break; + } + } + } + + qsort((char **)doc->page, doc->page_count, sizeof *doc->page, cbz_compare_page_names); +} + +static void +cbz_drop_document(fz_context *ctx, fz_document *doc_) +{ + cbz_document *doc = (cbz_document*)doc_; + fz_drop_archive(ctx, doc->arch); + fz_free(ctx, (char **)doc->page); +} + +static int +cbz_count_pages(fz_context *ctx, fz_document *doc_, int chapter) +{ + cbz_document *doc = (cbz_document*)doc_; + return doc->page_count; +} + +static fz_rect +cbz_bound_page(fz_context *ctx, fz_page *page_, fz_box_type box) +{ + cbz_page *page = (cbz_page*)page_; + fz_image *image = page->image; + int xres, yres; + fz_rect bbox = fz_empty_rect; + uint8_t orientation; + + if (image) + { + fz_image_resolution(image, &xres, &yres); + bbox.x0 = bbox.y0 = 0; + orientation = fz_image_orientation(ctx, image); + if (orientation == 0 || (orientation & 1) == 1) + { + bbox.x1 = image->w * DPI / xres; + bbox.y1 = image->h * DPI / yres; + } + else + { + bbox.y1 = image->w * DPI / xres; + bbox.x1 = image->h * DPI / yres; + } + } + return bbox; +} + +static void +cbz_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) +{ + cbz_page *page = (cbz_page*)page_; + fz_image *image = page->image; + int xres, yres; + float w, h; + uint8_t orientation; + fz_matrix immat; + + if (image) + { + fz_try(ctx) + { + fz_image_resolution(image, &xres, &yres); + orientation = fz_image_orientation(ctx, image); + if (orientation == 0 || (orientation & 1) == 1) + { + w = image->w * DPI / xres; + h = image->h * DPI / yres; + } + else + { + h = image->w * DPI / xres; + w = image->h * DPI / yres; + } + immat = fz_image_orientation_matrix(ctx, image); + immat = fz_post_scale(immat, w, h); + ctm = fz_concat(immat, ctm); + fz_fill_image(ctx, dev, image, ctm, 1, fz_default_color_params); + } + fz_catch(ctx) + { + fz_report_error(ctx); + fz_warn(ctx, "cannot render image on page"); + } + } +} + +static void +cbz_drop_page(fz_context *ctx, fz_page *page_) +{ + cbz_page *page = (cbz_page*)page_; + fz_drop_image(ctx, page->image); +} + +static fz_page * +cbz_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) +{ + cbz_document *doc = (cbz_document*)doc_; + cbz_page *page = NULL; + fz_buffer *buf = NULL; + + if (number < 0 || number >= doc->page_count) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid page number %d", number); + + fz_var(page); + + page = fz_new_derived_page(ctx, cbz_page, doc_); + page->super.bound_page = cbz_bound_page; + page->super.run_page_contents = cbz_run_page; + page->super.drop_page = cbz_drop_page; + + fz_try(ctx) + { + buf = fz_read_archive_entry(ctx, doc->arch, doc->page[number]); + page->image = fz_new_image_from_buffer(ctx, buf); + } + fz_always(ctx) + { + fz_drop_buffer(ctx, buf); + } + fz_catch(ctx) + { + fz_report_error(ctx); + fz_warn(ctx, "cannot decode image on page, leaving it blank"); + } + + return (fz_page*)page; +} + +static int +cbz_lookup_metadata(fz_context *ctx, fz_document *doc_, const char *key, char *buf, size_t size) +{ + cbz_document *doc = (cbz_document*)doc_; + if (!strcmp(key, FZ_META_FORMAT)) + return 1 + (int) fz_strlcpy(buf, fz_archive_format(ctx, doc->arch), size); + return -1; +} + +static fz_document * +cbz_open_document(fz_context *ctx, const fz_document_handler *handler, fz_stream *file, fz_stream *accel, fz_archive *dir, void *state) +{ + cbz_document *doc = fz_new_derived_document(ctx, cbz_document); + + doc->super.drop_document = cbz_drop_document; + doc->super.count_pages = cbz_count_pages; + doc->super.load_page = cbz_load_page; + doc->super.lookup_metadata = cbz_lookup_metadata; + + fz_try(ctx) + { + if (file) + doc->arch = fz_open_archive_with_stream(ctx, file); + else + doc->arch = fz_keep_archive(ctx, dir); + cbz_create_page_list(ctx, doc); + } + fz_catch(ctx) + { + fz_drop_document(ctx, (fz_document*)doc); + fz_rethrow(ctx); + } + return (fz_document*)doc; +} + +static const char *cbz_extensions[] = +{ +#ifdef HAVE_LIBARCHIVE + "cbr", +#endif + "cbt", + "cbz", + "tar", + "zip", + NULL +}; + +static const char *cbz_mimetypes[] = +{ +#ifdef HAVE_LIBARCHIVE + "application/vnd.comicbook-rar", +#endif + "application/vnd.comicbook+zip", +#ifdef HAVE_LIBARCHIVE + "application/x-cbr", +#endif + "application/x-cbt", + "application/x-cbz", + "application/x-tar", + "application/zip", + NULL +}; + +static int +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) +{ + fz_archive *arch = NULL; + int ret = 0; + int i, k, count; + + fz_var(arch); + fz_var(ret); + + fz_try(ctx) + { + if (stream == NULL) + arch = fz_keep_archive(ctx, dir); + else + { + arch = fz_try_open_archive_with_stream(ctx, stream); + if (arch == NULL) + break; + } + + /* If it's an archive, and we can find at least one plausible page + * then we can open it as a cbz. */ + count = fz_count_archive_entries(ctx, arch); + for (i = 0; i < count && ret == 0; i++) + { + const char *name = fz_list_archive_entry(ctx, arch, i); + const char *ext; + if (name == NULL) + continue; + ext = strrchr(name, '.'); + if (ext) + { + for (k = 0; cbz_ext_list[k]; k++) + { + if (!fz_strcasecmp(ext, cbz_ext_list[k])) + { + ret = 25; + break; + } + } + } + } + } + fz_always(ctx) + fz_drop_archive(ctx, arch); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +fz_document_handler cbz_document_handler = +{ + NULL, + cbz_open_document, + cbz_extensions, + cbz_mimetypes, + cbz_recognize_doc_content +};
