Mercurial > hgrepos > Python2 > PyMuPDF
view mupdf-source/source/cbz/mucbz.c @ 46:7ee69f120f19 default tip
>>>>> tag v1.26.5+1 for changeset b74429b0f5c4
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sat, 11 Oct 2025 17:17:30 +0200 |
| parents | b50eed0cc0ef |
| children |
line wrap: on
line source
// 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 };
