Coverage Report

Created: 2025-12-03 07:00

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