1 /*********************************************************************
2 eps -- functions to write EPS files.
3 This is part of GNU Astronomy Utilities (Gnuastro) package.
4
5 Original author:
6 Mohammad Akhlaghi <mohammad@akhlaghi.org>
7 Contributing author(s):
8 Copyright (C) 2015-2021, Free Software Foundation, Inc.
9
10 Gnuastro is free software: you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation, either version 3 of the License, or (at your
13 option) any later version.
14
15 Gnuastro is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
22 **********************************************************************/
23 #include <config.h>
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <gnuastro/eps.h>
32 #include <gnuastro/list.h>
33
34 #include <gnuastro-internal/timing.h>
35 #include <gnuastro-internal/checkset.h>
36
37
38
39
40
41
42 /*************************************************************
43 ************** Acceptable EPS names ***************
44 *************************************************************/
45 int
gal_eps_name_is_eps(char * name)46 gal_eps_name_is_eps(char *name)
47 {
48 size_t len;
49 if(name)
50 {
51 len=strlen(name);
52 if ( ( len>=3 && strcmp(&name[len-3], "eps") == 0 )
53 || ( len>=3 && strcmp(&name[len-3], "EPS") == 0 )
54 || ( len>=4 && strcmp(&name[len-4], "epsf") == 0 )
55 || ( len>=4 && strcmp(&name[len-4], "epsi") == 0 ) )
56 return 1;
57 else
58 return 0;
59 }
60 else return 0;
61 }
62
63
64
65
66
67 int
gal_eps_suffix_is_eps(char * name)68 gal_eps_suffix_is_eps(char *name)
69 {
70 if(name)
71 {
72 if (strcmp(name, "eps") == 0 || strcmp(name, ".eps") == 0
73 || strcmp(name, "EPS") == 0 || strcmp(name, ".EPS") == 0
74 || strcmp(name, "epsf") == 0 || strcmp(name, ".epsf") == 0
75 || strcmp(name, "epsi") == 0 || strcmp(name, ".epsi") == 0)
76 return 1;
77 else
78 return 0;
79 }
80 else return 0;
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 /*************************************************************
103 ************** Write an EPS image **************
104 *************************************************************/
105 static int
eps_is_binary(gal_data_t * in,uint8_t * bitone)106 eps_is_binary(gal_data_t *in, uint8_t *bitone)
107 {
108 gal_data_t *channel;
109 uint8_t second_set=0;
110 unsigned char *i, *fi, first=0, second=0;
111
112 /* Go through all the channels. */
113 for(channel=in; channel!=NULL; channel=channel->next)
114 {
115 /* Go through all the values and see if there is more than two values
116 in the array. */
117 fi = (i=channel->array) + channel->size;
118 first=*i;
119 do
120 if(*i!=first)
121 {
122 if(second_set)
123 { if(*i!=second) break; }
124 else
125 { second=*i; second_set=1; }
126 }
127 while(++i<fi);
128
129 /* If we didn't get to the end of the array, then we have a
130 multi-valued (not binary) image. */
131 if(i!=fi)
132 return 0;
133 }
134
135 /* If we get to this point, then all the channels were binary, so return
136 success. */
137 *bitone = first>second ? first : second;
138 return 1;
139 }
140
141
142
143
144
145 /* Convert the channels into into a 0 and 1 bit stream. This function is
146 only called when the image is binary (has only two values). NOTE: each
147 row has to have an integer number of bytes, so when the number of pixels
148 in a row is not a multiple of 8, we'll add one. */
149 static gal_data_t *
eps_convert_to_bitstream(gal_data_t * in,size_t * numbytes,uint8_t bitone)150 eps_convert_to_bitstream(gal_data_t *in, size_t *numbytes, uint8_t bitone)
151 {
152 size_t i, j, k, bytesinrow;
153 gal_data_t *channel, *out=NULL;
154 unsigned char *bits, byte, curbit, *arr;
155 size_t s0=in->dsize[0], s1=in->dsize[1];
156
157 /* Find the size values and allocate the array. */
158 if( s1 % 8 ) bytesinrow = s1/8 + 1;
159 else bytesinrow = s1/8;
160 *numbytes = bytesinrow*s0;
161
162 /* Go over all the channels. */
163 for(channel=in; channel!=NULL; channel=channel->next)
164 {
165 /* Allocate the array. Note that we currently don't have an
166 allocation system for bits, so we'll allocate space in bytes, then
167 convert */
168 gal_list_data_add_alloc(&out, NULL, GAL_TYPE_UINT8, 1, numbytes,
169 NULL, 0, -1, 1, NULL, NULL, NULL);
170 out->type=GAL_TYPE_BIT;
171 bits=out->array;
172
173 /* Put the values in. */
174 arr=channel->array;
175 for(i=0;i<s0;++i) /* i*s0+j is the byte, not bit position. */
176 for(j=0;j<bytesinrow;++j)
177 {
178 /* Set the 8 bits to zero. */
179 byte=0;
180
181 /* Current bit position. */
182 curbit=0x80;
183
184 /* Write the next 8 values as bits. */
185 for(k=0;k<8;++k)
186 {
187 if( j*8+k < s1 )
188 {
189 if(arr[i*s1+j*8+k]==bitone)
190 byte |= curbit;
191 curbit >>= 1;
192 }
193 else break;
194 }
195
196 /* Write the byte into the array. */
197 bits[i*bytesinrow+j]=byte;
198 }
199 }
200
201 /* Reverse the list and return it. */
202 gal_list_data_reverse(&out);
203 return out;
204 }
205
206
207
208
209
210 static void
eps_write_hex(gal_data_t * write,FILE * fp,size_t numbytes)211 eps_write_hex(gal_data_t *write, FILE *fp, size_t numbytes)
212 {
213 unsigned char *arr;
214 gal_data_t *channel;
215 size_t i=0, j, elem_for_newline=35;
216
217 for(channel=write; channel!=NULL; channel=channel->next)
218 {
219 if(channel->status) /* A blank channel has status==1. */
220 fprintf(fp, "{<00>} %% Channel %zu is blank\n", i);
221 else
222 {
223 arr=channel->array;
224 fprintf(fp, "{<");
225 for(j=0;j<numbytes;++j)
226 {
227 fprintf(fp, "%02X", arr[j]);
228 if(j%elem_for_newline==0) fprintf(fp, "\n");
229 }
230 fprintf(fp, ">}\n");
231 }
232 ++i;
233 }
234 }
235
236
237
238
239
240 static void
eps_write_ascii85(gal_data_t * write,FILE * fp,size_t numbytes)241 eps_write_ascii85(gal_data_t *write, FILE *fp, size_t numbytes)
242 {
243 unsigned char *arr;
244 gal_data_t *channel;
245 uint32_t anint, base;
246 size_t i=0, j, k, elem_for_newline=15; /* 15*5=75 */
247
248 for(channel=write; channel!=NULL; channel=channel->next)
249 {
250 if(channel->status)
251 fprintf(fp, "{<00>} %% Channel %zu is blank\n", i);
252 else
253 {
254 arr=channel->array;
255 fprintf(fp, "{<~");
256 for(j=0;j<numbytes;j+=4)
257 {
258 /* This is the last four bytes */
259 if(numbytes-j<4)
260 {
261 anint=arr[j]*256*256*256;
262 if(numbytes-j>1) anint+=arr[j+1]*256*256;
263 if(numbytes-j==3) anint+=arr[j+2]*256;
264 }
265 else
266 anint=( arr[j]*256*256*256 + arr[j+1]*256*256
267 + arr[j+2]*256 + arr[j+3] );
268
269 /* If all four bytes are zero, then just print 'z'. */
270 if(anint==0) fprintf(fp, "z");
271 else
272 {
273 /* To check, just change the fprintf below to printf:
274 printf("\n\n");
275 printf("%u %u %u %u\n", in[i], in[i+1],
276 in[i+2], in[i+3]);
277 */
278 base=85*85*85*85;
279 /* Do the ASCII85 encoding: */
280 for(k=0;k<5;++k)
281 {
282 fprintf(fp, "%c", anint/base+33);
283 anint%=base;
284 base/=85;
285 }
286 }
287 /* Go to the next line if on the right place: */
288 if(j%elem_for_newline==0) fprintf(fp, "\n");
289 }
290 fprintf(fp, "~>}\n");
291 }
292 ++i;
293 }
294 }
295
296
297
298
299
300 static void
eps_write_image(gal_data_t * in,FILE * fp,int hex,int dontoptimize)301 eps_write_image(gal_data_t *in, FILE *fp, int hex, int dontoptimize)
302 {
303 int bpc=8;
304 uint8_t bitone;
305 gal_data_t *write;
306 size_t i, numbytes, *dsize=in->dsize;
307 size_t numch=gal_list_data_number(in);
308
309 /* Set the number of bits per component. */
310 if( numch==1 && dontoptimize==0 && eps_is_binary(in, &bitone) )
311 {
312 bpc=1;
313 write=eps_convert_to_bitstream(in, &numbytes, bitone);
314 }
315 else
316 {
317 write=in;
318 numbytes=in->size;
319 }
320
321 /* Write the basic meta data. */
322 switch(numch)
323 {
324 case 1: fprintf(fp, "/DeviceGray setcolorspace\n"); break;
325 case 3: fprintf(fp, "/DeviceRGB setcolorspace\n"); break;
326 case 4: fprintf(fp, "/DeviceCMYK setcolorspace\n"); break;
327 default:
328 error(EXIT_FAILURE, 0, "%s: a bug! The number of channels (%zu) is "
329 "not 1, 3 or 4. Please contact us so we can find the issue and "
330 "fix it", __func__, numch);
331 }
332 fprintf(fp, "<<\n");
333 fprintf(fp, " /ImageType 1\n");
334 fprintf(fp, " /Width %zu\n", dsize[1]);
335 fprintf(fp, " /Height %zu\n", dsize[0]);
336 fprintf(fp, " /ImageMatrix [ %zu 0 0 %zu 0 0 ]\n", dsize[1], dsize[0]);
337 fprintf(fp, " /MultipleDataSources true\n");
338 fprintf(fp, " /BitsPerComponent %d\n", bpc);
339 fprintf(fp, " /Decode[");
340 for(i=0;i<numch;++i) {fprintf(fp, " 0 1");} fprintf(fp, " ]\n");
341 fprintf(fp, " /Interpolate false\n");
342 fprintf(fp, " /DataSource [\n");
343
344 /* Based on the encoding, write the contents of the image. */
345 if(hex)
346 eps_write_hex(write, fp, numbytes);
347 else
348 eps_write_ascii85(write, fp, numbytes);
349
350 /* Finish the file. */
351 fprintf(fp, " ]\n");
352 fprintf(fp, ">>\n");
353 fprintf(fp, "image\n\n");
354
355 /* Clean up. */
356 if(write!=in)
357 gal_list_data_free(write);
358 }
359
360
361
362
363
364 void
gal_eps_to_pt(float widthincm,size_t * dsize,size_t * w_h_in_pt)365 gal_eps_to_pt(float widthincm, size_t *dsize, size_t *w_h_in_pt)
366 {
367 w_h_in_pt[0] = widthincm*72.0f/2.54f;
368 w_h_in_pt[1] = (float)( dsize[0] * w_h_in_pt[0] )/(float)(dsize[1]);
369 }
370
371
372
373
374
375 void
gal_eps_write(gal_data_t * in,char * filename,float widthincm,uint32_t borderwidth,int hex,int dontoptimize,int forpdf)376 gal_eps_write(gal_data_t *in, char *filename, float widthincm,
377 uint32_t borderwidth, int hex, int dontoptimize, int forpdf)
378 {
379 FILE *fp;
380 float hbw;
381 time_t rawtime;
382 size_t numch=gal_list_data_number(in);
383 size_t w_h_in_pt[2], *dsize=in->dsize;
384
385 /* Sanity checks. */
386 if(numch==2 || numch>4)
387 error(EXIT_FAILURE, 0, "%s: only 1, 3, and 4 color channels are "
388 "acceptable, input is a list of %zu data sets", __func__, numch);
389 if(in->type!=GAL_TYPE_UINT8)
390 error(EXIT_FAILURE, 0, "%s: input has a '%s' type, but JPEG images can "
391 "only have a 'uint8' type", __func__, gal_type_name(in->type, 1));
392
393
394 /* Read the time to write in the output. */
395 time(&rawtime);
396
397
398 /* Find the bounding box */
399 hbw=(float)borderwidth/2.0;
400 gal_eps_to_pt(widthincm, dsize, w_h_in_pt);
401
402
403 /* Open the output file and write the top comments. */
404 errno=0;
405 fp=fopen(filename, "w");
406 if(fp==NULL)
407 error(EXIT_FAILURE, errno, "%s", filename);
408 fprintf(fp, "%%!PS-Adobe-3.0 EPSF-3.0\n");
409 fprintf(fp, "%%%%BoundingBox: 0 0 %zu %zu\n", w_h_in_pt[0]+2*borderwidth,
410 w_h_in_pt[1]+2*borderwidth);
411 fprintf(fp, "%%%%Creator: %s\n", PACKAGE_STRING);
412 fprintf(fp, "%%%%CreationDate: %s", ctime(&rawtime));
413 fprintf(fp, "%%%%LanuageLevel: 3\n");
414 fprintf(fp, "%%%%EndComments\n\n");
415 if(forpdf==0)
416 fprintf(fp, "gsave\n\n");
417
418
419 /* Commands to draw the border: */
420 if(borderwidth)
421 {
422 fprintf(fp, "%% Draw the border:\n");
423 fprintf(fp, "0 setgray\n");
424 fprintf(fp, "%d setlinewidth\n", borderwidth);
425 fprintf(fp, "%.1f %.1f moveto\n", hbw, hbw);
426 fprintf(fp, "0 %zu rlineto\n", w_h_in_pt[1]+borderwidth);
427 fprintf(fp, "%zu 0 rlineto\n", w_h_in_pt[0]+borderwidth);
428 fprintf(fp, "0 -%zu rlineto\n", w_h_in_pt[1]+borderwidth);
429 fprintf(fp, "closepath\n");
430 fprintf(fp, "stroke\n\n");
431 }
432
433
434 /* Write the image: */
435 fprintf(fp, "%% Draw the image:\n");
436 fprintf(fp, "%d %d translate\n", borderwidth, borderwidth);
437 fprintf(fp, "%zu %zu scale\n", w_h_in_pt[0], w_h_in_pt[1]);
438 eps_write_image(in, fp, hex, dontoptimize);
439
440
441 /* Ending of the EPS file: */
442 if(forpdf) fprintf(fp, "showpage\n");
443 else fprintf(fp, "grestore\n");
444 fprintf(fp, "%%%%EOF");
445 fclose(fp);
446 }
447