Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. #include "fitz.h"
  2. #include "mupdf.h"
  3.  
  4. struct info
  5. {
  6.         fz_obj *resources;
  7.         fz_obj *mediabox;
  8.         fz_obj *cropbox;
  9.         fz_obj *rotate;
  10. };
  11.  
  12. int
  13. pdf_count_pages(pdf_xref *xref)
  14. {
  15.         return xref->page_len;
  16. }
  17.  
  18. int
  19. pdf_find_page_number(pdf_xref *xref, fz_obj *page)
  20. {
  21.         int i, num = fz_to_num(page);
  22.         for (i = 0; i < xref->page_len; i++)
  23.                 if (num == fz_to_num(xref->page_refs[i]))
  24.                         return i;
  25.         return -1;
  26. }
  27.  
  28. static void
  29. pdf_load_page_tree_node(pdf_xref *xref, fz_obj *node, struct info info)
  30. {
  31.         fz_obj *dict, *kids, *count;
  32.         fz_obj *obj, *tmp;
  33.         int i, n;
  34.  
  35.         /* prevent infinite recursion */
  36.         if (fz_dict_gets(node, ".seen"))
  37.                 return;
  38.  
  39.         kids = fz_dict_gets(node, "Kids");
  40.         count = fz_dict_gets(node, "Count");
  41.  
  42.         if (fz_is_array(kids) && fz_is_int(count))
  43.         {
  44.                 obj = fz_dict_gets(node, "Resources");
  45.                 if (obj)
  46.                         info.resources = obj;
  47.                 obj = fz_dict_gets(node, "MediaBox");
  48.                 if (obj)
  49.                         info.mediabox = obj;
  50.                 obj = fz_dict_gets(node, "CropBox");
  51.                 if (obj)
  52.                         info.cropbox = obj;
  53.                 obj = fz_dict_gets(node, "Rotate");
  54.                 if (obj)
  55.                         info.rotate = obj;
  56.  
  57.                 tmp = fz_new_null();
  58.                 fz_dict_puts(node, ".seen", tmp);
  59.                 fz_drop_obj(tmp);
  60.  
  61.                 n = fz_array_len(kids);
  62.                 for (i = 0; i < n; i++)
  63.                 {
  64.                         obj = fz_array_get(kids, i);
  65.                         pdf_load_page_tree_node(xref, obj, info);
  66.                 }
  67.  
  68.                 fz_dict_dels(node, ".seen");
  69.         }
  70.         else
  71.         {
  72.                 dict = fz_resolve_indirect(node);
  73.  
  74.                 if (info.resources && !fz_dict_gets(dict, "Resources"))
  75.                         fz_dict_puts(dict, "Resources", info.resources);
  76.                 if (info.mediabox && !fz_dict_gets(dict, "MediaBox"))
  77.                         fz_dict_puts(dict, "MediaBox", info.mediabox);
  78.                 if (info.cropbox && !fz_dict_gets(dict, "CropBox"))
  79.                         fz_dict_puts(dict, "CropBox", info.cropbox);
  80.                 if (info.rotate && !fz_dict_gets(dict, "Rotate"))
  81.                         fz_dict_puts(dict, "Rotate", info.rotate);
  82.  
  83.                 if (xref->page_len == xref->page_cap)
  84.                 {
  85.                         fz_warn("found more pages than expected");
  86.                         xref->page_cap ++;
  87.                         xref->page_refs = fz_realloc(xref->page_refs, xref->page_cap, sizeof(fz_obj*));
  88.                         xref->page_objs = fz_realloc(xref->page_objs, xref->page_cap, sizeof(fz_obj*));
  89.                 }
  90.  
  91.                 xref->page_refs[xref->page_len] = fz_keep_obj(node);
  92.                 xref->page_objs[xref->page_len] = fz_keep_obj(dict);
  93.                 xref->page_len ++;
  94.         }
  95. }
  96.  
  97. fz_error
  98. pdf_load_page_tree(pdf_xref *xref)
  99. {
  100.         struct info info;
  101.         fz_obj *catalog = fz_dict_gets(xref->trailer, "Root");
  102.         fz_obj *pages = fz_dict_gets(catalog, "Pages");
  103.         fz_obj *count = fz_dict_gets(pages, "Count");
  104.  
  105.         if (!fz_is_dict(pages))
  106.                 return fz_throw("missing page tree");
  107.         if (!fz_is_int(count))
  108.                 return fz_throw("missing page count");
  109.  
  110.         xref->page_cap = fz_to_int(count);
  111.         xref->page_len = 0;
  112.         xref->page_refs = fz_calloc(xref->page_cap, sizeof(fz_obj*));
  113.         xref->page_objs = fz_calloc(xref->page_cap, sizeof(fz_obj*));
  114.  
  115.         info.resources = NULL;
  116.         info.mediabox = NULL;
  117.         info.cropbox = NULL;
  118.         info.rotate = NULL;
  119.  
  120.         pdf_load_page_tree_node(xref, pages, info);
  121.  
  122.         return fz_okay;
  123. }
  124.  
  125. /* We need to know whether to install a page-level transparency group */
  126.  
  127. static int pdf_resources_use_blending(fz_obj *rdb);
  128.  
  129. static int
  130. pdf_extgstate_uses_blending(fz_obj *dict)
  131. {
  132.         fz_obj *obj = fz_dict_gets(dict, "BM");
  133.         if (fz_is_name(obj) && strcmp(fz_to_name(obj), "Normal"))
  134.                 return 1;
  135.         return 0;
  136. }
  137.  
  138. static int
  139. pdf_pattern_uses_blending(fz_obj *dict)
  140. {
  141.         fz_obj *obj;
  142.         obj = fz_dict_gets(dict, "Resources");
  143.         if (pdf_resources_use_blending(obj))
  144.                 return 1;
  145.         obj = fz_dict_gets(dict, "ExtGState");
  146.         if (pdf_extgstate_uses_blending(obj))
  147.                 return 1;
  148.         return 0;
  149. }
  150.  
  151. static int
  152. pdf_xobject_uses_blending(fz_obj *dict)
  153. {
  154.         fz_obj *obj = fz_dict_gets(dict, "Resources");
  155.         if (pdf_resources_use_blending(obj))
  156.                 return 1;
  157.         return 0;
  158. }
  159.  
  160. static int
  161. pdf_resources_use_blending(fz_obj *rdb)
  162. {
  163.         fz_obj *dict;
  164.         fz_obj *tmp;
  165.         int i;
  166.  
  167.         if (!rdb)
  168.                 return 0;
  169.  
  170.         /* stop on cyclic resource dependencies */
  171.         if (fz_dict_gets(rdb, ".useBM"))
  172.                 return fz_to_bool(fz_dict_gets(rdb, ".useBM"));
  173.  
  174.         tmp = fz_new_bool(0);
  175.         fz_dict_puts(rdb, ".useBM", tmp);
  176.         fz_drop_obj(tmp);
  177.  
  178.         dict = fz_dict_gets(rdb, "ExtGState");
  179.         for (i = 0; i < fz_dict_len(dict); i++)
  180.                 if (pdf_extgstate_uses_blending(fz_dict_get_val(dict, i)))
  181.                         goto found;
  182.  
  183.         dict = fz_dict_gets(rdb, "Pattern");
  184.         for (i = 0; i < fz_dict_len(dict); i++)
  185.                 if (pdf_pattern_uses_blending(fz_dict_get_val(dict, i)))
  186.                         goto found;
  187.  
  188.         dict = fz_dict_gets(rdb, "XObject");
  189.         for (i = 0; i < fz_dict_len(dict); i++)
  190.                 if (pdf_xobject_uses_blending(fz_dict_get_val(dict, i)))
  191.                         goto found;
  192.  
  193.         return 0;
  194.  
  195. found:
  196.         tmp = fz_new_bool(1);
  197.         fz_dict_puts(rdb, ".useBM", tmp);
  198.         fz_drop_obj(tmp);
  199.         return 1;
  200. }
  201.  
  202. /* we need to combine all sub-streams into one for the content stream interpreter */
  203.  
  204. static fz_error
  205. pdf_load_page_contents_array(fz_buffer **bigbufp, pdf_xref *xref, fz_obj *list)
  206. {
  207.         fz_error error;
  208.         fz_buffer *big;
  209.         fz_buffer *one;
  210.         int i, n;
  211.  
  212.         big = fz_new_buffer(32 * 1024);
  213.  
  214.         n = fz_array_len(list);
  215.         for (i = 0; i < n; i++)
  216.         {
  217.                 fz_obj *stm = fz_array_get(list, i);
  218.                 error = pdf_load_stream(&one, xref, fz_to_num(stm), fz_to_gen(stm));
  219.                 if (error)
  220.                 {
  221.                         fz_catch(error, "cannot load content stream part %d/%d", i + 1, n);
  222.                         continue;
  223.                 }
  224.  
  225.                 if (big->len + one->len + 1 > big->cap)
  226.                         fz_resize_buffer(big, big->len + one->len + 1);
  227.                 memcpy(big->data + big->len, one->data, one->len);
  228.                 big->data[big->len + one->len] = ' ';
  229.                 big->len += one->len + 1;
  230.  
  231.                 fz_drop_buffer(one);
  232.         }
  233.  
  234.         if (n > 0 && big->len == 0)
  235.         {
  236.                 fz_drop_buffer(big);
  237.                 return fz_throw("cannot load content stream");
  238.         }
  239.  
  240.         *bigbufp = big;
  241.         return fz_okay;
  242. }
  243.  
  244. static fz_error
  245. pdf_load_page_contents(fz_buffer **bufp, pdf_xref *xref, fz_obj *obj)
  246. {
  247.         fz_error error;
  248.  
  249.         if (fz_is_array(obj))
  250.         {
  251.                 error = pdf_load_page_contents_array(bufp, xref, obj);
  252.                 if (error)
  253.                         return fz_rethrow(error, "cannot load content stream array");
  254.         }
  255.         else if (pdf_is_stream(xref, fz_to_num(obj), fz_to_gen(obj)))
  256.         {
  257.                 error = pdf_load_stream(bufp, xref, fz_to_num(obj), fz_to_gen(obj));
  258.                 if (error)
  259.                         return fz_rethrow(error, "cannot load content stream (%d 0 R)", fz_to_num(obj));
  260.         }
  261.         else
  262.         {
  263.                 fz_warn("page contents missing, leaving page blank");
  264.                 *bufp = fz_new_buffer(0);
  265.         }
  266.  
  267.         return fz_okay;
  268. }
  269.  
  270. fz_error
  271. pdf_load_page(pdf_page **pagep, pdf_xref *xref, int number)
  272. {
  273.         fz_error error;
  274.         pdf_page *page;
  275.         pdf_annot *annot;
  276.         fz_obj *pageobj, *pageref;
  277.         fz_obj *obj;
  278.         fz_bbox bbox;
  279.  
  280.         if (number < 0 || number >= xref->page_len)
  281.                 return fz_throw("cannot find page %d", number + 1);
  282.  
  283.         /* Ensure that we have a store for resource objects */
  284.         if (!xref->store)
  285.                 xref->store = pdf_new_store();
  286.  
  287.         pageobj = xref->page_objs[number];
  288.         pageref = xref->page_refs[number];
  289.  
  290.         page = fz_malloc(sizeof(pdf_page));
  291.         page->resources = NULL;
  292.         page->contents = NULL;
  293.         page->transparency = 0;
  294.         page->links = NULL;
  295.         page->annots = NULL;
  296.  
  297.         obj = fz_dict_gets(pageobj, "MediaBox");
  298.         bbox = fz_round_rect(pdf_to_rect(obj));
  299.         if (fz_is_empty_rect(pdf_to_rect(obj)))
  300.         {
  301.                 fz_warn("cannot find page size for page %d", number + 1);
  302.                 bbox.x0 = 0;
  303.                 bbox.y0 = 0;
  304.                 bbox.x1 = 612;
  305.                 bbox.y1 = 792;
  306.         }
  307.  
  308.         obj = fz_dict_gets(pageobj, "CropBox");
  309.         if (fz_is_array(obj))
  310.         {
  311.                 fz_bbox cropbox = fz_round_rect(pdf_to_rect(obj));
  312.                 bbox = fz_intersect_bbox(bbox, cropbox);
  313.         }
  314.  
  315.         page->mediabox.x0 = MIN(bbox.x0, bbox.x1);
  316.         page->mediabox.y0 = MIN(bbox.y0, bbox.y1);
  317.         page->mediabox.x1 = MAX(bbox.x0, bbox.x1);
  318.         page->mediabox.y1 = MAX(bbox.y0, bbox.y1);
  319.  
  320.         if (page->mediabox.x1 - page->mediabox.x0 < 1 || page->mediabox.y1 - page->mediabox.y0 < 1)
  321.         {
  322.                 fz_warn("invalid page size in page %d", number + 1);
  323.                 page->mediabox = fz_unit_rect;
  324.         }
  325.  
  326.         page->rotate = fz_to_int(fz_dict_gets(pageobj, "Rotate"));
  327.  
  328.         obj = fz_dict_gets(pageobj, "Annots");
  329.         if (obj)
  330.         {
  331.                 pdf_load_links(&page->links, xref, obj);
  332.                 pdf_load_annots(&page->annots, xref, obj);
  333.         }
  334.  
  335.         page->resources = fz_dict_gets(pageobj, "Resources");
  336.         if (page->resources)
  337.                 fz_keep_obj(page->resources);
  338.  
  339.         obj = fz_dict_gets(pageobj, "Contents");
  340.         error = pdf_load_page_contents(&page->contents, xref, obj);
  341.         if (error)
  342.         {
  343.                 pdf_free_page(page);
  344.                 return fz_rethrow(error, "cannot load page %d contents (%d 0 R)", number + 1, fz_to_num(pageref));
  345.         }
  346.  
  347.         if (pdf_resources_use_blending(page->resources))
  348.                 page->transparency = 1;
  349.  
  350.         for (annot = page->annots; annot && !page->transparency; annot = annot->next)
  351.                 if (pdf_resources_use_blending(annot->ap->resources))
  352.                         page->transparency = 1;
  353.  
  354.         *pagep = page;
  355.         return fz_okay;
  356. }
  357.  
  358. void
  359. pdf_free_page(pdf_page *page)
  360. {
  361.         if (page->resources)
  362.                 fz_drop_obj(page->resources);
  363.         if (page->contents)
  364.                 fz_drop_buffer(page->contents);
  365.         if (page->links)
  366.                 pdf_free_link(page->links);
  367.         if (page->annots)
  368.                 pdf_free_annot(page->annots);
  369.         fz_free(page);
  370. }
  371.