Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/xps/xps-link.c
Line
Count
Source
1
// Copyright (C) 2004-2022 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 <string.h>
27
#include <stdlib.h>
28
29
/* Quick parsing of document to find links. */
30
31
static void
32
xps_load_links_in_element(fz_context *ctx, xps_document *doc, fz_matrix ctm,
33
    char *base_uri, xps_resource *dict, fz_xml *node, fz_link **link);
34
35
static void
36
xps_add_link(fz_context *ctx, xps_document *doc, fz_rect area, char *base_uri, char *target_uri, fz_link **head)
37
0
{
38
0
  fz_link *link = fz_new_derived_link(ctx, fz_link, area, target_uri);
39
0
  link->next = *head;
40
0
  *head = link;
41
0
}
42
43
static void
44
xps_load_links_in_path(fz_context *ctx, xps_document *doc, fz_matrix ctm,
45
    char *base_uri, xps_resource *dict, fz_xml *root, fz_link **link)
46
0
{
47
0
  char *navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri");
48
0
  if (navigate_uri_att)
49
0
  {
50
0
    char *transform_att = fz_xml_att(root, "RenderTransform");
51
0
    fz_xml *transform_tag = fz_xml_down(fz_xml_find_down(root, "Path.RenderTransform"));
52
53
0
    char *data_att = fz_xml_att(root, "Data");
54
0
    fz_xml *data_tag = fz_xml_down(fz_xml_find_down(root, "Path.Data"));
55
56
0
    fz_path *path = NULL;
57
0
    int fill_rule;
58
0
    fz_rect area;
59
60
0
    xps_resolve_resource_reference(ctx, doc, dict, &data_att, &data_tag, NULL);
61
0
    xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
62
63
0
    ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm);
64
65
0
    if (data_att)
66
0
      path = xps_parse_abbreviated_geometry(ctx, doc, data_att, &fill_rule);
67
0
    else if (data_tag)
68
0
      path = xps_parse_path_geometry(ctx, doc, dict, data_tag, 0, &fill_rule);
69
0
    if (path)
70
0
    {
71
0
      fz_try(ctx)
72
0
        area = fz_bound_path(ctx, path, NULL, ctm);
73
0
      fz_always(ctx)
74
0
        fz_drop_path(ctx, path);
75
0
      fz_catch(ctx)
76
0
        fz_rethrow(ctx);
77
0
      xps_add_link(ctx, doc, area, base_uri, navigate_uri_att, link);
78
0
    }
79
0
  }
80
0
}
81
82
static void
83
xps_load_links_in_glyphs(fz_context *ctx, xps_document *doc, fz_matrix ctm,
84
    char *base_uri, xps_resource *dict, fz_xml *root, fz_link **link)
85
0
{
86
0
  char *navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri");
87
0
  if (navigate_uri_att)
88
0
  {
89
0
    char *transform_att = fz_xml_att(root, "RenderTransform");
90
0
    fz_xml *transform_tag = fz_xml_down(fz_xml_find_down(root, "Path.RenderTransform"));
91
92
0
    char *bidi_level_att = fz_xml_att(root, "BidiLevel");
93
0
    char *font_size_att = fz_xml_att(root, "FontRenderingEmSize");
94
0
    char *font_uri_att = fz_xml_att(root, "FontUri");
95
0
    char *origin_x_att = fz_xml_att(root, "OriginX");
96
0
    char *origin_y_att = fz_xml_att(root, "OriginY");
97
0
    char *is_sideways_att = fz_xml_att(root, "IsSideways");
98
0
    char *indices_att = fz_xml_att(root, "Indices");
99
0
    char *unicode_att = fz_xml_att(root, "UnicodeString");
100
0
    char *style_att = fz_xml_att(root, "StyleSimulations");
101
102
0
    int is_sideways = 0;
103
0
    int bidi_level = 0;
104
0
    fz_font *font;
105
0
    fz_text *text = NULL;
106
0
    fz_rect area;
107
108
0
    if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) {
109
0
      fz_warn(ctx, "missing attributes in glyphs element");
110
0
      return;
111
0
    }
112
113
0
    if (!indices_att && !unicode_att)
114
0
      return; /* nothing to draw */
115
116
0
    xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
117
118
0
    ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm);
119
120
0
    if (is_sideways_att)
121
0
      is_sideways = !strcmp(is_sideways_att, "true");
122
0
    if (bidi_level_att)
123
0
      bidi_level = atoi(bidi_level_att);
124
125
0
    font = xps_lookup_font(ctx, doc, base_uri, font_uri_att, style_att);
126
0
    if (!font)
127
0
      return;
128
129
0
    fz_var(text);
130
0
    fz_try(ctx)
131
0
    {
132
0
      text = xps_parse_glyphs_imp(ctx, doc, ctm, font, fz_atof(font_size_att),
133
0
          fz_atof(origin_x_att), fz_atof(origin_y_att),
134
0
          is_sideways, bidi_level, indices_att, unicode_att);
135
0
      area = fz_bound_text(ctx, text, NULL, ctm);
136
0
    }
