xps-outline.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright (C) 2004-2021 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 "xps-imp.h"
  24. #include <stdlib.h>
  25. #include <math.h>
  26. /*
  27. * Parse the document structure / outline parts referenced from fixdoc relationships.
  28. */
  29. static fz_outline *
  30. xps_lookup_last_outline_at_level(fz_context *ctx, xps_document *doc, fz_outline *node, int level, int target_level)
  31. {
  32. while (node->next)
  33. node = node->next;
  34. if (level == target_level || !node->down)
  35. return node;
  36. return xps_lookup_last_outline_at_level(ctx, doc, node->down, level + 1, target_level);
  37. }
  38. static fz_outline *
  39. xps_parse_document_outline(fz_context *ctx, xps_document *doc, fz_xml *root)
  40. {
  41. fz_xml *node;
  42. fz_outline *head = NULL, *entry, *tail;
  43. int last_level = 1, this_level;
  44. for (node = fz_xml_down(root); node; node = fz_xml_next(node))
  45. {
  46. if (fz_xml_is_tag(node, "OutlineEntry"))
  47. {
  48. char *level = fz_xml_att(node, "OutlineLevel");
  49. char *target = fz_xml_att(node, "OutlineTarget");
  50. char *description = fz_xml_att(node, "Description");
  51. if (!target || !description)
  52. continue;
  53. entry = fz_new_outline(ctx);
  54. entry->title = Memento_label(fz_strdup(ctx, description), "outline_title");
  55. entry->uri = Memento_label(fz_strdup(ctx, target), "outline_uri");
  56. entry->page = xps_lookup_link_target(ctx, (fz_document*)doc, target).loc;
  57. entry->down = NULL;
  58. entry->next = NULL;
  59. this_level = level ? atoi(level) : 1;
  60. if (!head)
  61. {
  62. head = entry;
  63. }
  64. else
  65. {
  66. tail = xps_lookup_last_outline_at_level(ctx, doc, head, 1, this_level);
  67. if (this_level > last_level)
  68. tail->down = entry;
  69. else
  70. tail->next = entry;
  71. }
  72. last_level = this_level;
  73. }
  74. }
  75. return head;
  76. }
  77. static fz_outline *
  78. xps_parse_document_structure(fz_context *ctx, xps_document *doc, fz_xml *root)
  79. {
  80. fz_xml *node;
  81. if (fz_xml_is_tag(root, "DocumentStructure"))
  82. {
  83. node = fz_xml_down(root);
  84. if (node && fz_xml_is_tag(node, "DocumentStructure.Outline"))
  85. {
  86. node = fz_xml_down(node);
  87. if (node && fz_xml_is_tag(node, "DocumentOutline"))
  88. return xps_parse_document_outline(ctx, doc, node);
  89. }
  90. }
  91. return NULL;
  92. }
  93. static fz_outline *
  94. xps_load_document_structure(fz_context *ctx, xps_document *doc, xps_fixdoc *fixdoc)
  95. {
  96. xps_part *part;
  97. fz_xml_doc *xml = NULL;
  98. fz_outline *outline = NULL;
  99. fz_var(xml);
  100. part = xps_read_part(ctx, doc, fixdoc->outline);
  101. fz_try(ctx)
  102. {
  103. xml = fz_parse_xml(ctx, part->data, 0);
  104. outline = xps_parse_document_structure(ctx, doc, fz_xml_root(xml));
  105. }
  106. fz_always(ctx)
  107. {
  108. fz_drop_xml(ctx, xml);
  109. xps_drop_part(ctx, doc, part);
  110. }
  111. fz_catch(ctx)
  112. {
  113. fz_rethrow(ctx);
  114. }
  115. return outline;
  116. }
  117. fz_outline *
  118. xps_load_outline(fz_context *ctx, fz_document *doc_)
  119. {
  120. xps_document *doc = (xps_document*)doc_;
  121. xps_fixdoc *fixdoc;
  122. fz_outline *head = NULL, *tail, *outline = NULL;
  123. for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
  124. {
  125. if (fixdoc->outline)
  126. {
  127. fz_try(ctx)
  128. {
  129. outline = xps_load_document_structure(ctx, doc, fixdoc);
  130. }
  131. fz_catch(ctx)
  132. {
  133. fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
  134. fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
  135. fz_report_error(ctx);
  136. outline = NULL;
  137. }
  138. if (!outline)
  139. continue;
  140. if (!head)
  141. head = outline;
  142. else
  143. {
  144. while (tail->next)
  145. tail = tail->next;
  146. tail->next = outline;
  147. }
  148. tail = outline;
  149. }
  150. }
  151. return head;
  152. }