Coverage Report

Created: 2025-09-04 06:50

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