137
0
    fz_always(ctx)
138
0
    {
139
0
      fz_drop_text(ctx, text);
140
0
      fz_drop_font(ctx, font);
141
0
    }
142
0
    fz_catch(ctx)
143
0
      fz_rethrow(ctx);
144
145
0
    xps_add_link(ctx, doc, area, base_uri, navigate_uri_att, link);
146
0
  }
147
0
}
148
149
static void
150
xps_load_links_in_canvas(fz_context *ctx, xps_document *doc, fz_matrix ctm,
151
    char *base_uri, xps_resource *dict, fz_xml *root, fz_link **link)
152
0
{
153
0
  xps_resource *new_dict = NULL;
154
0
  fz_xml *node;
155
156
0
  char *navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri");
157
0
  char *transform_att = fz_xml_att(root, "RenderTransform");
158
0
  fz_xml *transform_tag = fz_xml_down(fz_xml_find_down(root, "Canvas.RenderTransform"));
159
0
  fz_xml *resource_tag = fz_xml_down(fz_xml_find_down(root, "Canvas.Resources"));
160
161
0
  if (resource_tag)
162
0
  {
163
0
    new_dict = xps_parse_resource_dictionary(ctx, doc, base_uri, resource_tag);
164
0
    if (new_dict)
165
0
    {
166
0
      new_dict->parent = dict;
167
0
      dict = new_dict;
168
0
    }
169
0
  }
170
171
0
  xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
172
173
0
  ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm);
174
175
0
  if (navigate_uri_att)
176
0
    fz_warn(ctx, "FixedPage.NavigateUri attribute on Canvas element");
177
178
0
  for (node = fz_xml_down(root); node; node = fz_xml_next(node))
179
0
    xps_load_links_in_element(ctx, doc, ctm, base_uri, dict, node, link);
180
181
0
  if (new_dict)
182
0
    xps_drop_resource_dictionary(ctx, doc, new_dict);
183
0
}
184
185
static void
186
xps_load_links_in_element(fz_context *ctx, xps_document *doc, fz_matrix ctm, char *base_uri, xps_resource *dict, fz_xml *node, fz_link **link)
187
0
{
188
0
  if (fz_xml_is_tag(node, "Path"))
189
0
    xps_load_links_in_path(ctx, doc, ctm, base_uri, dict, node, link);
190
0
  else if (fz_xml_is_tag(node, "Glyphs"))
191
0
    xps_load_links_in_glyphs(ctx, doc, ctm, base_uri, dict, node, link);
192
0
  else if (fz_xml_is_tag(node, "Canvas"))
193
0
    xps_load_links_in_canvas(ctx, doc, ctm, base_uri, dict, node, link);
194
0
  else if (fz_xml_is_tag(node, "AlternateContent"))
195
0
  {
196
0
    node = xps_lookup_alternate_content(ctx, doc, node);
197
0
    if (node)
198
0
      xps_load_links_in_element(ctx, doc, ctm, base_uri, dict, node, link);
199
0
  }
200
0
}
201
202
static void
203
xps_load_links_in_fixed_page(fz_context *ctx, xps_document *doc, fz_matrix ctm, xps_page *page, fz_link **link)
204
0
{
205
0
  fz_xml *root, *node, *resource_tag;
206
0
  xps_resource *dict = NULL;
207
0
  char base_uri[1024];
208
0
  char *s;
209
210
0
  root = fz_xml_root(page->xml);
211
212
0
  if (!root)
213
0
    return;
214
215
0
  fz_strlcpy(base_uri, page->fix->name, sizeof base_uri);
216
0
  s = strrchr(base_uri, '/');
217
0
  if (s)
218
0
    s[1] = 0;
219
220
0
  resource_tag = fz_xml_down(fz_xml_find_down(root, "FixedPage.Resources"));
221
0
  if (resource_tag)
222
0
    dict = xps_parse_resource_dictionary(ctx, doc, base_uri, resource_tag);
223
224
0
  for (node = fz_xml_down(root); node; node = fz_xml_next(node))
225
0
    xps_load_links_in_element(ctx, doc, ctm, base_uri, dict, node, link);
226
227
0
  if (dict)
228
0
    xps_drop_resource_dictionary(ctx, doc, dict);
229
0
}
230
231
fz_link *
232
xps_load_links(fz_context *ctx, fz_page *page_)
233
0
{
234
0
  xps_page *page = (xps_page*)page_;
235
0
  fz_matrix ctm;
236
  fz_link *link = NULL;
237
0
  ctm = fz_scale(72.0f / 96.0f, 72.0f / 96.0f);
238
0
  xps_load_links_in_fixed_page(ctx, (xps_document*)page->super.doc, ctm, page, &link);
239
0
  return link;
240
0
}