1 | #include "mupdf/fitz.h" |
2 | #include "html-imp.h" |
3 | |
4 | #include <string.h> |
5 | #include <math.h> |
6 | |
7 | enum { T, R, B, L }; |
8 | |
9 | typedef struct html_document_s html_document; |
10 | typedef struct html_page_s html_page; |
11 | |
12 | struct html_document_s |
13 | { |
14 | fz_document super; |
15 | fz_archive *zip; |
16 | fz_html_font_set *set; |
17 | fz_html *html; |
18 | fz_outline *outline; |
19 | }; |
20 | |
21 | struct html_page_s |
22 | { |
23 | fz_page super; |
24 | html_document *doc; |
25 | int number; |
26 | }; |
27 | |
28 | static void |
29 | htdoc_drop_document(fz_context *ctx, fz_document *doc_) |
30 | { |
31 | html_document *doc = (html_document*)doc_; |
32 | fz_drop_archive(ctx, doc->zip); |
33 | fz_drop_html(ctx, doc->html); |
34 | fz_drop_html_font_set(ctx, doc->set); |
35 | fz_drop_outline(ctx, doc->outline); |
36 | } |
37 | |
38 | static int |
39 | htdoc_resolve_link(fz_context *ctx, fz_document *doc_, const char *dest, float *xp, float *yp) |
40 | { |
41 | html_document *doc = (html_document*)doc_; |
42 | const char *s = strchr(dest, '#'); |
43 | if (s && s[1] != 0) |
44 | { |
45 | float y = fz_find_html_target(ctx, doc->html, s+1); |
46 | if (y >= 0) |
47 | { |
48 | int page = y / doc->html->page_h; |
49 | if (yp) *yp = y - page * doc->html->page_h; |
50 | return page; |
51 | } |
52 | } |
53 | |
54 | return -1; |
55 | } |
56 | |
57 | static int |
58 | htdoc_count_pages(fz_context *ctx, fz_document *doc_) |
59 | { |
60 | html_document *doc = (html_document*)doc_; |
61 | if (doc->html->root->b > 0) |
62 | return ceilf(doc->html->root->b / doc->html->page_h); |
63 | return 1; |
64 | } |
65 | |
66 | static void |
67 | htdoc_update_outline(fz_context *ctx, fz_document *doc, fz_outline *node) |
68 | { |
69 | while (node) |
70 | { |
71 | node->page = htdoc_resolve_link(ctx, doc, node->uri, &node->x, &node->y); |
72 | htdoc_update_outline(ctx, doc, node->down); |
73 | node = node->next; |
74 | } |
75 | } |
76 | |
77 | static void |
78 | htdoc_layout(fz_context *ctx, fz_document *doc_, float w, float h, float em) |
79 | { |
80 | html_document *doc = (html_document*)doc_; |
81 | |
82 | fz_layout_html(ctx, doc->html, w, h, em); |
83 | |
84 | htdoc_update_outline(ctx, doc_, doc->outline); |
85 | } |
86 | |
87 | static void |
88 | htdoc_drop_page(fz_context *ctx, fz_page *page_) |
89 | { |
90 | } |
91 | |
92 | static fz_rect |
93 | htdoc_bound_page(fz_context *ctx, fz_page *page_) |
94 | { |
95 | html_page *page = (html_page*)page_; |
96 | html_document *doc = page->doc; |
97 | fz_rect bbox; |
98 | bbox.x0 = 0; |
99 | bbox.y0 = 0; |
100 | bbox.x1 = doc->html->page_w + doc->html->page_margin[L] + doc->html->page_margin[R]; |
101 | bbox.y1 = doc->html->page_h + doc->html->page_margin[T] + doc->html->page_margin[B]; |
102 | return bbox; |
103 | } |
104 | |
105 | static void |
106 | htdoc_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) |
107 | { |
108 | html_page *page = (html_page*)page_; |
109 | html_document *doc = page->doc; |
110 | fz_draw_html(ctx, dev, ctm, doc->html, page->number); |
111 | } |
112 | |
113 | static fz_link * |
114 | htdoc_load_links(fz_context *ctx, fz_page *page_) |
115 | { |
116 | html_page *page = (html_page*)page_; |
117 | html_document *doc = page->doc; |
118 | return fz_load_html_links(ctx, doc->html, page->number, "" , doc); |
119 | } |
120 | |
121 | static fz_bookmark |
122 | htdoc_make_bookmark(fz_context *ctx, fz_document *doc_, int page) |
123 | { |
124 | html_document *doc = (html_document*)doc_; |
125 | return fz_make_html_bookmark(ctx, doc->html, page); |
126 | } |
127 | |
128 | static int |
129 | htdoc_lookup_bookmark(fz_context *ctx, fz_document *doc_, fz_bookmark mark) |
130 | { |
131 | html_document *doc = (html_document*)doc_; |
132 | return fz_lookup_html_bookmark(ctx, doc->html, mark); |
133 | } |
134 | |
135 | static fz_page * |
136 | htdoc_load_page(fz_context *ctx, fz_document *doc_, int number) |
137 | { |
138 | html_document *doc = (html_document*)doc_; |
139 | html_page *page = fz_new_derived_page(ctx, html_page); |
140 | page->super.bound_page = htdoc_bound_page; |
141 | page->super.run_page_contents = htdoc_run_page; |
142 | page->super.load_links = htdoc_load_links; |
143 | page->super.drop_page = htdoc_drop_page; |
144 | page->doc = doc; |
145 | page->number = number; |
146 | return (fz_page*)page; |
147 | } |
148 | |
149 | static fz_outline * |
150 | htdoc_load_outline(fz_context *ctx, fz_document *doc_) |
151 | { |
152 | html_document *doc = (html_document*)doc_; |
153 | return fz_keep_outline(ctx, doc->outline); |
154 | } |
155 | |
156 | static int |
157 | htdoc_lookup_metadata(fz_context *ctx, fz_document *doc_, const char *key, char *buf, int size) |
158 | { |
159 | html_document *doc = (html_document*)doc_; |
160 | if (!strcmp(key, FZ_META_FORMAT)) |
161 | return (int)fz_strlcpy(buf, "XHTML" , size); |
162 | if (!strcmp(key, FZ_META_INFO_TITLE) && doc->html->title) |
163 | return (int)fz_strlcpy(buf, doc->html->title, size); |
164 | return -1; |
165 | } |
166 | |
167 | static fz_document * |
168 | htdoc_open_document_with_buffer(fz_context *ctx, const char *dirname, fz_buffer *buf) |
169 | { |
170 | html_document *doc = fz_new_derived_document(ctx, html_document); |
171 | doc->super.drop_document = htdoc_drop_document; |
172 | doc->super.layout = htdoc_layout; |
173 | doc->super.load_outline = htdoc_load_outline; |
174 | doc->super.resolve_link = htdoc_resolve_link; |
175 | doc->super.make_bookmark = htdoc_make_bookmark; |
176 | doc->super.lookup_bookmark = htdoc_lookup_bookmark; |
177 | doc->super.count_pages = htdoc_count_pages; |
178 | doc->super.load_page = htdoc_load_page; |
179 | doc->super.lookup_metadata = htdoc_lookup_metadata; |
180 | doc->super.is_reflowable = 1; |
181 | |
182 | fz_try(ctx) |
183 | { |
184 | doc->zip = fz_open_directory(ctx, dirname); |
185 | doc->set = fz_new_html_font_set(ctx); |
186 | doc->html = fz_parse_html(ctx, doc->set, doc->zip, "." , buf, fz_user_css(ctx)); |
187 | doc->outline = fz_load_html_outline(ctx, doc->html); |
188 | } |
189 | fz_always(ctx) |
190 | fz_drop_buffer(ctx, buf); |
191 | fz_catch(ctx) |
192 | { |
193 | fz_drop_document(ctx, &doc->super); |
194 | fz_rethrow(ctx); |
195 | } |
196 | |
197 | return (fz_document*)doc; |
198 | } |
199 | |
200 | static fz_document * |
201 | htdoc_open_document_with_stream(fz_context *ctx, fz_stream *file) |
202 | { |
203 | return htdoc_open_document_with_buffer(ctx, "." , fz_read_all(ctx, file, 0)); |
204 | } |
205 | |
206 | static fz_document * |
207 | htdoc_open_document(fz_context *ctx, const char *filename) |
208 | { |
209 | char dirname[2048]; |
210 | fz_dirname(dirname, filename, sizeof dirname); |
211 | return htdoc_open_document_with_buffer(ctx, dirname, fz_read_file(ctx, filename)); |
212 | } |
213 | |
214 | static const char *htdoc_extensions[] = |
215 | { |
216 | "fb2" , |
217 | "htm" , |
218 | "html" , |
219 | "xhtml" , |
220 | "xml" , |
221 | NULL |
222 | }; |
223 | |
224 | static const char *htdoc_mimetypes[] = |
225 | { |
226 | "application/html+xml" , |
227 | "application/x-fictionbook" , |
228 | "application/xml" , |
229 | "text/xml" , |
230 | NULL |
231 | }; |
232 | |
233 | fz_document_handler html_document_handler = |
234 | { |
235 | NULL, |
236 | htdoc_open_document, |
237 | htdoc_open_document_with_stream, |
238 | htdoc_extensions, |
239 | htdoc_mimetypes |
240 | }; |
241 | |