/xps/xps_doc.c

https://github.com/mescher/mupdf · C · 341 lines · 270 code · 56 blank · 15 comment · 42 complexity · 3cfd69f47f83114a87a5cea94c9c39cf MD5 · raw file

  1. #include "fitz.h"
  2. #include "muxps.h"
  3. /*
  4. * The FixedDocumentSequence and FixedDocument parts determine
  5. * which parts correspond to actual pages, and the page order.
  6. */
  7. void
  8. xps_debug_page_list(xps_context *ctx)
  9. {
  10. xps_document *fixdoc = ctx->first_fixdoc;
  11. xps_page *page = ctx->first_page;
  12. if (ctx->start_part)
  13. printf("start part %s\n", ctx->start_part);
  14. while (fixdoc)
  15. {
  16. printf("fixdoc %s\n", fixdoc->name);
  17. fixdoc = fixdoc->next;
  18. }
  19. while (page)
  20. {
  21. printf("page %s w=%d h=%d\n", page->name, page->width, page->height);
  22. page = page->next;
  23. }
  24. }
  25. static void
  26. xps_add_fixed_document(xps_context *ctx, char *name)
  27. {
  28. xps_document *fixdoc;
  29. /* Check for duplicates first */
  30. for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
  31. if (!strcmp(fixdoc->name, name))
  32. return;
  33. fixdoc = fz_malloc(sizeof(xps_document));
  34. fixdoc->name = fz_strdup(name);
  35. fixdoc->next = NULL;
  36. if (!ctx->first_fixdoc)
  37. {
  38. ctx->first_fixdoc = fixdoc;
  39. ctx->last_fixdoc = fixdoc;
  40. }
  41. else
  42. {
  43. ctx->last_fixdoc->next = fixdoc;
  44. ctx->last_fixdoc = fixdoc;
  45. }
  46. }
  47. static void
  48. xps_add_fixed_page(xps_context *ctx, char *name, int width, int height)
  49. {
  50. xps_page *page;
  51. /* Check for duplicates first */
  52. for (page = ctx->first_page; page; page = page->next)
  53. if (!strcmp(page->name, name))
  54. return;
  55. page = fz_malloc(sizeof(xps_page));
  56. page->name = fz_strdup(name);
  57. page->width = width;
  58. page->height = height;
  59. page->root = NULL;
  60. page->next = NULL;
  61. if (!ctx->first_page)
  62. {
  63. ctx->first_page = page;
  64. ctx->last_page = page;
  65. }
  66. else
  67. {
  68. ctx->last_page->next = page;
  69. ctx->last_page = page;
  70. }
  71. }
  72. static void
  73. xps_free_fixed_pages(xps_context *ctx)
  74. {
  75. xps_page *page = ctx->first_page;
  76. while (page)
  77. {
  78. xps_page *next = page->next;
  79. xps_free_page(ctx, page);
  80. fz_free(page->name);
  81. fz_free(page);
  82. page = next;
  83. }
  84. ctx->first_page = NULL;
  85. ctx->last_page = NULL;
  86. }
  87. static void
  88. xps_free_fixed_documents(xps_context *ctx)
  89. {
  90. xps_document *doc = ctx->first_fixdoc;
  91. while (doc)
  92. {
  93. xps_document *next = doc->next;
  94. fz_free(doc->name);
  95. fz_free(doc);
  96. doc = next;
  97. }
  98. ctx->first_fixdoc = NULL;
  99. ctx->last_fixdoc = NULL;
  100. }
  101. void
  102. xps_free_page_list(xps_context *ctx)
  103. {
  104. xps_free_fixed_documents(ctx);
  105. xps_free_fixed_pages(ctx);
  106. }
  107. /*
  108. * Parse the fixed document sequence structure and _rels/.rels to find the start part.
  109. */
  110. static void
  111. xps_parse_metadata_imp(xps_context *ctx, xml_element *item)
  112. {
  113. while (item)
  114. {
  115. xps_parse_metadata_imp(ctx, xml_down(item));
  116. if (!strcmp(xml_tag(item), "Relationship"))
  117. {
  118. char *target = xml_att(item, "Target");
  119. char *type = xml_att(item, "Type");
  120. if (target && type)
  121. {
  122. char tgtbuf[1024];
  123. xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf);
  124. if (!strcmp(type, REL_START_PART))
  125. ctx->start_part = fz_strdup(tgtbuf);
  126. }
  127. }
  128. if (!strcmp(xml_tag(item), "DocumentReference"))
  129. {
  130. char *source = xml_att(item, "Source");
  131. if (source)
  132. {
  133. char srcbuf[1024];
  134. xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf);
  135. xps_add_fixed_document(ctx, srcbuf);
  136. }
  137. }
  138. if (!strcmp(xml_tag(item), "PageContent"))
  139. {
  140. char *source = xml_att(item, "Source");
  141. char *width_att = xml_att(item, "Width");
  142. char *height_att = xml_att(item, "Height");
  143. int width = width_att ? atoi(width_att) : 0;
  144. int height = height_att ? atoi(height_att) : 0;
  145. if (source)
  146. {
  147. char srcbuf[1024];
  148. xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf);
  149. xps_add_fixed_page(ctx, srcbuf, width, height);
  150. }
  151. }
  152. item = xml_next(item);
  153. }
  154. }
  155. static int
  156. xps_parse_metadata(xps_context *ctx, xps_part *part)
  157. {
  158. xml_element *root;
  159. char buf[1024];
  160. char *s;
  161. /* Save directory name part */
  162. fz_strlcpy(buf, part->name, sizeof buf);
  163. s = strrchr(buf, '/');
  164. if (s)
  165. s[0] = 0;
  166. /* _rels parts are voodoo: their URI references are from
  167. * the part they are associated with, not the actual _rels
  168. * part being parsed.
  169. */
  170. s = strstr(buf, "/_rels");
  171. if (s)
  172. *s = 0;
  173. ctx->base_uri = buf;
  174. ctx->part_uri = part->name;
  175. root = xml_parse_document(part->data, part->size);
  176. if (!root)
  177. return fz_rethrow(-1, "cannot parse metadata part '%s'", part->name);
  178. xps_parse_metadata_imp(ctx, root);
  179. xml_free_element(root);
  180. ctx->base_uri = NULL;
  181. ctx->part_uri = NULL;
  182. return fz_okay;
  183. }
  184. static int
  185. xps_read_and_process_metadata_part(xps_context *ctx, char *name)
  186. {
  187. xps_part *part;
  188. int code;
  189. part = xps_read_part(ctx, name);
  190. if (!part)
  191. return fz_rethrow(-1, "cannot read zip part '%s'", name);
  192. code = xps_parse_metadata(ctx, part);
  193. if (code)
  194. return fz_rethrow(code, "cannot process metadata part '%s'", name);
  195. xps_free_part(ctx, part);
  196. return fz_okay;
  197. }
  198. int
  199. xps_read_page_list(xps_context *ctx)
  200. {
  201. xps_document *doc;
  202. int code;
  203. code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels");
  204. if (code)
  205. return fz_rethrow(code, "cannot process root relationship part");
  206. if (!ctx->start_part)
  207. return fz_throw("cannot find fixed document sequence start part");
  208. code = xps_read_and_process_metadata_part(ctx, ctx->start_part);
  209. if (code)
  210. return fz_rethrow(code, "cannot process FixedDocumentSequence part");
  211. for (doc = ctx->first_fixdoc; doc; doc = doc->next)
  212. {
  213. code = xps_read_and_process_metadata_part(ctx, doc->name);
  214. if (code)
  215. return fz_rethrow(code, "cannot process FixedDocument part");
  216. }
  217. return fz_okay;
  218. }
  219. int
  220. xps_count_pages(xps_context *ctx)
  221. {
  222. xps_page *page;
  223. int n = 0;
  224. for (page = ctx->first_page; page; page = page->next)
  225. n ++;
  226. return n;
  227. }
  228. static int
  229. xps_load_fixed_page(xps_context *ctx, xps_page *page)
  230. {
  231. xps_part *part;
  232. xml_element *root;
  233. char *width_att;
  234. char *height_att;
  235. part = xps_read_part(ctx, page->name);
  236. if (!part)
  237. return fz_rethrow(-1, "cannot read zip part '%s'", page->name);
  238. root = xml_parse_document(part->data, part->size);
  239. if (!root)
  240. return fz_rethrow(-1, "cannot parse xml part '%s'", page->name);
  241. xps_free_part(ctx, part);
  242. if (strcmp(xml_tag(root), "FixedPage"))
  243. return fz_throw("expected FixedPage element (found %s)", xml_tag(root));
  244. width_att = xml_att(root, "Width");
  245. if (!width_att)
  246. return fz_throw("FixedPage missing required attribute: Width");
  247. height_att = xml_att(root, "Height");
  248. if (!height_att)
  249. return fz_throw("FixedPage missing required attribute: Height");
  250. page->width = atoi(width_att);
  251. page->height = atoi(height_att);
  252. page->root = root;
  253. return 0;
  254. }
  255. int
  256. xps_load_page(xps_page **pagep, xps_context *ctx, int number)
  257. {
  258. xps_page *page;
  259. int code;
  260. int n = 0;
  261. for (page = ctx->first_page; page; page = page->next)
  262. {
  263. if (n == number)
  264. {
  265. if (!page->root)
  266. {
  267. code = xps_load_fixed_page(ctx, page);
  268. if (code)
  269. return fz_rethrow(code, "cannot load page %d", number + 1);
  270. }
  271. *pagep = page;
  272. return fz_okay;
  273. }
  274. n ++;
  275. }
  276. return fz_throw("cannot find page %d", number + 1);
  277. }
  278. void
  279. xps_free_page(xps_context *ctx, xps_page *page)
  280. {
  281. /* only free the XML contents */
  282. if (page->root)
  283. xml_free_element(page->root);
  284. page->root = NULL;
  285. }