outline.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. // Copyright (C) 2004-2025 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.h"
  23. fz_outline_item *fz_outline_iterator_item(fz_context *ctx, fz_outline_iterator *iter)
  24. {
  25. if (iter->item == NULL)
  26. return NULL;
  27. return iter->item(ctx, iter);
  28. }
  29. int fz_outline_iterator_next(fz_context *ctx, fz_outline_iterator *iter)
  30. {
  31. if (iter->next == NULL)
  32. return -1;
  33. return iter->next(ctx, iter);
  34. }
  35. int fz_outline_iterator_prev(fz_context *ctx, fz_outline_iterator *iter)
  36. {
  37. if (iter->prev == NULL)
  38. return -1;
  39. return iter->prev(ctx, iter);
  40. }
  41. int fz_outline_iterator_up(fz_context *ctx, fz_outline_iterator *iter)
  42. {
  43. if (iter->up == NULL)
  44. return -1;
  45. return iter->up(ctx, iter);
  46. }
  47. int fz_outline_iterator_down(fz_context *ctx, fz_outline_iterator *iter)
  48. {
  49. if (iter->down == NULL)
  50. return -1;
  51. return iter->down(ctx, iter);
  52. }
  53. int fz_outline_iterator_insert(fz_context *ctx, fz_outline_iterator *iter, fz_outline_item *item)
  54. {
  55. if (iter->insert == NULL)
  56. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Document type does not support Outline editing");
  57. return iter->insert(ctx, iter, item);
  58. }
  59. int fz_outline_iterator_delete(fz_context *ctx, fz_outline_iterator *iter)
  60. {
  61. if (iter->del == NULL)
  62. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Document type does not support Outline editing");
  63. return iter->del(ctx, iter);
  64. }
  65. void fz_outline_iterator_update(fz_context *ctx, fz_outline_iterator *iter, fz_outline_item *item)
  66. {
  67. if (iter->update == NULL)
  68. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Document type does not support Outline editing");
  69. iter->update(ctx, iter, item);
  70. }
  71. void fz_drop_outline_iterator(fz_context *ctx, fz_outline_iterator *iter)
  72. {
  73. if (iter == NULL)
  74. return;
  75. iter->drop(ctx, iter);
  76. fz_drop_document(ctx, iter->doc);
  77. fz_free(ctx, iter);
  78. }
  79. static void
  80. load_outline_sub(fz_context *ctx, fz_outline_iterator *iter, fz_outline **tail, char **t, char **u)
  81. {
  82. fz_outline_item *item;
  83. fz_outline *node, *onode;
  84. int res;
  85. do {
  86. item = fz_outline_iterator_item(ctx, iter);
  87. if (item == NULL)
  88. return;
  89. /* Duplicate title and uri first so we can recurse with limited try/catch. */
  90. *t = item->title == NULL ? NULL : fz_strdup(ctx, item->title);
  91. *u = item->uri == NULL ? NULL : fz_strdup(ctx, item->uri);
  92. node = fz_malloc_struct(ctx, fz_outline);
  93. node->is_open = item->is_open;
  94. node->refs = 1;
  95. node->title = *t;
  96. node->uri = *u;
  97. node->page.chapter = -1;
  98. node->page.page = -1;
  99. *t = NULL;
  100. *u = NULL;
  101. *tail = node;
  102. tail = &node->next;
  103. onode = node;
  104. node = NULL;
  105. onode->page = fz_resolve_link(ctx, iter->doc, onode->uri, &onode->x, &onode->y);
  106. res = fz_outline_iterator_down(ctx, iter);
  107. if (res == 0)
  108. load_outline_sub(ctx, iter, &onode->down, t, u);
  109. if (res >= 0)
  110. fz_outline_iterator_up(ctx, iter);
  111. }
  112. while (fz_outline_iterator_next(ctx, iter) == 0);
  113. }
  114. fz_outline *
  115. fz_load_outline_from_iterator(fz_context *ctx, fz_outline_iterator *iter)
  116. {
  117. fz_outline *head = NULL;
  118. fz_outline **tail = &head;
  119. char *title = NULL;
  120. char *uri = NULL;
  121. if (iter == NULL)
  122. return NULL;
  123. fz_try(ctx)
  124. load_outline_sub(ctx, iter, tail, &title, &uri);
  125. fz_always(ctx)
  126. fz_drop_outline_iterator(ctx, iter);
  127. fz_catch(ctx)
  128. {
  129. fz_drop_outline(ctx, head);
  130. fz_free(ctx, title);
  131. fz_free(ctx, uri);
  132. fz_rethrow(ctx);
  133. }
  134. return head;
  135. }
  136. fz_outline_iterator *fz_new_outline_iterator_of_size(fz_context *ctx, size_t size, fz_document *doc)
  137. {
  138. fz_outline_iterator *iter = fz_calloc(ctx, size, 1);
  139. iter->doc = fz_keep_document(ctx, doc);
  140. return iter;
  141. }
  142. fz_outline *
  143. fz_new_outline(fz_context *ctx)
  144. {
  145. fz_outline *outline = fz_malloc_struct(ctx, fz_outline);
  146. outline->refs = 1;
  147. return outline;
  148. }
  149. fz_outline *
  150. fz_keep_outline(fz_context *ctx, fz_outline *outline)
  151. {
  152. return fz_keep_imp(ctx, outline, &outline->refs);
  153. }
  154. void
  155. fz_drop_outline(fz_context *ctx, fz_outline *outline)
  156. {
  157. while (fz_drop_imp(ctx, outline, &outline->refs))
  158. {
  159. fz_outline *next = outline->next;
  160. fz_drop_outline(ctx, outline->down);
  161. fz_free(ctx, outline->title);
  162. fz_free(ctx, outline->uri);
  163. fz_free(ctx, outline);
  164. outline = next;
  165. }
  166. }
  167. typedef struct {
  168. fz_outline_iterator super;
  169. fz_outline *outline;
  170. fz_outline *current;
  171. fz_outline_item item;
  172. int down_max;
  173. int down_len;
  174. fz_outline **down_array;
  175. } fz_outline_iter_std;
  176. static int
  177. iter_std_down(fz_context *ctx, fz_outline_iterator *iter_)
  178. {
  179. fz_outline_iter_std *iter = (fz_outline_iter_std *)iter_;
  180. if (iter->current == NULL)
  181. return -1;
  182. if (iter->current->down == NULL)
  183. return -1;
  184. if (iter->down_max == iter->down_len)
  185. {
  186. int new_max = iter->down_max ? iter->down_max * 2 : 32;
  187. iter->down_array = fz_realloc_array(ctx, iter->down_array, new_max, fz_outline *);
  188. iter->down_max = new_max;
  189. }
  190. iter->down_array[iter->down_len++] = iter->current;
  191. iter->current = iter->current->down;
  192. return 0;
  193. }
  194. static int
  195. iter_std_up(fz_context *ctx, fz_outline_iterator *iter_)
  196. {
  197. fz_outline_iter_std *iter = (fz_outline_iter_std *)iter_;
  198. if (iter->current == NULL)
  199. return -1;
  200. if (iter->down_len == 0)
  201. return -1;
  202. iter->current = iter->down_array[--iter->down_len];
  203. return 0;
  204. }
  205. static int
  206. iter_std_next(fz_context *ctx, fz_outline_iterator *iter_)
  207. {
  208. fz_outline_iter_std *iter = (fz_outline_iter_std *)iter_;
  209. if (iter->current == NULL)
  210. return -1;
  211. if (iter->current->next == NULL)
  212. return -1;
  213. iter->current = iter->current->next;
  214. return 0;
  215. }
  216. static int
  217. iter_std_prev(fz_context *ctx, fz_outline_iterator *iter_)
  218. {
  219. fz_outline_iter_std *iter = (fz_outline_iter_std *)iter_;
  220. fz_outline *first;
  221. if (iter->current == NULL)
  222. return -1;
  223. first = iter->down_len == 0 ? iter->outline : iter->down_array[iter->down_len-1];
  224. if (iter->current == first)
  225. return -1;
  226. while (first->next != iter->current)
  227. first = first->next;
  228. iter->current = first;
  229. return 0;
  230. }
  231. static fz_outline_item *
  232. iter_std_item(fz_context *ctx, fz_outline_iterator *iter_)
  233. {
  234. fz_outline_iter_std *iter = (fz_outline_iter_std *)iter_;
  235. if (iter->current == NULL)
  236. return NULL;
  237. iter->item.is_open = iter->current->is_open;
  238. iter->item.title = iter->current->title;
  239. iter->item.uri = iter->current->uri;
  240. iter->item.r = iter->current->r / 255.0f;
  241. iter->item.g = iter->current->g / 255.0f;
  242. iter->item.b = iter->current->b / 255.0f;
  243. iter->item.flags = iter->current->flags;
  244. return &iter->item;
  245. }
  246. static void
  247. iter_std_drop(fz_context *ctx, fz_outline_iterator *iter_)
  248. {
  249. fz_outline_iter_std *iter = (fz_outline_iter_std *)iter_;
  250. if (iter == NULL)
  251. return;
  252. fz_drop_outline(ctx, iter->outline);
  253. fz_free(ctx, iter->down_array);
  254. }
  255. fz_outline_iterator *fz_outline_iterator_from_outline(fz_context *ctx, fz_outline *outline)
  256. {
  257. fz_outline_iter_std *iter;
  258. fz_try(ctx)
  259. {
  260. iter = fz_malloc_struct(ctx, fz_outline_iter_std);
  261. iter->super.down = iter_std_down;
  262. iter->super.up = iter_std_up;
  263. iter->super.next = iter_std_next;
  264. iter->super.prev = iter_std_prev;
  265. iter->super.item = iter_std_item;
  266. iter->super.drop = iter_std_drop;
  267. iter->outline = outline;
  268. iter->current = outline;
  269. }
  270. fz_catch(ctx)
  271. {
  272. fz_drop_outline(ctx, outline);
  273. fz_rethrow(ctx);
  274. }
  275. return &iter->super;
  276. }