Coverage Report

Created: 2023-06-07 06:20

/src/mupdf/source/svg/svg-doc.c
Line
Count
Source (jump to first uncovered line)
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 "svg-imp.h"
25
26
typedef struct
27
{
28
  fz_page super;
29
  svg_document *doc;
30
} svg_page;
31
32
static void
33
svg_drop_document(fz_context *ctx, fz_document *doc_)
34
0
{
35
0
  svg_document *doc = (svg_document*)doc_;
36
0
  fz_drop_tree(ctx, doc->idmap, NULL);
37
0
  fz_drop_xml(ctx, doc->xml);
38
0
}
39
40
static int
41
svg_count_pages(fz_context *ctx, fz_document *doc_, int chapter)
42
0
{
43
0
  return 1;
44
0
}
45
46
static fz_rect
47
svg_bound_page(fz_context *ctx, fz_page *page_)
48
0
{
49
0
  svg_page *page = (svg_page*)page_;
50
0
  svg_document *doc = page->doc;
51
52
0
  svg_parse_document_bounds(ctx, doc, doc->root);
53
54
0
  return fz_make_rect(0, 0, doc->width, doc->height);
55
0
}
56
57
static void
58
svg_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
59
0
{
60
0
  svg_page *page = (svg_page*)page_;
61
0
  svg_document *doc = page->doc;
62
0
  svg_run_document(ctx, doc, doc->root, dev, ctm);
63
0
}
64
65
static void
66
svg_drop_page(fz_context *ctx, fz_page *page_)
67
0
{
68
  /* nothing */
69
0
}
70
71
static fz_page *
72
svg_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number)
73
0
{
74
0
  svg_document *doc = (svg_document*)doc_;
75
0
  svg_page *page;
76
77
0
  if (number != 0)
78
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page %d", number);
79
80
0
  page = fz_new_derived_page(ctx, svg_page, doc_);
81
0
  page->super.bound_page = svg_bound_page;
82
0
  page->super.run_page_contents = svg_run_page;
83
0
  page->super.drop_page = svg_drop_page;
84
0
  page->doc = doc;
85
86
0
  return (fz_page*)page;
87
0
}
88
89
static void
90
svg_build_id_map(fz_context *ctx, svg_document *doc, fz_xml *root)
91
0
{
92
0
  fz_xml *node;
93
94
0
  char *id_att = fz_xml_att(root, "id");
95
0
  if (id_att)
96
0
    doc->idmap = fz_tree_insert(ctx, doc->idmap, id_att, root);
97
98
0
  for (node = fz_xml_down(root); node; node = fz_xml_next(node))
99
0
    svg_build_id_map(ctx, doc, node);
100
0
}
101
102
static fz_document *
103
svg_open_document_with_xml(fz_context *ctx, fz_xml_doc *xmldoc, fz_xml *xml, const char *base_uri, fz_archive *zip)
104
0
{
105
0
  svg_document *doc;
106
107
0
  doc = fz_new_derived_document(ctx, svg_document);
108
0
  doc->super.drop_document = svg_drop_document;
109
0
  doc->super.count_pages = svg_count_pages;
110
0
  doc->super.load_page = svg_load_page;
111
112
0
  doc->idmap = NULL;
113
0
  if (base_uri)
114
0
    fz_strlcpy(doc->base_uri, base_uri, sizeof doc->base_uri);
115
0
  doc->xml = NULL;
116
0
  doc->root = xml;
117
0
  doc->zip = zip;
118
119
0
  fz_try(ctx)
120
0
  {
121
0
    if (xmldoc)
122
0
      svg_build_id_map(ctx, doc, fz_xml_root(xmldoc));
123
0
    else
124
0
      svg_build_id_map(ctx, doc, doc->root);
125
0
  }
126
0
  fz_catch(ctx)
127
0
  {
128
0
    fz_drop_document(ctx, &doc->super);
129
0
    fz_rethrow(ctx);
130
0
  }
131
132
0
  return (fz_document*)doc;
133
0
}
134
135
static fz_document *
136
svg_open_document_with_buffer(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip)
137
0
{
138
0
  svg_document *doc;
139
140
0
  doc = fz_new_derived_document(ctx, svg_document);
141
0
  doc->super.drop_document = svg_drop_document;
142
0
  doc->super.count_pages = svg_count_pages;
143
0
  doc->super.load_page = svg_load_page;
144
145
0
  doc->idmap = NULL;
146
0
  if (base_uri)
147
0
    fz_strlcpy(doc->base_uri, base_uri, sizeof doc->base_uri);
148
0
  doc->zip = zip;
149
150
0
  fz_try(ctx)
151
0
  {
152
0
    doc->xml = fz_parse_xml(ctx, buf, 0);
153
0
    doc->root = fz_xml_root(doc->xml);
154
0
    svg_build_id_map(ctx, doc, doc->root);
155
0
  }
156
0
  fz_catch(ctx)
157
0
  {
158
0
    fz_drop_document(ctx, &doc->super);
159
0
    fz_rethrow(ctx);
160
0
  }
161
162
0
  return (fz_document*)doc;
163
0
}
164
165
static fz_document *
166
svg_open_document_with_stream(fz_context *ctx, fz_stream *file)
167
0
{
168
0
  fz_buffer *buf;
169
0
  fz_document *doc = NULL;
170
171
0
  buf = fz_read_all(ctx, file, 0);
172
0
  fz_try(ctx)
173
0
    doc = svg_open_document_with_buffer(ctx, buf, NULL, NULL);
174
0
  fz_always(ctx)
175
0
    fz_drop_buffer(ctx, buf);
176
0
  fz_catch(ctx)
177
0
    fz_rethrow(ctx);
178
179
0
  return doc;
180
0
}
181
182
fz_display_list *
183
fz_new_display_list_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip, float *w, float *h)
184
0
{
185
0
  fz_document *doc;
186
0
  fz_display_list *list = NULL;
187
188
0
  doc = svg_open_document_with_buffer(ctx, buf, base_uri, zip);
189
0
  fz_try(ctx)
190
0
  {
191
0
    list = fz_new_display_list_from_page_number(ctx, doc, 0);
192
0
    *w = ((svg_document*)doc)->width;
193
0
    *h = ((svg_document*)doc)->height;
194
0
  }
195
0
  fz_always(ctx)
196
0
    fz_drop_document(ctx, doc);
197
0
  fz_catch(ctx)
198
0
    fz_rethrow(ctx);
199
200
0
  return list;
201
0
}
202
203
fz_display_list *
204
fz_new_display_list_from_svg_xml(fz_context *ctx, fz_xml_doc *xmldoc, fz_xml *xml, const char *base_uri, fz_archive *zip, float *w, float *h)
205
0
{
206
0
  fz_document *doc;
207
0
  fz_display_list *list = NULL;
208
209
0
  doc = svg_open_document_with_xml(ctx, xmldoc, xml, base_uri, zip);
210
0
  fz_try(ctx)
211
0
  {
212
0
    list = fz_new_display_list_from_page_number(ctx, doc, 0);
213
0
    *w = ((svg_document*)doc)->width;
214
0
    *h = ((svg_document*)doc)->height;
215
0
  }
216
0
  fz_always(ctx)
217
0
    fz_drop_document(ctx, doc);
218
0
  fz_catch(ctx)
219
0
    fz_rethrow(ctx);
220
221
0
  return list;
222
0
}
223
224
fz_image *
225
fz_new_image_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip)
226
0
{
227
0
  fz_display_list *list;
228
0
  fz_image *image = NULL;
229
0
  float w, h;
230
231
0
  list = fz_new_display_list_from_svg(ctx, buf, base_uri, zip, &w, &h);
232
0
  fz_try(ctx)
233
0
    image = fz_new_image_from_display_list(ctx, w, h, list);
234
0
  fz_always(ctx)
235
0
    fz_drop_display_list(ctx, list);
236
0
  fz_catch(ctx)
237
0
    fz_rethrow(ctx);
238
0
  return image;
239
0
}
240
241
fz_image *
242
fz_new_image_from_svg_xml(fz_context *ctx, fz_xml_doc *xmldoc, fz_xml *xml, const char *base_uri, fz_archive *zip)
243
0
{
244
0
  fz_display_list *list;
245
0
  fz_image *image = NULL;
246
0
  float w, h;
247
248
0
  list = fz_new_display_list_from_svg_xml(ctx, xmldoc, xml, base_uri, zip, &w, &h);
249
0
  fz_try(ctx)
250
0
    image = fz_new_image_from_display_list(ctx, w, h, list);
251
0
  fz_always(ctx)
252
0
    fz_drop_display_list(ctx, list);
253
0
  fz_catch(ctx)
254
0
    fz_rethrow(ctx);
255
0
  return image;
256
0
}
257
258
static const char *svg_extensions[] =
259
{
260
  "svg",
261
  NULL
262
};
263
264
static const char *svg_mimetypes[] =
265
{
266
  "image/svg+xml",
267
  NULL
268
};
269
270
static int
271
svg_recognize_doc_content(fz_context *ctx, fz_stream *stream)
272
14.8k
{
273
14.8k
  int c;
274
14.8k
  int n = 0;
275
14.8k
  const char *match = "svg";
276
14.8k
  int pos = 0;
277
278
  /* Is the first non-whitespace char '<' ? */
279
14.8k
  do
280
19.8k
  {
281
19.8k
    c = fz_read_byte(ctx, stream);
282
19.8k
    if (c == EOF)
283
4
      return 0;
284
19.8k
    if (c == '<')
285
1.76k
      break;
286
18.1k
    if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
287
13.1k
      return 0;
288
18.1k
  }
289
14.8k
  while (++n < 4096);
290
291
  /* Then do we find 'svg' in the first 4k? */
292
1.76k
  do
293
3.78M
  {
294
3.78M
    c = fz_read_byte(ctx, stream);
295
3.78M
    if (c == EOF)
296
1.44k
      return 0;
297
3.78M
    if (c >= 'A' && c <= 'Z')
298
327k
      c += 'a' - 'A';
299
3.78M
    if (c == match[pos])
300
53.0k
    {
301
53.0k
      pos++;
302
53.0k
      if (pos == 3)
303
0
        return 100;
304
53.0k
    }
305
3.73M
    else
306
3.73M
    {
307
      /* Restart matching, but recheck c against the start. */
308
3.73M
      pos = (c == match[0]);
309
3.73M
    }
310
3.78M
  }
311
3.78M
  while (++n < 4096);
312
313
321
  return 0;
314
1.76k
}
315
316
fz_document_handler svg_document_handler =
317
{
318
  NULL,
319
  NULL,
320
  svg_open_document_with_stream,
321
  svg_extensions,
322
  svg_mimetypes,
323
  NULL,
324
  NULL,
325
  svg_recognize_doc_content
326
};