comparison mupdf-source/docs/examples/storytest.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 #include "mupdf/fitz.h"
2 #include "mupdf/pdf.h"
3
4 #include <string.h>
5 #include <memory.h>
6
7 const char snark[] =
8 "<!DOCTYPE html>"
9 "<style>"
10 "#a { margin: 30px; }"
11 "#b { margin: 20px; }"
12 "#c { margin: 5px; }"
13 "#a { border: 1px solid red; }"
14 "#b { border: 1px solid green; }"
15 "#c { border: 1px solid blue; }"
16 "</style>"
17 "<body>"
18 "<div id=\"a\">"
19 "A"
20 "</div>"
21 "<div id=\"b\">"
22 "<div id=\"c\">"
23 "C"
24 "</div>"
25 "</div>"
26 "<div style=\"text-align:center;\"><IMG width=\"300\" src=\"docs/examples/SnarkFront.svg\"></div>"
27 "<H1>The hunting of the Snark</H1>"
28 "<div style=\"text-align:center\"><IMG width=\"30\" src=\"docs/examples/huntingofthesnark.png\"></div>"
29 "<p>\"Just the place for a Snark!\" the Bellman cried,<br>"
30 "As he landed his crew with care;<br>"
31 "Supporting each man on the top of the tide<br>"
32 "By a finger entwined in his hair.</p>"
33
34 "<P>Just the place for a Snark! I have said it twice:<br>"
35 "That alone should encourage the crew.<br>"
36 "Just the place for a Snark! I have said it thrice:<br>"
37 "What I tell you three times is true.</p>"
38
39 "<p>The crew was complete: it included a Boots-<br>"
40 "A maker of Bonnets and Hoods-<br>"
41 "A Barrister, brought to arrange their disputes-<br>"
42 "And a Broker, to value their goods.</p>"
43
44 "<p>A Billiard-marker, whose skill was immense,<br>"
45 "Might perhaps have won more than his share-<br>"
46 "But a Banker, engaged at enormous expense,<br>"
47 "Had the whole of their cash in his care.</p>"
48
49 "<p>There was also a Beaver, that paced on the deck,<br>"
50 "Or would sit making lace in the bow:<br>"
51 "And had often (the Bellman said) saved them from wreck,<br>"
52 "Though none of the sailors knew how.</p>"
53
54 "<p>There was one who was famed for the number of things<br>"
55 "He forgot when he entered the ship:<br>"
56 "His umbrella, his watch, all his jewels and rings,<br>"
57 "And the clothes he had bought for the trip.</p>"
58 "<div id=\"a\">"
59 "<p>He had forty-two boxes, all carefully packed,<br>"
60 "With his name painted clearly on each:<br>"
61 "But, since he omitted to mention the fact,<br>"
62 "They were all left behind on the beach.</p>"
63 "</div>"
64
65 "<p>The loss of his clothes hardly mattered, because<br>"
66 "He had seven coats on when he came,<br>"
67 "With three pair of boots-but the worst of it was,<br>"
68 "He had wholly forgotten his name.</p>"
69
70 "<p>He would answer to \"Hi!\" or to any loud cry,<br>"
71 "Such as \"Fry me!\" or \"Fritter my wig!\"<br>"
72 "To \"What-you-may-call-um!\" or \"What-was-his-name!\"<br>"
73 "But especially \"Thing-um-a-jig!\"</p>"
74
75 "<p>While, for those who preferred a more forcible word,<br>"
76 "He had different names from these:<br>"
77 "His intimate friends called him \"Candle-ends,\"<br>"
78 "And his enemies \"Toasted-cheese.\"</p>"
79
80 "<p>\"His form is ungainly-his intellect small-\"<br>"
81 "(So the Bellman would often remark)<br>"
82 "\"But his courage is perfect! And that, after all,<br>"
83 "Is the thing that one needs with a Snark.\"</p>"
84
85 "<p>He would joke with hyenas, returning their stare<br>"
86 "With an impudent wag of the head:<br>"
87 "And he once went a walk, paw-in-paw, with a bear,<br>"
88 "\"Just to keep up its spirits,\" he said.</p>"
89
90 "<p>He came as a Baker: but owned, when too late-<br>"
91 "And it drove the poor Bellman half-mad-<br>"
92 "He could only bake Bride-cake-for which, I may state,<br>"
93 "No materials were to be had.</p>"
94
95 "<p>The last of the crew needs especial remark,<br>"
96 "Though he looked an incredible dunce:<br>"
97 "He had just one idea-but, that one being \"Snark,\"<br>"
98 "The good Bellman engaged him at once.</p>"
99
100 "<p>He came as a Butcher: but gravely declared,<br>"
101 "When the ship had been sailing a week,<br>"
102 "He could only kill Beavers. The Bellman looked scared,<br>"
103 "And was almost too frightened to speak:</p>"
104
105 "<p>But at length he explained, in a tremulous tone,<br>"
106 "There was only one Beaver on board;<br>"
107 "And that was a tame one he had of his own,<br>"
108 "Whose death would be deeply deplored.</p>"
109
110 "<div id=\"b\">"
111 "<p>The Beaver, who happened to hear the remark,<br>"
112 "Protested, with tears in its eyes,<br>"
113 "That not even the rapture of hunting the Snark<br>"
114 "Could atone for that dismal surprise!</p>"
115 "</div>"
116
117 "<p style=\"-mupdf-leading:7pt;\">It strongly advised that the Butcher should be<br>"
118 "Conveyed in a separate ship:<br>"
119 "But the Bellman declared that would never agree<br>"
120 "With the plans he had made for the trip:</p>"
121
122 "<p style=\"-mupdf-leading:11pt;\">Navigation was always a difficult art,<br>"
123 "Though with only one ship and one bell:<br>"
124 "And he feared he must really decline, for his part,<br>"
125 "Undertaking another as well.</p>"
126
127 "<p style=\"-mupdf-leading:15pt;\">The Beaver's best course was, no doubt, to procure<br>"
128 "A second-hand dagger-proof coat-<br>"
129 "So the Baker advised it-and next, to insure<br>"
130 "Its life in some Office of note:</p>"
131
132 "<p style=\"-mupdf-leading:20pt;\">This the Banker suggested, and offered for hire<br>"
133 "(On moderate terms), or for sale,<br>"
134 "Two excellent Policies, one Against Fire,<br>"
135 "And one Against Damage From Hail.</p>"
136
137 "<p style=\"-mupdf-leading:30pt;\">Yet still, ever after that sorrowful day,<br>"
138 "Whenever the Butcher was by,<br>"
139 "The Beaver kept looking the opposite way,<br>"
140 "And appeared unaccountably shy.</p>"
141 ;
142
143 #define MAX_CAST_MEMBERS 32
144
145 typedef struct {
146 const char *title;
147 const char *director;
148 const char *year;
149 const char *cast[MAX_CAST_MEMBERS];
150 } film_t;
151
152 static const film_t films[] =
153 {
154 {
155 "Pulp Fiction",
156 "Quentin Tarantino",
157 "1994",
158 {
159 "John Travolta",
160 "Samuel L Jackson",
161 "Uma Thurman",
162 "Bruce Willis",
163 "Ving Rhames",
164 "Harvey Keitel",
165 "Tim Roth",
166 "Bridget Fonda"
167 }
168 },
169
170 {
171 "The Usual Suspects",
172 "Bryan Singer",
173 "1995",
174 {
175 "Kevin Spacey",
176 "Gabriel Byrne",
177 "Chazz Palminteri",
178 "Benicio Del Toro",
179 "Kevin Pollak",
180 "Pete Postlethwaite",
181 "Steven Baldwin"
182 }
183
184 },
185
186 {
187 "Fight Club",
188 "David Fincher",
189 "1999",
190 {
191 "Brad Pitt",
192 "Edward Norton",
193 "Helena Bonham Carter"
194 }
195 }
196 };
197
198 const char *festival_template =
199 "<html><head><title>Why do we have a title? Why not?</title></head>"
200 "<body><h1 style=\"text-align:center\">Hook Norton Film Festival</h1>"
201 "<ol>"
202 "<li id=\"filmtemplate\">"
203 "<b id=\"filmtitle\"></b>"
204 "<dl>"
205 "<dt>Director<dd id=\"director\">"
206 "<dt>Release Year<dd id=\"filmyear\">"
207 "<dt>Cast<dd id=\"cast\">"
208 "</dl>"
209 "</li>"
210 "<ul>"
211 "</body></html";
212
213
214 static int toc_rectfn(fz_context *ctx, void *ref, int num, fz_rect filled, fz_rect *rect, fz_matrix *ctm, fz_rect *mediabox)
215 {
216 if (num == 0)
217 {
218 rect->x0 = 100;
219 rect->y0 = 200;
220 rect->x1 = 175;
221 rect->y1 = 300;
222 }
223 else
224 {
225 rect->x0 = 10;
226 rect->y0 = 50;
227 rect->x1 = 290;
228 rect->y1 = 350;
229 }
230 mediabox->x0 = 0;
231 mediabox->y0 = 0;
232 mediabox->x1 = 300;
233 mediabox->y1 = 400;
234 return 1;
235 }
236
237 static void toc_contentfn(fz_context *ctx, void *ref, const fz_write_story_positions *positions, fz_buffer *buffer)
238 {
239 int i;
240 fz_append_string(ctx, buffer,
241 "<!DOCTYPE html>\n"
242 "<style>\n"
243 "#a { margin: 30px; }\n"
244 "#b { margin: 20px; }\n"
245 "#c { margin: 5px; }\n"
246 "#a { border: 1px solid red; }\n"
247 "#b { border: 1px solid green; }\n"
248 "#c { border: 1px solid blue; }\n"
249 "</style>\n"
250 "<body>\n"
251 "<h2>Contents</h2>\n"
252 "<ol>\n"
253 );
254 for (i=0; i<positions->num; ++i)
255 {
256 fz_write_story_position *position = &positions->positions[i];
257
258 fz_append_printf(ctx, buffer,
259 " <li>page=%i depth=%i heading=%i id='%s' rect=(%f %f %f %f) text='<b>%s</b>' open_close=%i\n",
260 position->page_num,
261 position->element.depth,
262 position->element.heading,
263 (position->element.id) ? position->element.id : "",
264 position->element.rect.x0,
265 position->element.rect.y0,
266 position->element.rect.x1,
267 position->element.rect.y1,
268 (position->element.text) ? position->element.text : "",
269 position->element.open_close
270 );
271 }
272 fz_append_string(ctx, buffer, "</ol>\n");
273 fz_append_string(ctx, buffer,
274 "<h1>Section the first</h1>\n"
275 "<p>Blah.\n"
276 "<h1>Section the second</h1>\n"
277 "<p>Blah blah.\n"
278 "<h2>Subsection</h2>\n"
279 "<p>Blah blah blah.\n"
280 "<p>Blah blah blah.\n"
281 "<p>Blah blah blah.\n"
282 "<h1>Section the third</h1>\n"
283 "<p>Blah blah.\n"
284 "</body>\n"
285 );
286 {
287 const char *data = fz_string_from_buffer(ctx, buffer);
288 printf( "======== Html content: ========\n");
289 printf( "%s", data);
290 printf( "========\n");
291 }
292 }
293
294 static void toc_pagefn(fz_context *ctx, void *ref, int page_num, fz_rect mediabox, fz_device *dev, int after)
295 {
296 fz_path *path = fz_new_path(ctx);
297 fz_matrix ctm = fz_identity;
298 printf("toc_pagefn(): ref=%p page_num=%i dev=%p after=%i\n", ref, page_num, dev, after);
299 if (after)
300 {
301 float rgb[3] = { 0.75, 0.25, 0.125};
302 fz_moveto(ctx, path, 50, 50);
303 fz_lineto(ctx, path, 100, 200);
304 fz_lineto(ctx, path, 50, 200);
305 fz_closepath(ctx, path);
306 fz_fill_path(ctx, dev, path, 0, ctm, fz_device_rgb(ctx), rgb, 0.9f /*alpha*/, fz_default_color_params);
307 fz_drop_path(ctx, path);
308 }
309 else
310 {
311 float rgb[3] = { 0.125, 0.25, 0.75};
312 fz_moveto(ctx, path, 50, 50);
313 fz_lineto(ctx, path, 50, 200);
314 fz_lineto(ctx, path, 100, 50);
315 fz_closepath(ctx, path);
316 fz_fill_path(ctx, dev, path, 0, ctm, fz_device_rgb(ctx), rgb, 0.9f /*alpha*/, fz_default_color_params);
317 fz_drop_path(ctx, path);
318 }
319 }
320
321 static void test_write_stabilized_story(fz_context *ctx)
322 {
323 fz_document_writer *writer = fz_new_pdf_writer(ctx, "out_toc.pdf", "");
324 fz_write_stabilized_story(
325 ctx,
326 writer,
327 "" /*user_css*/,
328 11 /*em*/,
329 toc_contentfn,
330 NULL /*contentfn_ref*/,
331 toc_rectfn,
332 NULL /*rectfn_ref*/,
333 toc_pagefn /*pagefn*/,
334 NULL /*pagefn_ref*/,
335 NULL /* archive */
336 );
337 fz_close_document_writer(ctx, writer);
338 fz_drop_document_writer(ctx, writer);
339 }
340
341 int main(int argc, const char *argv[])
342 {
343 fz_context *ctx;
344 fz_document_writer *writer = NULL;
345 fz_story *story = NULL;
346 fz_buffer *buf = NULL;
347 fz_device *dev = NULL;
348 fz_archive *archive = NULL;
349 fz_rect mediabox = { 0, 0, 512, 640 };
350 float margin = 10;
351 int more;
352
353 ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT);
354 if (ctx == NULL)
355 {
356 fprintf(stderr, "Failed to create context");
357 return 1;
358 }
359
360 fz_var(writer);
361 fz_var(story);
362 fz_var(buf);
363 fz_var(dev);
364 fz_var(archive);
365
366 /* First one made with precooked content. */
367 fz_try(ctx)
368 {
369 writer = fz_new_pdf_writer(ctx, "out.pdf", "");
370
371 buf = fz_new_buffer_from_copied_data(ctx, (unsigned char *)snark, strlen(snark)+1);
372
373 archive = fz_open_directory(ctx, ".");
374
375 story = fz_new_story(ctx, buf, "", 11, archive);
376
377 do
378 {
379 fz_rect where;
380 fz_rect filled;
381
382 where.x0 = mediabox.x0 + margin;
383 where.y0 = mediabox.y0 + margin;
384 where.x1 = mediabox.x1 - margin;
385 where.y1 = mediabox.y1 - margin;
386
387 dev = fz_begin_page(ctx, writer, mediabox);
388
389 more = fz_place_story(ctx, story, where, &filled);
390
391 fz_draw_story(ctx, story, dev, fz_identity);
392
393 fz_end_page(ctx, writer);
394 }
395 while (more);
396
397 fz_close_document_writer(ctx, writer);
398 }
399 fz_always(ctx)
400 {
401 fz_drop_story(ctx, story);
402 fz_drop_buffer(ctx, buf);
403 fz_drop_document_writer(ctx, writer);
404 fz_drop_archive(ctx, archive);
405 }
406 fz_catch(ctx)
407 {
408 fz_report_error(ctx);
409 }
410
411 /* Now one made with programmatic content. */
412 writer = NULL;
413 buf = NULL;
414 story = NULL;
415 dev = NULL;
416
417 fz_try(ctx)
418 {
419 fz_xml *dom, *body, *tmp;
420
421 writer = fz_new_pdf_writer(ctx, "out2.pdf", "");
422
423 story = fz_new_story(ctx, NULL, "", 11, NULL);
424
425 dom = fz_story_document(ctx, story);
426
427 body = fz_dom_body(ctx, dom);
428
429 fz_dom_append_child(ctx, body, fz_dom_create_text_node(ctx, dom, "This is some text."));
430 tmp = fz_dom_create_element(ctx, dom, "b");
431 fz_dom_append_child(ctx, body, tmp);
432 fz_dom_append_child(ctx, tmp, fz_dom_create_text_node(ctx, dom, "This is some bold text."));
433 fz_dom_append_child(ctx, body, fz_dom_create_text_node(ctx, dom, "This is some normal text."));
434
435 do
436 {
437 fz_rect where;
438 fz_rect filled;
439
440 where.x0 = mediabox.x0 + margin;
441 where.y0 = mediabox.y0 + margin;
442 where.x1 = mediabox.x1 - margin;
443 where.y1 = mediabox.y1 - margin;
444
445 dev = fz_begin_page(ctx, writer, mediabox);
446
447 more = fz_place_story(ctx, story, where, &filled);
448
449 fz_draw_story(ctx, story, dev, fz_identity);
450
451 fz_end_page(ctx, writer);
452 }
453 while (more);
454
455 fz_close_document_writer(ctx, writer);
456 }
457 fz_always(ctx)
458 {
459 fz_drop_story(ctx, story);
460 fz_drop_buffer(ctx, buf);
461 fz_drop_document_writer(ctx, writer);
462 }
463 fz_catch(ctx)
464 {
465 fz_report_error(ctx);
466 }
467
468 /* Now a combination of the two. */
469 writer = NULL;
470 buf = NULL;
471 story = NULL;
472 dev = NULL;
473
474 fz_try(ctx)
475 {
476 fz_xml *dom, *body, *tmp, *templat;
477 int i, j;
478
479 writer = fz_new_pdf_writer(ctx, "out3.pdf", "");
480
481 buf = fz_new_buffer_from_copied_data(ctx, (unsigned char *)festival_template, strlen(festival_template)+1);
482 story = fz_new_story(ctx, buf, "", 11, NULL);
483
484 dom = fz_story_document(ctx, story);
485
486 body = fz_dom_body(ctx, dom);
487
488 templat = fz_dom_find(ctx, body, NULL, "id", "filmtemplate");
489
490 for (i = 0; i < nelem(films); i++)
491 {
492 fz_xml *film = fz_dom_clone(ctx, templat);
493
494 /* Now fill in some of the template. */
495 tmp = fz_dom_find(ctx, film, NULL, "id", "filmtitle");
496 fz_dom_append_child(ctx, tmp, fz_dom_create_text_node(ctx, dom, films[i].title));
497
498 tmp = fz_dom_find(ctx, film, NULL, "id", "director");
499 fz_dom_append_child(ctx, tmp, fz_dom_create_text_node(ctx, dom, films[i].director));
500
501 tmp = fz_dom_find(ctx, film, NULL, "id", "filmyear");
502 fz_dom_append_child(ctx, tmp, fz_dom_create_text_node(ctx, dom, films[i].year));
503
504 tmp = fz_dom_find(ctx, film, NULL, "id", "cast");
505 for (j = 0; j < MAX_CAST_MEMBERS; j++)
506 {
507 if (films[i].cast[j] == NULL)
508 break;
509 fz_dom_append_child(ctx, tmp, fz_dom_create_text_node(ctx, dom, films[i].cast[j]));
510 fz_dom_append_child(ctx, tmp, fz_dom_create_element(ctx, dom, "br"));
511 }
512
513 fz_dom_append_child(ctx, fz_dom_parent(ctx, templat), film);
514 }
515
516 /* Remove the template. */
517 fz_dom_remove(ctx, templat);
518
519 do
520 {
521 fz_rect where;
522 fz_rect filled;
523
524 where.x0 = mediabox.x0 + margin;
525 where.y0 = mediabox.y0 + margin;
526 where.x1 = mediabox.x1 - margin;
527 where.y1 = mediabox.y1 - margin;
528
529 dev = fz_begin_page(ctx, writer, mediabox);
530
531 more = fz_place_story(ctx, story, where, &filled);
532
533 fz_draw_story(ctx, story, dev, fz_identity);
534
535 fz_end_page(ctx, writer);
536 }
537 while (more);
538
539 fz_close_document_writer(ctx, writer);
540 }
541 fz_always(ctx)
542 {
543 fz_drop_story(ctx, story);
544 fz_drop_buffer(ctx, buf);
545 fz_drop_document_writer(ctx, writer);
546 }
547 fz_catch(ctx)
548 {
549 fz_report_error(ctx);
550 }
551
552 test_write_stabilized_story(ctx);
553
554 fz_drop_context(ctx);
555
556 return 0;
557 }