comparison mupdf-source/source/pdf/pdf-recolor.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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
23 #include "mupdf/pdf.h"
24
25 typedef struct {
26 fz_colorspace *outcs;
27 pdf_obj *outcs_obj;
28 } recolor_data;
29
30 static void
31 color_rewrite(fz_context *ctx, void *opaque, pdf_obj **cs_obj, int *n, float color[FZ_MAX_COLORS])
32 {
33 recolor_data *rd = (recolor_data *)opaque;
34 fz_colorspace *cs;
35 float cols[4] = { 0 };
36
37 if (pdf_name_eq(ctx, *cs_obj, PDF_NAME(Pattern)))
38 return;
39 if (pdf_name_eq(ctx, pdf_dict_get(ctx, *cs_obj, PDF_NAME(Type)), PDF_NAME(Pattern)))
40 return;
41
42 if (*n != 0)
43 {
44 cs = pdf_load_colorspace(ctx, *cs_obj);
45
46 fz_try(ctx)
47 {
48 fz_convert_color(ctx, cs, color, rd->outcs, cols, NULL, fz_default_color_params);
49 }
50 fz_always(ctx)
51 fz_drop_colorspace(ctx, cs);
52 fz_catch(ctx)
53 fz_rethrow(ctx);
54 *n = rd->outcs->n;
55 }
56
57 pdf_drop_obj(ctx, *cs_obj);
58 *cs_obj = rd->outcs_obj;
59 memcpy(color, cols, sizeof(color[0])*4);
60 }
61
62 static void
63 image_rewrite(fz_context *ctx, void *opaque, fz_image **image, fz_matrix ctm, pdf_obj *im_obj)
64 {
65 recolor_data *rd = (recolor_data *)opaque;
66 fz_image *orig = *image;
67 fz_pixmap *pix = NULL;
68 fz_colorspace* dst_cs;
69
70 fz_var(pix);
71
72 if ((*image)->imagemask)
73 return;
74
75 dst_cs = rd->outcs;
76 pix = fz_get_unscaled_pixmap_from_image(ctx, orig);
77
78 fz_try(ctx)
79 {
80 if (pix->colorspace != dst_cs)
81 {
82 fz_pixmap *pix2 = fz_convert_pixmap(ctx, pix, dst_cs, NULL, NULL, fz_default_color_params, 1);
83 fz_drop_pixmap(ctx, pix);
84 pix = pix2;
85 }
86
87 *image = fz_new_image_from_pixmap(ctx, pix, orig->mask);
88 fz_drop_image(ctx, orig);
89 }
90 fz_always(ctx)
91 fz_drop_pixmap(ctx, pix);
92 fz_catch(ctx)
93 fz_rethrow(ctx);
94 }
95
96 static void
97 vertex_rewrite(fz_context *ctx, void *opaque, fz_colorspace *dst_cs, float *d, fz_colorspace *src_cs, const float *s)
98 {
99 recolor_data *rd = (recolor_data *)opaque;
100
101 fz_convert_color(ctx, src_cs, s, rd->outcs, d, NULL, fz_default_color_params);
102 }
103
104 static pdf_recolor_vertex *
105 shade_rewrite(fz_context *ctx, void *opaque, fz_colorspace *src_cs, fz_colorspace **dst_cs)
106 {
107 recolor_data *rd = (recolor_data *)opaque;
108
109 *dst_cs = rd->outcs;
110
111 return vertex_rewrite;
112 }
113
114 static void
115 rewrite_page_streams(fz_context *ctx, pdf_document *doc, int page_num, recolor_data *rd)
116 {
117 pdf_page *page = pdf_load_page(ctx, doc, page_num);
118 pdf_filter_options options = { 0 };
119 pdf_filter_factory list[2] = { 0 };
120 pdf_color_filter_options copts = { 0 };
121 pdf_annot *annot;
122
123 copts.opaque = rd;
124 copts.color_rewrite = color_rewrite;
125 copts.image_rewrite = image_rewrite;
126 copts.shade_rewrite = shade_rewrite;
127 options.filters = list;
128 options.recurse = 1;
129 list[0].filter = pdf_new_color_filter;
130 list[0].options = &copts;
131
132 fz_try(ctx)
133 {
134 pdf_filter_page_contents(ctx, doc, page, &options);
135
136 for (annot = pdf_first_annot(ctx, page); annot != NULL; annot = pdf_next_annot(ctx, annot))
137 pdf_filter_annot_contents(ctx, doc, annot, &options);
138 }
139 fz_always(ctx)
140 fz_drop_page(ctx, &page->super);
141 fz_catch(ctx)
142 fz_rethrow(ctx);
143 }
144
145
146 void pdf_recolor_page(fz_context *ctx, pdf_document *doc, int pagenum, const pdf_recolor_options *opts)
147 {
148 recolor_data rd = { 0 };
149
150 if (opts == NULL)
151 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Options must be supplied");
152
153 switch (opts->num_comp)
154 {
155 case 1:
156 rd.outcs = fz_device_gray(ctx);
157 rd.outcs_obj = PDF_NAME(DeviceGray);
158 break;
159 case 3:
160 rd.outcs = fz_device_rgb(ctx);
161 rd.outcs_obj = PDF_NAME(DeviceRGB);
162 break;
163 case 4:
164 rd.outcs = fz_device_cmyk(ctx);
165 rd.outcs_obj = PDF_NAME(DeviceCMYK);
166 break;
167 default:
168 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported number of components");
169 }
170
171 rewrite_page_streams(ctx, doc, pagenum, &rd);
172 }
173
174 void pdf_remove_output_intents(fz_context *ctx, pdf_document *doc)
175 {
176 pdf_dict_del(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)), PDF_NAME(OutputIntents));
177 }