Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7643 views
1
#include "mupdf/xps.h"
2
3
#define REL_START_PART \
4
"http://schemas.microsoft.com/xps/2005/06/fixedrepresentation"
5
#define REL_DOC_STRUCTURE \
6
"http://schemas.microsoft.com/xps/2005/06/documentstructure"
7
#define REL_REQUIRED_RESOURCE \
8
"http://schemas.microsoft.com/xps/2005/06/required-resource"
9
#define REL_REQUIRED_RESOURCE_RECURSIVE \
10
"http://schemas.microsoft.com/xps/2005/06/required-resource#recursive"
11
12
#define REL_START_PART_OXPS \
13
"http://schemas.openxps.org/oxps/v1.0/fixedrepresentation"
14
#define REL_DOC_STRUCTURE_OXPS \
15
"http://schemas.openxps.org/oxps/v1.0/documentstructure"
16
17
static void
18
xps_rels_for_part(fz_context *ctx, xps_document *doc, char *buf, char *name, int buflen)
19
{
20
char *p, *basename;
21
p = strrchr(name, '/');
22
basename = p ? p + 1 : name;
23
fz_strlcpy(buf, name, buflen);
24
p = strrchr(buf, '/');
25
if (p) *p = 0;
26
fz_strlcat(buf, "/_rels/", buflen);
27
fz_strlcat(buf, basename, buflen);
28
fz_strlcat(buf, ".rels", buflen);
29
}
30
31
/*
32
* The FixedDocumentSequence and FixedDocument parts determine
33
* which parts correspond to actual pages, and the page order.
34
*/
35
36
void
37
xps_print_page_list(fz_context *ctx, xps_document *doc)
38
{
39
xps_fixdoc *fixdoc = doc->first_fixdoc;
40
xps_fixpage *page = doc->first_page;
41
42
if (doc->start_part)
43
printf("start part %s\n", doc->start_part);
44
45
while (fixdoc)
46
{
47
printf("fixdoc %s\n", fixdoc->name);
48
fixdoc = fixdoc->next;
49
}
50
51
while (page)
52
{
53
printf("page[%d] %s w=%d h=%d\n", page->number, page->name, page->width, page->height);
54
page = page->next;
55
}
56
}
57
58
static void
59
xps_add_fixed_document(fz_context *ctx, xps_document *doc, char *name)
60
{
61
xps_fixdoc *fixdoc;
62
63
/* Check for duplicates first */
64
for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
65
if (!strcmp(fixdoc->name, name))
66
return;
67
68
fixdoc = fz_malloc_struct(ctx, xps_fixdoc);
69
fixdoc->name = fz_strdup(ctx, name);
70
fixdoc->outline = NULL;
71
fixdoc->next = NULL;
72
73
if (!doc->first_fixdoc)
74
{
75
doc->first_fixdoc = fixdoc;
76
doc->last_fixdoc = fixdoc;
77
}
78
else
79
{
80
doc->last_fixdoc->next = fixdoc;
81
doc->last_fixdoc = fixdoc;
82
}
83
}
84
85
void
86
xps_add_link(fz_context *ctx, xps_document *doc, const fz_rect *area, char *base_uri, char *target_uri)
87
{
88
int len;
89
char *buffer = NULL;
90
char *uri;
91
xps_target *target;
92
fz_link_dest dest;
93
fz_link *link;
94
95
fz_var(buffer);
96
97
if (doc->current_page == NULL || doc->current_page->links_resolved)
98
return;
99
100
fz_try(ctx)
101
{
102
len = 2 + (base_uri ? strlen(base_uri) : 0) +
103
(target_uri ? strlen(target_uri) : 0);
104
buffer = fz_malloc(ctx, len);
105
xps_resolve_url(ctx, doc, buffer, base_uri, target_uri, len);
106
if (xps_url_is_remote(ctx, doc, buffer))
107
{
108
dest.kind = FZ_LINK_URI;
109
dest.ld.uri.is_map = 0;
110
dest.ld.uri.uri = buffer;
111
buffer = NULL;
112
}
113
else
114
{
115
uri = buffer;
116
117
/* FIXME: This won't work for remote docs */
118
/* Skip until we find the fragment marker */
119
while (*uri && *uri != '#')
120
uri++;
121
if (*uri == '#')
122
uri++;
123
124
for (target = doc->target; target; target = target->next)
125
if (!strcmp(target->name, uri))
126
break;
127
128
if (target == NULL)
129
break;
130
131
dest.kind = FZ_LINK_GOTO;
132
dest.ld.gotor.flags = 0;
133
dest.ld.gotor.lt.x = 0;
134
dest.ld.gotor.lt.y = 0;
135
dest.ld.gotor.rb.x = 0;
136
dest.ld.gotor.rb.y = 0;
137
dest.ld.gotor.page = target->page;
138
dest.ld.gotor.file_spec = NULL;
139
dest.ld.gotor.new_window = 0;
140
}
141
142
link = fz_new_link(ctx, area, dest);
143
link->next = doc->current_page->links;
144
doc->current_page->links = link;
145
}
146
fz_always(ctx)
147
{
148
fz_free(ctx, buffer);
149
}
150
fz_catch(ctx)
151
{
152
fz_rethrow(ctx);
153
}
154
}
155
156
static void
157
xps_add_fixed_page(fz_context *ctx, xps_document *doc, char *name, int width, int height)
158
{
159
xps_fixpage *page;
160
161
/* Check for duplicates first */
162
for (page = doc->first_page; page; page = page->next)
163
if (!strcmp(page->name, name))
164
return;
165
166
page = fz_malloc_struct(ctx, xps_fixpage);
167
page->name = fz_strdup(ctx, name);
168
page->number = doc->page_count++;
169
page->width = width;
170
page->height = height;
171
page->links = NULL;
172
page->links_resolved = 0;
173
page->next = NULL;
174
175
if (!doc->first_page)
176
{
177
doc->first_page = page;
178
doc->last_page = page;
179
}
180
else
181
{
182
doc->last_page->next = page;
183
doc->last_page = page;
184
}
185
}
186
187
static void
188
xps_add_link_target(fz_context *ctx, xps_document *doc, char *name)
189
{
190
xps_fixpage *page = doc->last_page;
191
xps_target *target = fz_malloc_struct(ctx, xps_target);
192
target->name = fz_strdup(ctx, name);
193
target->page = page->number;
194
target->next = doc->target;
195
doc->target = target;
196
}
197
198
int
199
xps_lookup_link_target(fz_context *ctx, xps_document *doc, char *target_uri)
200
{
201
xps_target *target;
202
char *needle = strrchr(target_uri, '#');
203
needle = needle ? needle + 1 : target_uri;
204
for (target = doc->target; target; target = target->next)
205
if (!strcmp(target->name, needle))
206
return target->page;
207
return 0;
208
}
209
210
static void
211
xps_drop_link_targets(fz_context *ctx, xps_document *doc)
212
{
213
xps_target *target = doc->target, *next;
214
while (target)
215
{
216
next = target->next;
217
fz_free(ctx, target->name);
218
fz_free(ctx, target);
219
target = next;
220
}
221
}
222
223
static void
224
xps_drop_fixed_pages(fz_context *ctx, xps_document *doc)
225
{
226
xps_fixpage *page = doc->first_page;
227
while (page)
228
{
229
xps_fixpage *next = page->next;
230
fz_drop_link(ctx, page->links);
231
fz_free(ctx, page->name);
232
fz_free(ctx, page);
233
page = next;
234
}
235
doc->first_page = NULL;
236
doc->last_page = NULL;
237
}
238
239
static void
240
xps_drop_fixed_documents(fz_context *ctx, xps_document *doc)
241
{
242
xps_fixdoc *fixdoc = doc->first_fixdoc;
243
while (fixdoc)
244
{
245
xps_fixdoc *next = fixdoc->next;
246
fz_free(ctx, fixdoc->name);
247
fz_free(ctx, fixdoc->outline);
248
fz_free(ctx, fixdoc);
249
fixdoc = next;
250
}
251
doc->first_fixdoc = NULL;
252
doc->last_fixdoc = NULL;
253
}
254
255
void
256
xps_drop_page_list(fz_context *ctx, xps_document *doc)
257
{
258
xps_drop_fixed_documents(ctx, doc);
259
xps_drop_fixed_pages(ctx, doc);
260
xps_drop_link_targets(ctx, doc);
261
}
262
263
/*
264
* Parse the fixed document sequence structure and _rels/.rels to find the start part.
265
*/
266
267
static void
268
xps_parse_metadata_imp(fz_context *ctx, xps_document *doc, fz_xml *item, xps_fixdoc *fixdoc)
269
{
270
while (item)
271
{
272
if (fz_xml_is_tag(item, "Relationship"))
273
{
274
char *target = fz_xml_att(item, "Target");
275
char *type = fz_xml_att(item, "Type");
276
if (target && type)
277
{
278
char tgtbuf[1024];
279
xps_resolve_url(ctx, doc, tgtbuf, doc->base_uri, target, sizeof tgtbuf);
280
if (!strcmp(type, REL_START_PART) || !strcmp(type, REL_START_PART_OXPS))
281
doc->start_part = fz_strdup(ctx, tgtbuf);
282
if ((!strcmp(type, REL_DOC_STRUCTURE) || !strcmp(type, REL_DOC_STRUCTURE_OXPS)) && fixdoc)
283
fixdoc->outline = fz_strdup(ctx, tgtbuf);
284
if (!fz_xml_att(item, "Id"))
285
fz_warn(ctx, "missing relationship id for %s", target);
286
}
287
}
288
289
if (fz_xml_is_tag(item, "DocumentReference"))
290
{
291
char *source = fz_xml_att(item, "Source");
292
if (source)
293
{
294
char srcbuf[1024];
295
xps_resolve_url(ctx, doc, srcbuf, doc->base_uri, source, sizeof srcbuf);
296
xps_add_fixed_document(ctx, doc, srcbuf);
297
}
298
}
299
300
if (fz_xml_is_tag(item, "PageContent"))
301
{
302
char *source = fz_xml_att(item, "Source");
303
char *width_att = fz_xml_att(item, "Width");
304
char *height_att = fz_xml_att(item, "Height");
305
int width = width_att ? atoi(width_att) : 0;
306
int height = height_att ? atoi(height_att) : 0;
307
if (source)
308
{
309
char srcbuf[1024];
310
xps_resolve_url(ctx, doc, srcbuf, doc->base_uri, source, sizeof srcbuf);
311
xps_add_fixed_page(ctx, doc, srcbuf, width, height);
312
}
313
}
314
315
if (fz_xml_is_tag(item, "LinkTarget"))
316
{
317
char *name = fz_xml_att(item, "Name");
318
if (name)
319
xps_add_link_target(ctx, doc, name);
320
}
321
322
xps_parse_metadata_imp(ctx, doc, fz_xml_down(item), fixdoc);
323
324
item = fz_xml_next(item);
325
}
326
}
327
328
static void
329
xps_parse_metadata(fz_context *ctx, xps_document *doc, xps_part *part, xps_fixdoc *fixdoc)
330
{
331
fz_xml *root;
332
char buf[1024];
333
char *s;
334
335
/* Save directory name part */
336
fz_strlcpy(buf, part->name, sizeof buf);
337
s = strrchr(buf, '/');
338
if (s)
339
s[0] = 0;
340
341
/* _rels parts are voodoo: their URI references are from
342
* the part they are associated with, not the actual _rels
343
* part being parsed.
344
*/
345
s = strstr(buf, "/_rels");
346
if (s)
347
*s = 0;
348
349
doc->base_uri = buf;
350
doc->part_uri = part->name;
351
352
root = fz_parse_xml(ctx, part->data, part->size, 0);
353
xps_parse_metadata_imp(ctx, doc, root, fixdoc);
354
fz_drop_xml(ctx, root);
355
356
doc->base_uri = NULL;
357
doc->part_uri = NULL;
358
}
359
360
static void
361
xps_read_and_process_metadata_part(fz_context *ctx, xps_document *doc, char *name, xps_fixdoc *fixdoc)
362
{
363
xps_part *part;
364
365
if (!xps_has_part(ctx, doc, name))
366
return;
367
368
part = xps_read_part(ctx, doc, name);
369
fz_try(ctx)
370
{
371
xps_parse_metadata(ctx, doc, part, fixdoc);
372
}
373
fz_always(ctx)
374
{
375
xps_drop_part(ctx, doc, part);
376
}
377
fz_catch(ctx)
378
{
379
fz_rethrow(ctx);
380
}
381
}
382
383
void
384
xps_read_page_list(fz_context *ctx, xps_document *doc)
385
{
386
xps_fixdoc *fixdoc;
387
388
xps_read_and_process_metadata_part(ctx, doc, "/_rels/.rels", NULL);
389
390
if (!doc->start_part)
391
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find fixed document sequence start part");
392
393
xps_read_and_process_metadata_part(ctx, doc, doc->start_part, NULL);
394
395
for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
396
{
397
char relbuf[1024];
398
fz_try(ctx)
399
{
400
xps_rels_for_part(ctx, doc, relbuf, fixdoc->name, sizeof relbuf);
401
xps_read_and_process_metadata_part(ctx, doc, relbuf, fixdoc);
402
}
403
fz_catch(ctx)
404
{
405
fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
406
fz_warn(ctx, "cannot process FixedDocument rels part");
407
}
408
xps_read_and_process_metadata_part(ctx, doc, fixdoc->name, fixdoc);
409
}
410
}
411
412
int
413
xps_count_pages(fz_context *ctx, xps_document *doc)
414
{
415
return doc->page_count;
416
}
417
418
static fz_xml *
419
xps_load_fixed_page(fz_context *ctx, xps_document *doc, xps_fixpage *page)
420
{
421
xps_part *part;
422
fz_xml *root;
423
char *width_att;
424
char *height_att;
425
426
part = xps_read_part(ctx, doc, page->name);
427
fz_try(ctx)
428
{
429
root = fz_parse_xml(ctx, part->data, part->size, 0);
430
}
431
fz_always(ctx)
432
{
433
xps_drop_part(ctx, doc, part);
434
}
435
fz_catch(ctx)
436
{
437
fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
438
root = NULL;
439
}
440
if (!root)
441
fz_throw(ctx, FZ_ERROR_GENERIC, "FixedPage missing root element");
442
443
if (fz_xml_is_tag(root, "AlternateContent"))
444
{
445
fz_xml *node = xps_lookup_alternate_content(ctx, doc, root);
446
if (!node)
447
{
448
fz_drop_xml(ctx, root);
449
fz_throw(ctx, FZ_ERROR_GENERIC, "FixedPage missing alternate root element");
450
}
451
fz_detach_xml(node);
452
fz_drop_xml(ctx, root);
453
root = node;
454
}
455
456
if (!fz_xml_is_tag(root, "FixedPage"))
457
{
458
fz_drop_xml(ctx, root);
459
fz_throw(ctx, FZ_ERROR_GENERIC, "expected FixedPage element");
460
}
461
462
width_att = fz_xml_att(root, "Width");
463
if (!width_att)
464
{
465
fz_drop_xml(ctx, root);
466
fz_throw(ctx, FZ_ERROR_GENERIC, "FixedPage missing required attribute: Width");
467
}
468
469
height_att = fz_xml_att(root, "Height");
470
if (!height_att)
471
{
472
fz_drop_xml(ctx, root);
473
fz_throw(ctx, FZ_ERROR_GENERIC, "FixedPage missing required attribute: Height");
474
}
475
476
page->width = atoi(width_att);
477
page->height = atoi(height_att);
478
479
return root;
480
}
481
482
fz_link *
483
xps_load_links(fz_context *ctx, xps_page *page)
484
{
485
if (!page->fix->links_resolved)
486
fz_warn(ctx, "xps_load_links before page has been executed!");
487
return fz_keep_link(ctx, page->fix->links);
488
}
489
490
fz_rect *
491
xps_bound_page(fz_context *ctx, xps_page *page, fz_rect *bounds)
492
{
493
bounds->x0 = bounds->y0 = 0;
494
bounds->x1 = page->fix->width * 72.0f / 96.0f;
495
bounds->y1 = page->fix->height * 72.0f / 96.0f;
496
return bounds;
497
}
498
499
void
500
xps_drop_page_imp(fz_context *ctx, xps_page *page)
501
{
502
if (page == NULL)
503
return;
504
fz_drop_document(ctx, &page->doc->super);
505
fz_drop_xml(ctx, page->root);
506
}
507
508
xps_page *
509
xps_load_page(fz_context *ctx, xps_document *doc, int number)
510
{
511
xps_page *page = NULL;
512
xps_fixpage *fix;
513
fz_xml *root;
514
int n = 0;
515
516
fz_var(page);
517
518
for (fix = doc->first_page; fix; fix = fix->next)
519
{
520
if (n == number)
521
{
522
doc->current_page = fix;
523
524
root = xps_load_fixed_page(ctx, doc, fix);
525
fz_try(ctx)
526
{
527
page = fz_new_page(ctx, sizeof *page);
528
page->super.load_links = (fz_page_load_links_fn *)xps_load_links;
529
page->super.bound_page = (fz_page_bound_page_fn *)xps_bound_page;
530
page->super.run_page_contents = (fz_page_run_page_contents_fn *)xps_run_page;
531
page->super.drop_page_imp = (fz_page_drop_page_imp_fn *)xps_drop_page_imp;
532
533
page->doc = (xps_document*) fz_keep_document(ctx, &doc->super);
534
page->fix = fix;
535
page->root = root;
536
}
537
fz_catch(ctx)
538
{
539
fz_drop_xml(ctx, root);
540
fz_rethrow(ctx);
541
}
542
return page;
543
}
544
n ++;
545
}
546
547
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page %d", number + 1);
548
}
549
550
static int
551
xps_recognize(fz_context *ctx, const char *magic)
552
{
553
char *ext = strrchr(magic, '.');
554
555
if (ext)
556
{
557
if (!fz_strcasecmp(ext, ".xps") || !fz_strcasecmp(ext, ".rels") || !fz_strcasecmp(ext, ".oxps"))
558
return 100;
559
}
560
if (!strcmp(magic, "xps") || !strcmp(magic, "oxps") ||
561
!strcmp(magic, "application/vnd.ms-xpsdocument") ||
562
!strcmp(magic, "application/xps") ||
563
!strcmp(magic, "application/oxps"))
564
return 100;
565
566
return 0;
567
}
568
569
fz_document_handler xps_document_handler =
570
{
571
(fz_document_recognize_fn *)&xps_recognize,
572
(fz_document_open_fn *)&xps_open_document,
573
(fz_document_open_with_stream_fn *)&xps_open_document_with_stream
574
};
575
576