pdf-recolor.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright (C) 2024 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. #include "mupdf/pdf.h"
  23. typedef struct {
  24. fz_colorspace *outcs;
  25. pdf_obj *outcs_obj;
  26. } recolor_data;
  27. static void
  28. color_rewrite(fz_context *ctx, void *opaque, pdf_obj **cs_obj, int *n, float color[FZ_MAX_COLORS])
  29. {
  30. recolor_data *rd = (recolor_data *)opaque;
  31. fz_colorspace *cs;
  32. float cols[4] = { 0 };
  33. if (pdf_name_eq(ctx, *cs_obj, PDF_NAME(Pattern)))
  34. return;
  35. if (pdf_name_eq(ctx, pdf_dict_get(ctx, *cs_obj, PDF_NAME(Type)), PDF_NAME(Pattern)))
  36. return;
  37. if (*n != 0)
  38. {
  39. cs = pdf_load_colorspace(ctx, *cs_obj);
  40. fz_try(ctx)
  41. {
  42. fz_convert_color(ctx, cs, color, rd->outcs, cols, NULL, fz_default_color_params);
  43. }
  44. fz_always(ctx)
  45. fz_drop_colorspace(ctx, cs);
  46. fz_catch(ctx)
  47. fz_rethrow(ctx);
  48. *n = rd->outcs->n;
  49. }
  50. pdf_drop_obj(ctx, *cs_obj);
  51. *cs_obj = rd->outcs_obj;
  52. memcpy(color, cols, sizeof(color[0])*4);
  53. }
  54. static void
  55. image_rewrite(fz_context *ctx, void *opaque, fz_image **image, fz_matrix ctm, pdf_obj *im_obj)
  56. {
  57. recolor_data *rd = (recolor_data *)opaque;
  58. fz_image *orig = *image;
  59. fz_pixmap *pix = NULL;
  60. fz_colorspace* dst_cs;
  61. fz_var(pix);
  62. if ((*image)->imagemask)
  63. return;
  64. dst_cs = rd->outcs;
  65. pix = fz_get_unscaled_pixmap_from_image(ctx, orig);
  66. fz_try(ctx)
  67. {
  68. if (pix->colorspace != dst_cs)
  69. {
  70. fz_pixmap *pix2 = fz_convert_pixmap(ctx, pix, dst_cs, NULL, NULL, fz_default_color_params, 1);
  71. fz_drop_pixmap(ctx, pix);
  72. pix = pix2;
  73. }
  74. *image = fz_new_image_from_pixmap(ctx, pix, orig->mask);
  75. fz_drop_image(ctx, orig);
  76. }
  77. fz_always(ctx)
  78. fz_drop_pixmap(ctx, pix);
  79. fz_catch(ctx)
  80. fz_rethrow(ctx);
  81. }
  82. static void
  83. vertex_rewrite(fz_context *ctx, void *opaque, fz_colorspace *dst_cs, float *d, fz_colorspace *src_cs, const float *s)
  84. {
  85. recolor_data *rd = (recolor_data *)opaque;
  86. fz_convert_color(ctx, src_cs, s, rd->outcs, d, NULL, fz_default_color_params);
  87. }
  88. static pdf_recolor_vertex *
  89. shade_rewrite(fz_context *ctx, void *opaque, fz_colorspace *src_cs, fz_colorspace **dst_cs)
  90. {
  91. recolor_data *rd = (recolor_data *)opaque;
  92. *dst_cs = rd->outcs;
  93. return vertex_rewrite;
  94. }
  95. static void
  96. rewrite_page_streams(fz_context *ctx, pdf_document *doc, int page_num, recolor_data *rd)
  97. {
  98. pdf_page *page = pdf_load_page(ctx, doc, page_num);
  99. pdf_filter_options options = { 0 };
  100. pdf_filter_factory list[2] = { 0 };
  101. pdf_color_filter_options copts = { 0 };
  102. pdf_annot *annot;
  103. copts.opaque = rd;
  104. copts.color_rewrite = color_rewrite;
  105. copts.image_rewrite = image_rewrite;
  106. copts.shade_rewrite = shade_rewrite;
  107. options.filters = list;
  108. options.recurse = 1;
  109. list[0].filter = pdf_new_color_filter;
  110. list[0].options = &copts;
  111. fz_try(ctx)
  112. {
  113. pdf_filter_page_contents(ctx, doc, page, &options);
  114. for (annot = pdf_first_annot(ctx, page); annot != NULL; annot = pdf_next_annot(ctx, annot))
  115. pdf_filter_annot_contents(ctx, doc, annot, &options);
  116. }
  117. fz_always(ctx)
  118. fz_drop_page(ctx, &page->super);
  119. fz_catch(ctx)
  120. fz_rethrow(ctx);
  121. }
  122. void pdf_recolor_page(fz_context *ctx, pdf_document *doc, int pagenum, const pdf_recolor_options *opts)
  123. {
  124. recolor_data rd = { 0 };
  125. if (opts == NULL)
  126. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Options must be supplied");
  127. switch (opts->num_comp)
  128. {
  129. case 1:
  130. rd.outcs = fz_device_gray(ctx);
  131. rd.outcs_obj = PDF_NAME(DeviceGray);
  132. break;
  133. case 3:
  134. rd.outcs = fz_device_rgb(ctx);
  135. rd.outcs_obj = PDF_NAME(DeviceRGB);
  136. break;
  137. case 4:
  138. rd.outcs = fz_device_cmyk(ctx);
  139. rd.outcs_obj = PDF_NAME(DeviceCMYK);
  140. break;
  141. default:
  142. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported number of components");
  143. }
  144. rewrite_page_streams(ctx, doc, pagenum, &rd);
  145. }
  146. void pdf_remove_output_intents(fz_context *ctx, pdf_document *doc)
  147. {
  148. pdf_dict_del(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)), PDF_NAME(OutputIntents));
  149. }