story-writer.c 6.7 KB

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