Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. #include "fitz.h"
  2. #include "muxps.h"
  3.  
  4. #include <zlib.h>
  5.  
  6. xps_part *
  7. xps_new_part(xps_context *ctx, char *name, int size)
  8. {
  9.         xps_part *part;
  10.  
  11.         part = fz_malloc(sizeof(xps_part));
  12.         part->name = fz_strdup(name);
  13.         part->size = size;
  14.         part->data = fz_malloc(size + 1);
  15.         part->data[size] = 0; /* null-terminate for xml parser */
  16.  
  17.         return part;
  18. }
  19.  
  20. void
  21. xps_free_part(xps_context *ctx, xps_part *part)
  22. {
  23.         fz_free(part->name);
  24.         fz_free(part->data);
  25.         fz_free(part);
  26. }
  27.  
  28. static inline int getshort(fz_stream *file)
  29. {
  30.         int a = fz_read_byte(file);
  31.         int b = fz_read_byte(file);
  32.         return a | b << 8;
  33. }
  34.  
  35. static inline int getlong(fz_stream *file)
  36. {
  37.         int a = fz_read_byte(file);
  38.         int b = fz_read_byte(file);
  39.         int c = fz_read_byte(file);
  40.         int d = fz_read_byte(file);
  41.         return a | b << 8 | c << 16 | d << 24;
  42. }
  43.  
  44. static void *
  45. xps_zip_alloc_items(xps_context *ctx, int items, int size)
  46. {
  47.         return fz_calloc(items, size);
  48. }
  49.  
  50. static void
  51. xps_zip_free(xps_context *ctx, void *ptr)
  52. {
  53.         fz_free(ptr);
  54. }
  55.  
  56. static int
  57. xps_compare_entries(const void *a0, const void *b0)
  58. {
  59.         xps_entry *a = (xps_entry*) a0;
  60.         xps_entry *b = (xps_entry*) b0;
  61.         return xps_strcasecmp(a->name, b->name);
  62. }
  63.  
  64. static xps_entry *
  65. xps_find_zip_entry(xps_context *ctx, char *name)
  66. {
  67.         int l = 0;
  68.         int r = ctx->zip_count - 1;
  69.         while (l <= r)
  70.         {
  71.                 int m = (l + r) >> 1;
  72.                 int c = xps_strcasecmp(name, ctx->zip_table[m].name);
  73.                 if (c < 0)
  74.                         r = m - 1;
  75.                 else if (c > 0)
  76.                         l = m + 1;
  77.                 else
  78.                         return &ctx->zip_table[m];
  79.         }
  80.         return NULL;
  81. }
  82.  
  83. static int
  84. xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf)
  85. {
  86.         z_stream stream;
  87.         unsigned char *inbuf;
  88.         int sig;
  89.         int version, general, method;
  90.         int namelength, extralength;
  91.         int code;
  92.  
  93.         fz_seek(ctx->file, ent->offset, 0);
  94.  
  95.         sig = getlong(ctx->file);
  96.         if (sig != ZIP_LOCAL_FILE_SIG)
  97.                 return fz_throw("wrong zip local file signature (0x%x)", sig);
  98.  
  99.         version = getshort(ctx->file);
  100.         general = getshort(ctx->file);
  101.         method = getshort(ctx->file);
  102.         (void) getshort(ctx->file); /* file time */
  103.         (void) getshort(ctx->file); /* file date */
  104.         (void) getlong(ctx->file); /* crc-32 */
  105.         (void) getlong(ctx->file); /* csize */
  106.         (void) getlong(ctx->file); /* usize */
  107.         namelength = getshort(ctx->file);
  108.         extralength = getshort(ctx->file);
  109.  
  110.         fz_seek(ctx->file, namelength + extralength, 1);
  111.  
  112.         if (method == 0)
  113.         {
  114.                 fz_read(ctx->file, outbuf, ent->usize);
  115.         }
  116.         else if (method == 8)
  117.         {
  118.                 inbuf = fz_malloc(ent->csize);
  119.  
  120.                 fz_read(ctx->file, inbuf, ent->csize);
  121.  
  122.                 memset(&stream, 0, sizeof(z_stream));
  123.                 stream.zalloc = (alloc_func) xps_zip_alloc_items;
  124.                 stream.zfree = (free_func) xps_zip_free;
  125.                 stream.opaque = ctx;
  126.                 stream.next_in = inbuf;
  127.                 stream.avail_in = ent->csize;
  128.                 stream.next_out = outbuf;
  129.                 stream.avail_out = ent->usize;
  130.  
  131.                 code = inflateInit2(&stream, -15);
  132.                 if (code != Z_OK)
  133.                         return fz_throw("zlib inflateInit2 error: %s", stream.msg);
  134.                 code = inflate(&stream, Z_FINISH);
  135.                 if (code != Z_STREAM_END)
  136.                 {
  137.                         inflateEnd(&stream);
  138.                         return fz_throw("zlib inflate error: %s", stream.msg);
  139.                 }
  140.                 code = inflateEnd(&stream);
  141.                 if (code != Z_OK)
  142.                         return fz_throw("zlib inflateEnd error: %s", stream.msg);
  143.  
  144.                 fz_free(inbuf);
  145.         }
  146.         else
  147.         {
  148.                 return fz_throw("unknown compression method (%d)", method);
  149.         }
  150.  
  151.         return fz_okay;
  152. }
  153.  
  154. /*
  155.  * Read the central directory in a zip file.
  156.  */
  157.  
  158. static int
  159. xps_read_zip_dir(xps_context *ctx, int start_offset)
  160. {
  161.         int sig;
  162.         int offset, count;
  163.         int namesize, metasize, commentsize;
  164.         int i;
  165.  
  166.         fz_seek(ctx->file, start_offset, 0);
  167.  
  168.         sig = getlong(ctx->file);
  169.         if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG)
  170.                 return fz_throw("wrong zip end of central directory signature (0x%x)", sig);
  171.  
  172.         (void) getshort(ctx->file); /* this disk */
  173.         (void) getshort(ctx->file); /* start disk */
  174.         (void) getshort(ctx->file); /* entries in this disk */
  175.         count = getshort(ctx->file); /* entries in central directory disk */
  176.         (void) getlong(ctx->file); /* size of central directory */
  177.         offset = getlong(ctx->file); /* offset to central directory */
  178.  
  179.         ctx->zip_count = count;
  180.         ctx->zip_table = fz_calloc(count, sizeof(xps_entry));
  181.         memset(ctx->zip_table, 0, sizeof(xps_entry) * count);
  182.  
  183.         fz_seek(ctx->file, offset, 0);
  184.  
  185.         for (i = 0; i < count; i++)
  186.         {
  187.                 sig = getlong(ctx->file);
  188.                 if (sig != ZIP_CENTRAL_DIRECTORY_SIG)
  189.                         return fz_throw("wrong zip central directory signature (0x%x)", sig);
  190.  
  191.                 (void) getshort(ctx->file); /* version made by */
  192.                 (void) getshort(ctx->file); /* version to extract */
  193.                 (void) getshort(ctx->file); /* general */
  194.                 (void) getshort(ctx->file); /* method */
  195.                 (void) getshort(ctx->file); /* last mod file time */
  196.                 (void) getshort(ctx->file); /* last mod file date */
  197.                 (void) getlong(ctx->file); /* crc-32 */
  198.                 ctx->zip_table[i].csize = getlong(ctx->file);
  199.                 ctx->zip_table[i].usize = getlong(ctx->file);
  200.                 namesize = getshort(ctx->file);
  201.                 metasize = getshort(ctx->file);
  202.                 commentsize = getshort(ctx->file);
  203.                 (void) getshort(ctx->file); /* disk number start */
  204.                 (void) getshort(ctx->file); /* int file atts */
  205.                 (void) getlong(ctx->file); /* ext file atts */
  206.                 ctx->zip_table[i].offset = getlong(ctx->file);
  207.  
  208.                 ctx->zip_table[i].name = fz_malloc(namesize + 1);
  209.                 fz_read(ctx->file, (unsigned char*)ctx->zip_table[i].name, namesize);
  210.                 ctx->zip_table[i].name[namesize] = 0;
  211.  
  212.                 fz_seek(ctx->file, metasize, 1);
  213.                 fz_seek(ctx->file, commentsize, 1);
  214.         }
  215.  
  216.         qsort(ctx->zip_table, count, sizeof(xps_entry), xps_compare_entries);
  217.  
  218.         return fz_okay;
  219. }
  220.  
  221. static int
  222. xps_find_and_read_zip_dir(xps_context *ctx)
  223. {
  224.         unsigned char buf[512];
  225.         int file_size, back, maxback;
  226.         int i, n;
  227.  
  228.         fz_seek(ctx->file, 0, SEEK_END);
  229.         file_size = fz_tell(ctx->file);
  230.  
  231.         maxback = MIN(file_size, 0xFFFF + sizeof buf);
  232.         back = MIN(maxback, sizeof buf);
  233.  
  234.         while (back < maxback)
  235.         {
  236.                 fz_seek(ctx->file, file_size - back, 0);
  237.  
  238.                 n = fz_read(ctx->file, buf, sizeof buf);
  239.                 if (n < 0)
  240.                         return fz_throw("cannot read end of central directory");
  241.  
  242.                 for (i = n - 4; i > 0; i--)
  243.                         if (!memcmp(buf + i, "PK\5\6", 4))
  244.                                 return xps_read_zip_dir(ctx, file_size - back + i);
  245.  
  246.                 back += sizeof buf - 4;
  247.         }
  248.  
  249.         return fz_throw("cannot find end of central directory");
  250. }
  251.  
  252. /*
  253.  * Read and interleave split parts from a ZIP file.
  254.  */
  255. static xps_part *
  256. xps_read_zip_part(xps_context *ctx, char *partname)
  257. {
  258.         char buf[2048];
  259.         xps_entry *ent;
  260.         xps_part *part;
  261.         int count, size, offset, i;
  262.         char *name;
  263.  
  264.         name = partname;
  265.         if (name[0] == '/')
  266.                 name ++;
  267.  
  268.         /* All in one piece */
  269.         ent = xps_find_zip_entry(ctx, name);
  270.         if (ent)
  271.         {
  272.                 part = xps_new_part(ctx, partname, ent->usize);
  273.                 xps_read_zip_entry(ctx, ent, part->data);
  274.                 return part;
  275.         }
  276.  
  277.         /* Count the number of pieces and their total size */
  278.         count = 0;
  279.         size = 0;
  280.         while (1)
  281.         {
  282.                 sprintf(buf, "%s/[%d].piece", name, count);
  283.                 ent = xps_find_zip_entry(ctx, buf);
  284.                 if (!ent)
  285.                 {
  286.                         sprintf(buf, "%s/[%d].last.piece", name, count);
  287.                         ent = xps_find_zip_entry(ctx, buf);
  288.                 }
  289.                 if (!ent)
  290.                         break;
  291.                 count ++;
  292.                 size += ent->usize;
  293.         }
  294.  
  295.         /* Inflate the pieces */
  296.         if (count)
  297.         {
  298.                 part = xps_new_part(ctx, partname, size);
  299.                 offset = 0;
  300.                 for (i = 0; i < count; i++)
  301.                 {
  302.                         if (i < count - 1)
  303.                                 sprintf(buf, "%s/[%d].piece", name, i);
  304.                         else
  305.                                 sprintf(buf, "%s/[%d].last.piece", name, i);
  306.                         ent = xps_find_zip_entry(ctx, buf);
  307.                         xps_read_zip_entry(ctx, ent, part->data + offset);
  308.                         offset += ent->usize;
  309.                 }
  310.                 return part;
  311.         }
  312.  
  313.         return NULL;
  314. }
  315.  
  316. /*
  317.  * Read and interleave split parts from files in the directory.
  318.  */
  319. static xps_part *
  320. xps_read_dir_part(xps_context *ctx, char *name)
  321. {
  322.         char buf[2048];
  323.         xps_part *part;
  324.         FILE *file;
  325.         int count, size, offset, i, n;
  326.  
  327.         fz_strlcpy(buf, ctx->directory, sizeof buf);
  328.         fz_strlcat(buf, name, sizeof buf);
  329.  
  330.         /* All in one piece */
  331.         file = fopen(buf, "rb");
  332.         if (file)
  333.         {
  334.                 fseek(file, 0, SEEK_END);
  335.                 size = ftell(file);
  336.                 fseek(file, 0, SEEK_SET);
  337.                 part = xps_new_part(ctx, name, size);
  338.                 fread(part->data, 1, size, file);
  339.                 fclose(file);
  340.                 return part;
  341.         }
  342.  
  343.         /* Count the number of pieces and their total size */
  344.         count = 0;
  345.         size = 0;
  346.         while (1)
  347.         {
  348.                 sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, count);
  349.                 file = fopen(buf, "rb");
  350.                 if (!file)
  351.                 {
  352.                         sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, count);
  353.                         file = fopen(buf, "rb");
  354.                 }
  355.                 if (!file)
  356.                         break;
  357.                 count ++;
  358.                 fseek(file, 0, SEEK_END);
  359.                 size += ftell(file);
  360.                 fclose(file);
  361.         }
  362.  
  363.         /* Inflate the pieces */
  364.         if (count)
  365.         {
  366.                 part = xps_new_part(ctx, name, size);
  367.                 offset = 0;
  368.                 for (i = 0; i < count; i++)
  369.                 {
  370.                         if (i < count - 1)
  371.                                 sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, i);
  372.                         else
  373.                                 sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, i);
  374.                         file = fopen(buf, "rb");
  375.                         n = fread(part->data + offset, 1, size - offset, file);
  376.                         offset += n;
  377.                         fclose(file);
  378.                 }
  379.                 return part;
  380.         }
  381.  
  382.         return NULL;
  383. }
  384.  
  385. xps_part *
  386. xps_read_part(xps_context *ctx, char *partname)
  387. {
  388.         if (ctx->directory)
  389.                 return xps_read_dir_part(ctx, partname);
  390.         return xps_read_zip_part(ctx, partname);
  391. }
  392.  
  393. static int
  394. xps_open_directory(xps_context **ctxp, char *directory)
  395. {
  396.         xps_context *ctx;
  397.         int code;
  398.  
  399.         ctx = fz_malloc(sizeof(xps_context));
  400.         memset(ctx, 0, sizeof(xps_context));
  401.  
  402.         ctx->directory = fz_strdup(directory);
  403.  
  404.         code = xps_read_page_list(ctx);
  405.         if (code)
  406.         {
  407.                 xps_free_context(ctx);
  408.                 return fz_rethrow(code, "cannot read page list");
  409.         }
  410.  
  411.         *ctxp = ctx;
  412.         return fz_okay;
  413. }
  414.  
  415. int
  416. xps_open_stream(xps_context **ctxp, fz_stream *file)
  417. {
  418.         xps_context *ctx;
  419.         int code;
  420.  
  421.         ctx = fz_malloc(sizeof(xps_context));
  422.         memset(ctx, 0, sizeof(xps_context));
  423.  
  424.         ctx->file = fz_keep_stream(file);
  425.  
  426.         code = xps_find_and_read_zip_dir(ctx);
  427.         if (code < 0)
  428.         {
  429.                 xps_free_context(ctx);
  430.                 return fz_rethrow(code, "cannot read zip central directory");
  431.         }
  432.  
  433.         code = xps_read_page_list(ctx);
  434.         if (code)
  435.         {
  436.                 xps_free_context(ctx);
  437.                 return fz_rethrow(code, "cannot read page list");
  438.         }
  439.  
  440.         *ctxp = ctx;
  441.         return fz_okay;
  442. }
  443.  
  444. int
  445. xps_open_file(xps_context **ctxp, char *filename)
  446. {
  447.         char buf[2048];
  448.         fz_stream *file;
  449.         char *p;
  450.         int code;
  451.  
  452.         if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels"))
  453.         {
  454.                 fz_strlcpy(buf, filename, sizeof buf);
  455.                 p = strstr(buf, "/_rels/.rels");
  456.                 if (!p)
  457.                         p = strstr(buf, "\\_rels\\.rels");
  458.                 *p = 0;
  459.                 return xps_open_directory(ctxp, buf);
  460.         }
  461.  
  462.         file = fz_open_file(filename);
  463.         if (!file)
  464.                 return fz_throw("cannot open file '%s': %s", filename, strerror(errno));
  465.  
  466.         code = xps_open_stream(ctxp, file);
  467.         fz_close(file);
  468.         if (code)
  469.                 return fz_rethrow(code, "cannot load document '%s'", filename);
  470.         return fz_okay;
  471. }
  472.  
  473. void
  474. xps_free_context(xps_context *ctx)
  475. {
  476.         xps_font_cache *font, *next;
  477.         int i;
  478.  
  479.         if (ctx->file)
  480.                 fz_close(ctx->file);
  481.  
  482.         for (i = 0; i < ctx->zip_count; i++)
  483.                 fz_free(ctx->zip_table[i].name);
  484.         fz_free(ctx->zip_table);
  485.  
  486.         font = ctx->font_table;
  487.         while (font)
  488.         {
  489.                 next = font->next;
  490.                 fz_drop_font(font->font);
  491.                 fz_free(font->name);
  492.                 fz_free(font);
  493.                 font = next;
  494.         }
  495.  
  496.         xps_free_page_list(ctx);
  497.  
  498.         fz_free(ctx->start_part);
  499.         fz_free(ctx->directory);
  500.         fz_free(ctx);
  501. }
  502.