comparison mupdf-source/source/html/story-writer.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) 2022 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
24 #include "mupdf/fitz/story-writer.h"
25
26 #include <string.h>
27
28 /*
29 * Internal state for fz_write_story() to allow passing of page_num to
30 * positionfn.
31 */
32 typedef struct
33 {
34 fz_write_story_positionfn *positionfn;
35 void *positionfn_ref;
36 int page_num;
37 } positionfn_state;
38
39 /*
40 * Intermediate callback for fz_write_story() which calls the user-supplied
41 * callback.
42 */
43 static void positionfn_state_fn(fz_context *ctx, void *arg, const fz_story_element_position *position)
44 {
45 positionfn_state *state = arg;
46 fz_write_story_position do_position;
47 do_position.element = *position;
48 do_position.page_num = state->page_num;
49 state->positionfn(ctx, state->positionfn_ref, &do_position);
50 }
51
52 void fz_write_story(
53 fz_context *ctx,
54 fz_document_writer *writer,
55 fz_story *story,
56 fz_write_story_rectfn rectfn,
57 void *rectfn_ref,
58 fz_write_story_positionfn positionfn,
59 void *positionfn_ref,
60 fz_write_story_pagefn pagefn,
61 void *pagefn_ref
62 )
63 {
64 fz_rect filled = {0};
65 int rect_num = 0;
66 positionfn_state positionfn_state;
67
68 positionfn_state.positionfn = positionfn;
69 positionfn_state.positionfn_ref = positionfn_ref;
70 positionfn_state.page_num = 0;
71
72 fz_var(positionfn_state);
73
74 fz_try(ctx)
75 {
76 fz_device *dev = NULL;
77 for(;;)
78 {
79 fz_matrix ctm = fz_identity;
80 fz_rect rect;
81 fz_rect mediabox;
82 int newpage;
83 int more;
84
85 newpage = rectfn(ctx, rectfn_ref, rect_num, filled, &rect, &ctm, &mediabox);
86 rect_num += 1;
87 if (newpage)
88 positionfn_state.page_num += 1;
89
90 more = fz_place_story(ctx, story, rect, &filled);
91
92 if (positionfn)
93 {
94 fz_story_positions(ctx, story, positionfn_state_fn, &positionfn_state /*ref*/);
95 }
96
97 if (writer)
98 {
99 if (newpage)
100 {
101 if (dev)
102 {
103 if (pagefn)
104 pagefn(ctx, pagefn_ref, positionfn_state.page_num, mediabox, dev, 1 /*after*/);
105 fz_end_page(ctx, writer);
106 }
107 dev = fz_begin_page(ctx, writer, mediabox);
108 if (pagefn)
109 pagefn(ctx, pagefn_ref, positionfn_state.page_num, mediabox, dev, 0 /*after*/);
110 }
111 assert(dev);
112 fz_draw_story(ctx, story, dev, ctm);
113 if (!more)
114 {
115 if (pagefn)
116 pagefn(ctx, pagefn_ref, positionfn_state.page_num, mediabox, dev, 1 /*after*/);
117 fz_end_page(ctx, writer);
118 }
119 }
120 else
121 {
122 fz_draw_story(ctx, story, NULL /*dev*/, ctm);
123 }
124 if (!more)
125 break;
126 }
127 }
128 fz_always(ctx)
129 {
130 }
131 fz_catch(ctx)
132 {
133 fz_rethrow(ctx);
134 }
135 }
136
137 /*
138 * Copies a fz_write_story_position, taking care to use fz_strdup() for
139 * strings.
140 */
141 static void do_position_copy(fz_context *ctx, fz_write_story_position *to, const fz_write_story_position *from)
142 {
143 *to = *from;
144 to->element.id = NULL;
145 to->element.text = NULL;
146 if (from->element.id) to->element.id = fz_strdup(ctx, from->element.id);
147 if (from->element.text) to->element.text = fz_strdup(ctx, from->element.text);
148 }
149
150 static void positions_clear(fz_context *ctx, fz_write_story_positions *positions)
151 {
152 int i;
153 /* positions_clear() will have used fz_strdup() for strings, so free
154 them first: */
155 for (i=0; i<positions->num; ++i)
156 {
157 fz_free(ctx, (void*) positions->positions[i].element.id);
158 fz_free(ctx, (void*) positions->positions[i].element.text);
159 }
160 fz_free(ctx, positions->positions);
161 positions->positions = NULL;
162 positions->num = 0;
163 }
164
165 static int buffers_identical(fz_context *ctx, fz_buffer *a, fz_buffer *b)
166 {
167 size_t a_len;
168 size_t b_len;
169 unsigned char *a_data;
170 unsigned char *b_data;
171 a_len = fz_buffer_storage(ctx, a, &a_data);
172 b_len = fz_buffer_storage(ctx, b, &b_data);
173 return a_len == b_len && !memcmp(a_data, b_data, a_len);
174 }
175
176
177 static void stabilize_positionfn(fz_context *ctx, void *ref, const fz_write_story_position *position)
178 {
179 fz_write_story_positions *positions = ref;
180 /* Append <element> plus page_num to items->items[]. */
181 positions->positions = fz_realloc(ctx, positions->positions, sizeof(*positions->positions) * (positions->num + 1));
182 do_position_copy(ctx, &positions->positions[positions->num], position);
183 positions->num += 1;
184 }
185
186
187 void fz_write_stabilized_story(
188 fz_context *ctx,
189 fz_document_writer *writer,
190 const char *user_css,
191 float em,
192 fz_write_story_contentfn contentfn,
193 void *contentfn_ref,
194 fz_write_story_rectfn rectfn,
195 void *rectfn_ref,
196 fz_write_story_pagefn pagefn,
197 void *pagefn_ref,
198 fz_archive *zip
199 )
200 {
201 fz_write_story_positions positions = {0};
202 fz_story *story = NULL;
203 fz_buffer *content = NULL;
204 fz_buffer *content_prev = NULL;
205 int stable = 0;
206
207 positions.positions = NULL;
208 positions.num = 0;
209
210 fz_var(positions);
211 fz_var(story);
212 fz_var(content);
213
214 fz_try(ctx)
215 {
216 content = fz_new_buffer(ctx, 0 /*capacity*/);
217 content_prev = fz_new_buffer(ctx, 0 /*capacity*/);
218
219 /* Iterate until stable. */
220 for(;;)
221 {
222 /* Move <content> to <content_prev> and make <content>
223 contain new html from contentfn(). */
224 {
225 fz_buffer *content_tmp = content;
226 content = content_prev;
227 content_prev = content_tmp;
228 }
229 fz_clear_buffer(ctx, content);
230 contentfn(ctx, contentfn_ref, &positions, content);
231
232 if (buffers_identical(ctx, content, content_prev))
233 {
234 /* Content is unchanged, so this is the last iteration and we
235 will use <writer> not NULL. */
236 stable = 1;
237 }
238
239 /* Create story from new content. */
240 fz_drop_story(ctx, story);
241 story = NULL;
242 story = fz_new_story(ctx, content, user_css, em, zip);
243
244 /* Layout the story, gathering toc information as we go. */
245 positions_clear(ctx, &positions);
246 fz_write_story(
247 ctx,
248 (stable) ? writer : NULL,
249 story,
250 rectfn,
251 rectfn_ref,
252 stabilize_positionfn,
253 &positions /*positionfn_ref*/,
254 pagefn,
255 pagefn_ref
256 );
257
258 if (stable)
259 break;
260 }
261 }
262 fz_always(ctx)
263 {
264 fz_drop_story(ctx, story);
265 fz_drop_buffer(ctx, content);
266 fz_drop_buffer(ctx, content_prev);
267 positions_clear(ctx, &positions);
268 }
269 fz_catch(ctx)
270 {
271 fz_rethrow(ctx);
272 }
273 }