storytest.c 14 KB

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