pdf-outline.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  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. #include "mupdf/pdf.h"
  24. #include "pdf-annot-imp.h"
  25. #include <string.h>
  26. #include <math.h>
  27. /*
  28. The URI encoding format broadly follows that described in
  29. "Parameters for Opening PDF files" from the Adobe Acrobat SDK,
  30. version 8.1, which can, at the time of writing, be found here:
  31. https://web.archive.org/web/20170921000830/http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_open_parameters.pdf
  32. */
  33. static void
  34. pdf_test_outline(fz_context *ctx, pdf_document *doc, pdf_obj *dict, pdf_mark_bits *marks, pdf_obj *parent, int *fixed)
  35. {
  36. int parent_diff, prev_diff, last_diff;
  37. pdf_obj *first, *last, *next, *prev;
  38. pdf_obj *expected_parent = parent;
  39. pdf_obj *expected_prev = NULL;
  40. last = pdf_dict_get(ctx, expected_parent, PDF_NAME(Last));
  41. while (dict && pdf_is_dict(ctx, dict))
  42. {
  43. if (pdf_mark_bits_set(ctx, marks, dict))
  44. fz_throw(ctx, FZ_ERROR_FORMAT, "Cycle detected in outlines");
  45. parent = pdf_dict_get(ctx, dict, PDF_NAME(Parent));
  46. prev = pdf_dict_get(ctx, dict, PDF_NAME(Prev));
  47. next = pdf_dict_get(ctx, dict, PDF_NAME(Next));
  48. parent_diff = pdf_objcmp(ctx, parent, expected_parent);
  49. prev_diff = pdf_objcmp(ctx, prev, expected_prev);
  50. last_diff = next == NULL && pdf_objcmp_resolve(ctx, last, dict);
  51. if (fixed == NULL)
  52. {
  53. if (parent_diff)
  54. fz_throw(ctx, FZ_ERROR_FORMAT, "Outline parent pointer still bad or missing despite repair");
  55. if (prev_diff)
  56. fz_throw(ctx, FZ_ERROR_FORMAT, "Outline prev pointer still bad or missing despite repair");
  57. if (last_diff)
  58. fz_throw(ctx, FZ_ERROR_FORMAT, "Outline last pointer still bad or missing despite repair");
  59. }
  60. else if (parent_diff || prev_diff || last_diff)
  61. {
  62. if (*fixed == 0)
  63. pdf_begin_operation(ctx, doc, "Repair outline nodes");
  64. *fixed = 1;
  65. doc->non_structural_change = 1;
  66. fz_try(ctx)
  67. {
  68. if (parent_diff)
  69. {
  70. fz_warn(ctx, "Bad or missing parent pointer in outline tree, repairing");
  71. pdf_dict_put(ctx, dict, PDF_NAME(Parent), expected_parent);
  72. }
  73. if (prev_diff)
  74. {
  75. fz_warn(ctx, "Bad or missing prev pointer in outline tree, repairing");
  76. if (expected_prev)
  77. pdf_dict_put(ctx, dict, PDF_NAME(Prev), expected_prev);
  78. else
  79. pdf_dict_del(ctx, dict, PDF_NAME(Prev));
  80. }
  81. if (last_diff)
  82. {
  83. fz_warn(ctx, "Bad or missing last pointer in outline tree, repairing");
  84. pdf_dict_put(ctx, expected_parent, PDF_NAME(Last), dict);
  85. }
  86. }
  87. fz_always(ctx)
  88. doc->non_structural_change = 0;
  89. fz_catch(ctx)
  90. fz_rethrow(ctx);
  91. }
  92. first = pdf_dict_get(ctx, dict, PDF_NAME(First));
  93. if (first)
  94. pdf_test_outline(ctx, doc, first, marks, dict, fixed);
  95. expected_prev = dict;
  96. dict = next;
  97. }
  98. }
  99. fz_outline *
  100. pdf_load_outline(fz_context *ctx, pdf_document *doc)
  101. {
  102. /* Just appeal to the fz_ level. */
  103. return fz_load_outline(ctx, (fz_document *)doc);
  104. }
  105. enum {
  106. MOD_NONE = 0,
  107. MOD_BELOW = 1,
  108. MOD_AFTER = 2
  109. };
  110. typedef struct pdf_outline_iterator {
  111. fz_outline_iterator super;
  112. fz_outline_item item;
  113. pdf_obj *current;
  114. int modifier;
  115. } pdf_outline_iterator;
  116. static int
  117. pdf_outline_iterator_next(fz_context *ctx, fz_outline_iterator *iter_)
  118. {
  119. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  120. pdf_obj *next;
  121. if (iter->modifier != MOD_NONE || iter->current == NULL)
  122. return -1;
  123. next = pdf_dict_get(ctx, iter->current, PDF_NAME(Next));
  124. if (next == NULL)
  125. {
  126. iter->modifier = MOD_AFTER;
  127. return 1;
  128. }
  129. iter->modifier = MOD_NONE;
  130. iter->current = next;
  131. return 0;
  132. }
  133. static int
  134. pdf_outline_iterator_prev(fz_context *ctx, fz_outline_iterator *iter_)
  135. {
  136. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  137. pdf_obj *prev;
  138. if (iter->modifier == MOD_BELOW || iter->current == NULL)
  139. return -1;
  140. if (iter->modifier == MOD_AFTER)
  141. {
  142. iter->modifier = MOD_NONE;
  143. return 0;
  144. }
  145. prev = pdf_dict_get(ctx, iter->current, PDF_NAME(Prev));
  146. if (prev == NULL)
  147. return -1;
  148. iter->modifier = MOD_NONE;
  149. iter->current = prev;
  150. return 0;
  151. }
  152. static int
  153. pdf_outline_iterator_up(fz_context *ctx, fz_outline_iterator *iter_)
  154. {
  155. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  156. pdf_obj *up;
  157. pdf_obj *grandparent;
  158. if (iter->current == NULL)
  159. return -1;
  160. if (iter->modifier == MOD_BELOW)
  161. {
  162. iter->modifier = MOD_NONE;
  163. return 0;
  164. }
  165. /* The topmost level still has a parent pointer, just one
  166. * that points to the outlines object. We never want to
  167. * allow us to move 'up' onto the outlines object. */
  168. up = pdf_dict_get(ctx, iter->current, PDF_NAME(Parent));
  169. if (up == NULL)
  170. /* This should never happen! */
  171. return -1;
  172. grandparent = pdf_dict_get(ctx, up, PDF_NAME(Parent));
  173. if (grandparent == NULL)
  174. return -1;
  175. iter->modifier = MOD_NONE;
  176. iter->current = up;
  177. return 0;
  178. }
  179. static int
  180. pdf_outline_iterator_down(fz_context *ctx, fz_outline_iterator *iter_)
  181. {
  182. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  183. pdf_obj *down;
  184. if (iter->modifier != MOD_NONE || iter->current == NULL)
  185. return -1;
  186. down = pdf_dict_get(ctx, iter->current, PDF_NAME(First));
  187. if (down == NULL)
  188. {
  189. iter->modifier = MOD_BELOW;
  190. return 1;
  191. }
  192. iter->modifier = MOD_NONE;
  193. iter->current = down;
  194. return 0;
  195. }
  196. static void
  197. do_outline_update(fz_context *ctx, pdf_obj *obj, fz_outline_item *item, int is_new_node)
  198. {
  199. int count;
  200. int open_delta = 0;
  201. pdf_obj *parent;
  202. /* If the open/closed state changes, update. */
  203. count = pdf_dict_get_int(ctx, obj, PDF_NAME(Count));
  204. if ((count < 0 && item->is_open) || (count > 0 && !item->is_open))
  205. {
  206. pdf_dict_put_int(ctx, obj, PDF_NAME(Count), -count);
  207. open_delta = -count;
  208. }
  209. else if (is_new_node)
  210. open_delta = 1;
  211. parent = pdf_dict_get(ctx, obj, PDF_NAME(Parent));
  212. while (parent)
  213. {
  214. pdf_obj *cobj = pdf_dict_get(ctx, parent, PDF_NAME(Count));
  215. count = pdf_to_int(ctx, cobj);
  216. if (open_delta || cobj == NULL)
  217. pdf_dict_put_int(ctx, parent, PDF_NAME(Count), count > 0 ? count + open_delta : count - open_delta);
  218. if (count < 0)
  219. break;
  220. parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent));
  221. }
  222. if (item->title)
  223. pdf_dict_put_text_string(ctx, obj, PDF_NAME(Title), item->title);
  224. else
  225. pdf_dict_del(ctx, obj, PDF_NAME(Title));
  226. pdf_dict_del(ctx, obj, PDF_NAME(A));
  227. pdf_dict_del(ctx, obj, PDF_NAME(C));
  228. pdf_dict_del(ctx, obj, PDF_NAME(F));
  229. pdf_dict_del(ctx, obj, PDF_NAME(Dest));
  230. if (item->uri)
  231. {
  232. pdf_document *doc = pdf_get_bound_document(ctx, obj);
  233. if (item->uri[0] == '#')
  234. pdf_dict_put_drop(ctx, obj, PDF_NAME(Dest),
  235. pdf_new_dest_from_link(ctx, doc, item->uri, 0));
  236. else if (!strncmp(item->uri, "file:", 5))
  237. pdf_dict_put_drop(ctx, obj, PDF_NAME(Dest),
  238. pdf_new_dest_from_link(ctx, doc, item->uri, 1));
  239. else
  240. pdf_dict_put_drop(ctx, obj, PDF_NAME(A),
  241. pdf_new_action_from_link(ctx, doc, item->uri));
  242. }
  243. if (item->r != 0 || item->g != 0 || item->b != 0)
  244. {
  245. pdf_obj *color = pdf_dict_put_array(ctx, obj, PDF_NAME(C), 3);
  246. pdf_array_put_real(ctx, color, 0, item->r / 255.0);
  247. pdf_array_put_real(ctx, color, 1, item->g / 255.0);
  248. pdf_array_put_real(ctx, color, 2, item->b / 255.0);
  249. }
  250. if (item->flags != 0)
  251. pdf_dict_put_int(ctx, obj, PDF_NAME(F), item->flags);
  252. }
  253. static int
  254. pdf_outline_iterator_insert(fz_context *ctx, fz_outline_iterator *iter_, fz_outline_item *item)
  255. {
  256. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  257. pdf_document *doc = (pdf_document *)iter->super.doc;
  258. pdf_obj *obj = NULL;
  259. pdf_obj *prev;
  260. pdf_obj *parent;
  261. pdf_obj *outlines = NULL;
  262. pdf_obj *newoutlines = NULL;
  263. int result = 0;
  264. fz_var(obj);
  265. fz_var(newoutlines);
  266. pdf_begin_operation(ctx, doc, "Insert outline item");
  267. fz_try(ctx)
  268. {
  269. obj = pdf_add_new_dict(ctx, doc, 4);
  270. if (iter->modifier == MOD_BELOW)
  271. parent = iter->current;
  272. else if (iter->modifier == MOD_NONE && iter->current == NULL)
  273. {
  274. pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
  275. outlines = pdf_dict_get(ctx, root, PDF_NAME(Outlines));
  276. if (outlines == NULL)
  277. {
  278. /* No outlines entry, better make one. */
  279. newoutlines = outlines = pdf_add_new_dict(ctx, doc, 4);
  280. pdf_dict_put(ctx, root, PDF_NAME(Outlines), outlines);
  281. pdf_dict_put(ctx, outlines, PDF_NAME(Type), PDF_NAME(Outlines));
  282. }
  283. iter->modifier = MOD_BELOW;
  284. iter->current = outlines;
  285. parent = outlines;
  286. }
  287. else
  288. parent = pdf_dict_get(ctx, iter->current, PDF_NAME(Parent));
  289. pdf_dict_put(ctx, obj, PDF_NAME(Parent), parent);
  290. do_outline_update(ctx, obj, item, 1);
  291. switch (iter->modifier)
  292. {
  293. case MOD_BELOW:
  294. pdf_dict_put(ctx, iter->current, PDF_NAME(First), obj);
  295. pdf_dict_put(ctx, iter->current, PDF_NAME(Last), obj);
  296. iter->current = obj;
  297. iter->modifier = MOD_AFTER;
  298. result = 1;
  299. break;
  300. case MOD_AFTER:
  301. pdf_dict_put(ctx, obj, PDF_NAME(Prev), iter->current);
  302. pdf_dict_put(ctx, iter->current, PDF_NAME(Next), obj);
  303. pdf_dict_put(ctx, parent, PDF_NAME(Last), obj);
  304. iter->current = obj;
  305. result = 1;
  306. break;
  307. default:
  308. prev = pdf_dict_get(ctx, iter->current, PDF_NAME(Prev));
  309. if (prev)
  310. {
  311. pdf_dict_put(ctx, prev, PDF_NAME(Next), obj);
  312. pdf_dict_put(ctx, obj, PDF_NAME(Prev), prev);
  313. }
  314. else
  315. pdf_dict_put(ctx, parent, PDF_NAME(First), obj);
  316. pdf_dict_put(ctx, iter->current, PDF_NAME(Prev), obj);
  317. pdf_dict_put(ctx, obj, PDF_NAME(Next), iter->current);
  318. result = 0;
  319. break;
  320. }
  321. pdf_end_operation(ctx, doc);
  322. }
  323. fz_always(ctx)
  324. {
  325. pdf_drop_obj(ctx, obj);
  326. pdf_drop_obj(ctx, newoutlines);
  327. }
  328. fz_catch(ctx)
  329. {
  330. pdf_abandon_operation(ctx, doc);
  331. fz_rethrow(ctx);
  332. }
  333. return result;
  334. }
  335. static void
  336. pdf_outline_iterator_update(fz_context *ctx, fz_outline_iterator *iter_, fz_outline_item *item)
  337. {
  338. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  339. pdf_document *doc = (pdf_document *)iter->super.doc;
  340. if (iter->modifier != MOD_NONE || iter->current == NULL)
  341. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't update a non-existent outline item!");
  342. pdf_begin_operation(ctx, doc, "Update outline item");
  343. fz_try(ctx)
  344. {
  345. do_outline_update(ctx, iter->current, item, 0);
  346. pdf_end_operation(ctx, doc);
  347. }
  348. fz_catch(ctx)
  349. {
  350. pdf_abandon_operation(ctx, doc);
  351. fz_rethrow(ctx);
  352. }
  353. }
  354. static int
  355. pdf_outline_iterator_del(fz_context *ctx, fz_outline_iterator *iter_)
  356. {
  357. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  358. pdf_document *doc = (pdf_document *)iter->super.doc;
  359. pdf_obj *next, *prev, *parent;
  360. int result = 0;
  361. int count;
  362. if (iter->modifier != MOD_NONE || iter->current == NULL)
  363. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't delete a non-existent outline item!");
  364. prev = pdf_dict_get(ctx, iter->current, PDF_NAME(Prev));
  365. next = pdf_dict_get(ctx, iter->current, PDF_NAME(Next));
  366. parent = pdf_dict_get(ctx, iter->current, PDF_NAME(Parent));
  367. count = pdf_dict_get_int(ctx, iter->current, PDF_NAME(Count));
  368. /* How many nodes visible from above are being removed? */
  369. if (count > 0)
  370. count++; /* Open children, plus this node. */
  371. else
  372. count = 1; /* Just this node */
  373. pdf_begin_operation(ctx, doc, "Delete outline item");
  374. fz_try(ctx)
  375. {
  376. pdf_obj *up = parent;
  377. while (up)
  378. {
  379. int c = pdf_dict_get_int(ctx, up, PDF_NAME(Count));
  380. pdf_dict_put_int(ctx, up, PDF_NAME(Count), (c > 0 ? c - count : c + count));
  381. if (c < 0)
  382. break;
  383. up = pdf_dict_get(ctx, up, PDF_NAME(Parent));
  384. }
  385. if (prev)
  386. {
  387. if (next)
  388. pdf_dict_put(ctx, prev, PDF_NAME(Next), next);
  389. else
  390. pdf_dict_del(ctx, prev, PDF_NAME(Next));
  391. }
  392. if (next)
  393. {
  394. if (prev)
  395. pdf_dict_put(ctx, next, PDF_NAME(Prev), prev);
  396. else
  397. {
  398. pdf_dict_put(ctx, parent, PDF_NAME(First), next);
  399. pdf_dict_del(ctx, next, PDF_NAME(Prev));
  400. }
  401. iter->current = next;
  402. }
  403. else if (prev)
  404. {
  405. iter->current = prev;
  406. pdf_dict_put(ctx, parent, PDF_NAME(Last), prev);
  407. }
  408. else if (parent)
  409. {
  410. iter->current = parent;
  411. iter->modifier = MOD_BELOW;
  412. pdf_dict_del(ctx, parent, PDF_NAME(First));
  413. pdf_dict_del(ctx, parent, PDF_NAME(Last));
  414. result = 1;
  415. }
  416. else
  417. {
  418. iter->current = NULL;
  419. result = 1;
  420. }
  421. pdf_end_operation(ctx, doc);
  422. }
  423. fz_catch(ctx)
  424. {
  425. pdf_abandon_operation(ctx, doc);
  426. fz_rethrow(ctx);
  427. }
  428. return result;
  429. }
  430. static fz_outline_item *
  431. pdf_outline_iterator_item(fz_context *ctx, fz_outline_iterator *iter_)
  432. {
  433. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  434. pdf_obj *obj;
  435. pdf_document *doc = (pdf_document *)iter->super.doc;
  436. if (iter->modifier != MOD_NONE || iter->current == NULL)
  437. return NULL;
  438. fz_free(ctx, iter->item.title);
  439. iter->item.title = NULL;
  440. fz_free(ctx, iter->item.uri);
  441. iter->item.uri = NULL;
  442. obj = pdf_dict_get(ctx, iter->current, PDF_NAME(Title));
  443. if (obj)
  444. iter->item.title = Memento_label(fz_strdup(ctx, pdf_to_text_string(ctx, obj)), "outline_title");
  445. obj = pdf_dict_get(ctx, iter->current, PDF_NAME(Dest));
  446. if (obj)
  447. iter->item.uri = Memento_label(pdf_parse_link_dest(ctx, doc, obj), "outline_uri");
  448. else
  449. {
  450. obj = pdf_dict_get(ctx, iter->current, PDF_NAME(A));
  451. if (obj)
  452. iter->item.uri = Memento_label(pdf_parse_link_action(ctx, doc, obj, -1), "outline_uri");
  453. }
  454. iter->item.is_open = pdf_dict_get_int(ctx, iter->current, PDF_NAME(Count)) > 0;
  455. obj = pdf_dict_get(ctx, iter->current, PDF_NAME(C));
  456. iter->item.r = (int)(0.5 + 255 * pdf_array_get_real(ctx, obj, 0));
  457. iter->item.g = (int)(0.5 + 255 * pdf_array_get_real(ctx, obj, 1));
  458. iter->item.b = (int)(0.5 + 255 * pdf_array_get_real(ctx, obj, 2));
  459. iter->item.flags = pdf_dict_get_int(ctx, iter->current, PDF_NAME(F)) & 127;
  460. return &iter->item;
  461. }
  462. static void
  463. pdf_outline_iterator_drop(fz_context *ctx, fz_outline_iterator *iter_)
  464. {
  465. pdf_outline_iterator *iter = (pdf_outline_iterator *)iter_;
  466. if (iter == NULL)
  467. return;
  468. fz_free(ctx, iter->item.title);
  469. fz_free(ctx, iter->item.uri);
  470. }
  471. fz_outline_iterator *pdf_new_outline_iterator(fz_context *ctx, pdf_document *doc)
  472. {
  473. pdf_obj *root, *obj, *first;
  474. pdf_mark_bits *marks;
  475. pdf_outline_iterator *iter = NULL;
  476. int fixed = 0;
  477. /* Walk the outlines to spot problems that might bite us later
  478. * (in particular, for cycles). */
  479. marks = pdf_new_mark_bits(ctx, doc);
  480. fz_try(ctx)
  481. {
  482. root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
  483. obj = pdf_dict_get(ctx, root, PDF_NAME(Outlines));
  484. first = pdf_dict_get(ctx, obj, PDF_NAME(First));
  485. if (first)
  486. {
  487. /* cache page tree for fast link destination lookups. This
  488. * will be dropped 'just in time' on writes to the doc. */
  489. pdf_load_page_tree(ctx, doc);
  490. fz_try(ctx)
  491. {
  492. /* Pass through the outlines once, fixing inconsistencies */
  493. pdf_test_outline(ctx, doc, first, marks, obj, &fixed);
  494. if (fixed)
  495. {
  496. /* If a fix was performed, pass through again,
  497. * this time throwing if it's still not correct. */
  498. pdf_mark_bits_reset(ctx, marks);
  499. pdf_test_outline(ctx, doc, first, marks, obj, NULL);
  500. pdf_end_operation(ctx, doc);
  501. }
  502. }
  503. fz_catch(ctx)
  504. {
  505. if (fixed)
  506. pdf_abandon_operation(ctx, doc);
  507. fz_rethrow(ctx);
  508. }
  509. }
  510. }
  511. fz_always(ctx)
  512. pdf_drop_mark_bits(ctx, marks);
  513. fz_catch(ctx)
  514. fz_rethrow(ctx);
  515. iter = fz_new_derived_outline_iter(ctx, pdf_outline_iterator, &doc->super);
  516. iter->super.del = pdf_outline_iterator_del;
  517. iter->super.next = pdf_outline_iterator_next;
  518. iter->super.prev = pdf_outline_iterator_prev;
  519. iter->super.up = pdf_outline_iterator_up;
  520. iter->super.down = pdf_outline_iterator_down;
  521. iter->super.insert = pdf_outline_iterator_insert;
  522. iter->super.update = pdf_outline_iterator_update;
  523. iter->super.drop = pdf_outline_iterator_drop;
  524. iter->super.item = pdf_outline_iterator_item;
  525. iter->current = first;
  526. iter->modifier = MOD_NONE;
  527. return &iter->super;
  528. }