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