1 /*
2  *
3  *   Super simple color calibration program for the Common UNIX
4  *   Printing System.
5  *
6  *   Copyright 1993-2000 by Easy Software Products.
7  *
8  *   This program is free software; you can redistribute it and/or modify it
9  *   under the terms of the GNU General Public License as published by the Free
10  *   Software Foundation; either version 2 of the License, or (at your option)
11  *   any later version.
12  *
13  *   This program is distributed in the hope that it will be useful, but
14  *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16  *   for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  *
21  * Contents:
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <math.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 
36 /*
37  * min/max/abs macros...
38  */
39 
40 #ifndef max
41 #  define 	max(a,b)	((a) > (b) ? (a) : (b))
42 #endif /* !max */
43 #ifndef min
44 #  define 	min(a,b)	((a) < (b) ? (a) : (b))
45 #endif /* !min */
46 #ifndef abs
47 #  define	abs(a)		((a) < 0 ? -(a) : (a))
48 #endif /* !abs */
49 
50 
51 /*
52  * Prototypes...
53  */
54 
55 float	get_calibration(const char *prompt, float minval, float maxval);
56 char	*safe_gets(const char *prompt, char *s, int size);
57 int	safe_geti(const char *prompt, int defval);
58 void	send_prolog(FILE *fp);
59 void	send_pass1(FILE *fp);
60 void	send_pass2(FILE *fp, float kd, float rd, float yellow);
61 void	send_pass3(FILE *fp, float kd, float rd, float g, float yellow);
62 void	send_pass4(FILE *fp, float red, float green, float blue, const char *p);
63 
64 
65 /*
66  * 'main()' - Main entry for calibration program.
67  */
68 
69 int
main(int argc,char * argv[])70 main(int  argc,
71      char *argv[])
72 {
73   char	printer[255];
74   char	resolution[255];
75   char	mediatype[255];
76   char	*profile;
77   char	cupsProfile[1024];
78   char	command[1024];
79   char  lpoptionscommand[1024];
80   char  line[255];
81   char	junk[255];
82   FILE	*fp;
83   float	kd, rd, g;
84   float	color;
85   float	red, green, blue;
86   float	cyan, magenta, yellow;
87   float	m[3][3];
88 
89 
90   puts("ESP Printer Calibration Tool v1.0");
91   puts("Copyright 1999-2000 by Easy Software Products, All Rights Reserved.");
92   puts("");
93   puts("This program allows you to calibrate the color output of printers");
94   puts("using the Gutenprint CUPS or ESP Print Pro drivers.");
95   puts("");
96   puts("Please note that this program ONLY works with the Gutenprint CUPS");
97   puts("driver.");
98   puts("");
99   puts("These drivers by the text \"CUPS+Gutenprint\"");
100   puts("the model description displayed by the CUPS web interface or");
101   puts("similar tool.");
102   puts("");
103   puts("If you are not using the correct driver, press CTRL+C now and");
104   puts("reinstall your printer queue with the appropriate driver first.");
105   puts("");
106   puts("To make a calibration profile for all users, run this program as");
107   puts("the \"root\" user.");
108   puts("");
109   puts("");
110 
111   safe_gets("Printer name [default]?", printer, sizeof(printer));
112   safe_gets("Resolution [default]?", resolution, sizeof(resolution));
113   safe_gets("Media type [default]?", mediatype, sizeof(mediatype));
114 
115   strcpy(command, "lp -s");
116   if (printer[0])
117   {
118     strcat(command, " -d ");
119     strcat(command, printer);
120   }
121   if (resolution[0])
122   {
123     strcat(command, " -o resolution=");
124     strcat(command, resolution);
125   }
126   if (mediatype[0])
127   {
128     strcat(command, " -o media=");
129     strcat(command, mediatype);
130   }
131 
132   strcat(command, " -o profile=");
133 
134   profile = command + strlen(command);
135 
136   strcpy(profile, "1000,1000,1000,0,0,0,1000,0,0,0,1000");
137 
138   safe_gets("Press ENTER to print pass #1 or N to skip...", junk, sizeof(junk));
139 
140   if (!junk[0])
141   {
142     puts("Sending calibration pass #1 for density/saturation levels...");
143 
144     fp = popen(command, "w");
145     send_prolog(fp);
146     send_pass1(fp);
147     fputs("showpage\n", fp);
148     pclose(fp);
149 
150     puts("Calibration pass #1 sent.");
151   }
152 
153   puts("");
154   puts("Please select the character that corresponds to the black block that");
155   puts("is 100% saturated (dark) while not bleeding through the paper.  If");
156   puts("the saturation point appears to occur between two characters, enter");
157   puts("both characters.");
158   puts("");
159 
160   kd = get_calibration("Black density?", 0.25f, 1.0f);
161 
162   puts("");
163   puts("Now select the character that corresponds to the yellow block that is");
164   puts("100% saturated (dark) while not bleeding through the paper. If the");
165   puts("saturation point appears to occur between two characters, enter both");
166   puts("characters.");
167   puts("");
168 
169   cyan    = kd;
170   magenta = kd;
171   yellow  = get_calibration("Yellow density?", 0.25f, 1.0f);
172 
173   puts("");
174   puts("Now select the character that corresponds to the red block that is");
175   puts("100% saturated (dark) while not bleeding through the paper. If the");
176   puts("saturation point appears to occur between two characters, enter both");
177   puts("characters.");
178   puts("");
179 
180   rd = get_calibration("Red density?", 0.5f, 2.0f);
181 
182   puts("");
183   puts("Thank you.  Now insert the page back into the printer and press the");
184   puts("ENTER key to print calibration pass #2.");
185   puts("");
186 
187   safe_gets("Press ENTER to print pass #2 or N to skip...", junk, sizeof(junk));
188 
189   if (!junk[0])
190   {
191     puts("Sending calibration pass #2 for gamma levels...");
192 
193     fp = popen(command, "w");
194     send_prolog(fp);
195     send_pass2(fp, kd, rd, yellow);
196     fputs("showpage\n", fp);
197     pclose(fp);
198 
199     puts("Calibration pass #2 sent.");
200   }
201 
202   puts("");
203   puts("Please select the character that corresponds to the column of gray");
204   puts("blocks that appear to be 1/2 and 1/4 as dark as the black blocks,");
205   puts("respectively.  If the transition point appears to occur between two");
206   puts("characters, enter both characters.");
207   puts("");
208 
209   g = get_calibration("Gamma?", 1.0f, 4.0f);
210 
211   puts("");
212   puts("Thank you.  Now insert the page back into the printer and press the");
213   puts("ENTER key to print calibration pass #3.");
214   puts("");
215 
216   safe_gets("Press ENTER to print pass #3 or N to skip...", junk, sizeof(junk));
217 
218   if (!junk[0])
219   {
220     puts("Sending calibration pass #3 for red, green, and blue adjustment...");
221 
222     fp = popen(command, "w");
223     send_prolog(fp);
224     send_pass3(fp, kd, rd, g, yellow);
225     fputs("showpage\n", fp);
226     pclose(fp);
227 
228     puts("Calibration pass #3 sent.");
229   }
230 
231   puts("");
232   puts("Please select the character that corresponds to the correct red,");
233   puts("green, and blue colors.  If the transition point appears to occur");
234   puts("between two characters, enter both characters.");
235   puts("");
236 
237   red   = get_calibration("Red color?", 0.35f, -0.40f);
238   green = get_calibration("Green color?", 0.35f, -0.40f);
239   blue  = get_calibration("Blue color?", 0.35f, -0.40f);
240 
241   color = 0.5f * rd / kd - kd;
242 
243   cyan    /= kd;
244   magenta /= kd;
245   yellow  /= kd;
246 
247   m[0][0] = cyan;			/* C */
248   m[0][1] = cyan * (color + blue);	/* C + M (blue) */
249   m[0][2] = cyan * (color - green);	/* C + Y (green) */
250   m[1][0] = magenta * (color - blue);	/* M + C (blue) */
251   m[1][1] = magenta;			/* M */
252   m[1][2] = magenta * (color + red);	/* M + Y (red) */
253   m[2][0] = yellow * (color + green);	/* Y + C (green) */
254   m[2][1] = yellow * (color - red);	/* Y + M (red) */
255   m[2][2] = yellow;			/* Y */
256 
257   if (m[0][1] > 0.0f)
258   {
259     m[1][0] -= m[0][1];
260     m[0][1] = 0.0f;
261   }
262   else if (m[1][0] > 0.0f)
263   {
264     m[0][1] -= m[1][0];
265     m[1][0] = 0.0f;
266   }
267 
268   if (m[0][2] > 0.0f)
269   {
270     m[2][0] -= m[0][2];
271     m[0][2] = 0.0f;
272   }
273   else if (m[2][0] > 0.0f)
274   {
275     m[0][2] -= m[2][0];
276     m[2][0] = 0.0f;
277   }
278 
279   if (m[1][2] > 0.0f)
280   {
281     m[2][1] -= m[1][2];
282     m[1][2] = 0.0f;
283   }
284   else if (m[2][1] > 0.0f)
285   {
286     m[1][2] -= m[2][1];
287     m[2][1] = 0.0f;
288   }
289 
290   sprintf(profile, "%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f",
291           kd * 1000.0f, g * 1000.0f,
292 	  m[0][0] * 1000.0f, m[0][1] * 1000.0f, m[0][2] * 1000.0f,
293 	  m[1][0] * 1000.0f, m[1][1] * 1000.0f, m[1][2] * 1000.0f,
294 	  m[2][0] * 1000.0f, m[2][1] * 1000.0f, m[2][2] * 1000.0f);
295 
296   sprintf(cupsProfile, "    *cupsColorProfile %s/%s: \"%.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\"\n",
297           resolution[0] ? resolution : "-", mediatype[0] ? mediatype : "-",
298 	  kd, g, m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2],
299 	  m[2][0], m[2][1], m[2][2]);
300 
301   puts("");
302   puts("Thank you.  Now insert the page back into the printer and press the");
303   puts("ENTER key to print the final calibration pass.");
304   puts("");
305 
306   safe_gets("Press ENTER to continue...", junk, sizeof(junk));
307 
308   puts("Sending calibration pass #4 for visual confirmation...");
309 
310   fp = popen(command, "w");
311   send_prolog(fp);
312   send_pass4(fp, red, green, blue, cupsProfile);
313   fputs("showpage\n", fp);
314   pclose(fp);
315 
316   puts("Calibration pass #4 sent.");
317 
318   puts("");
319   puts("The basic color profile for these values is:");
320   puts("");
321   printf("    %s\n", cupsProfile);
322   puts("");
323   puts("You can add this to the PPD file for this printer to make this change");
324   puts("permanent, or use the following option with a printing command:");
325   puts("");
326   printf("    -o profile=%s\n", profile);
327   puts("");
328   puts("to use the profile for this job only.");
329   puts("");
330   puts("Calibration is complete.");
331   puts("");
332 
333   if (getuid() == 0)
334   {
335     do
336       safe_gets("Would you like to save the profile as a system-wide default (y/n)? ",
337                 line, sizeof(line));
338     while (tolower(line[0]) != 'n' && tolower(line[0]) != 'y');
339   }
340   else
341     line[0] = 'n';
342 
343   if (line[0] == 'n')
344   {
345     do
346       safe_gets("Would you like to save the profile as a personal default (y/n)? ",
347                 line, sizeof(line));
348     while (tolower(line[0]) != 'n' && tolower(line[0]) != 'y');
349   }
350 
351   puts("");
352   if (tolower(line[0]) == 'n')
353   {
354     puts("Calibration profile NOT saved.");
355     return (0);
356   }
357 
358   strcpy(lpoptionscommand, "lpoptions");
359   if (printer[0])
360   {
361     strcat(lpoptionscommand, " -p ");
362     strcat(lpoptionscommand, printer);
363   }
364 
365   strcat(lpoptionscommand, " -o profile=");
366 
367   strcat(lpoptionscommand, profile);
368 
369   if (system(lpoptionscommand) == 0)
370     puts("Calibration profile successfully saved.");
371   else
372     puts("An error occurred while saving the calibration profile.");
373 
374   return (0);
375 }
376 
377 
378 float
get_calibration(const char * prompt,float minval,float maxval)379 get_calibration(const char  *prompt,
380                 float       minval,
381 		float       maxval)
382 {
383   char	line[4];		/* Input from user */
384   int	val1, val2;		/* Calibration values */
385 
386 
387   do
388   {
389     if (safe_gets(prompt, line, sizeof(line)) == NULL)
390       return (minval);
391   }
392   while (!isxdigit(line[0]) || (line[1] && !isxdigit(line[1])));
393 
394   if (isdigit(line[0]))
395     val1 = line[0] - '0';
396   else
397     val1 = tolower(line[0]) - 'a' + 10;
398 
399   if (!line[1])
400     val2 = val1;
401   else if (isdigit(line[1]))
402     val2 = line[1] - '0';
403   else
404     val2 = tolower(line[1]) - 'a' + 10;
405 
406   return (minval + (maxval - minval) * (val1 + val2) / 30.0f);
407 }
408 
409 
410 int
safe_geti(const char * prompt,int defval)411 safe_geti(const char *prompt,
412           int        defval)
413 {
414   char	line[255];
415 
416 
417   do
418   {
419     safe_gets(prompt, line, sizeof(line));
420 
421     if (defval && !line[0])
422       return (defval);
423   }
424   while (!line[0] || !isdigit(line[0]));
425 
426   return (atoi(line));
427 }
428 
429 
430 /*
431  * 'safe_gets()' - Get a string from the user.
432  */
433 
434 char *
safe_gets(const char * prompt,char * s,int size)435 safe_gets(const char *prompt,
436           char       *s,
437           int        size)
438 {
439   printf("%s ", prompt);
440 
441   if (fgets(s, size, stdin) == NULL)
442     return (NULL);
443 
444   if (s[0])
445     s[strlen(s) - 1] = '\0';
446 
447   return (s);
448 }
449 
450 
451 void
send_prolog(FILE * fp)452 send_prolog(FILE *fp)
453 {
454   fputs("%!\n", fp);
455   fputs("/Helvetica findfont 12 scalefont setfont\n", fp);
456   fputs("/BLOCK {\n"
457 	"	14.4 mul neg 720 add exch\n"
458 	"	14.4 mul 72 add exch\n"
459 	"	14.4 14.4 rectfill\n"
460 	"} bind def\n", fp);
461   fputs("/DIAMOND {\n"
462 	"	14.4 mul neg 720 add 7.2 add exch\n"
463 	"	14.4 mul 72 add exch\n"
464 	"	newpath\n"
465 	"	moveto\n"
466 	"	7.2 7.2 rlineto\n"
467 	"	7.2 -7.2 rlineto\n"
468 	"	-7.2 -7.2 rlineto\n"
469 	"	closepath\n"
470 	"	0 ne { fill } { stroke } ifelse\n"
471 	"} bind def\n", fp);
472   fputs("/PLUS {\n"
473 	"	14.4 mul neg 720 add 7.2 add exch\n"
474 	"	14.4 mul 72 add exch\n"
475 	"	newpath\n"
476 	"	moveto\n"
477 	"	14.4 0 rlineto\n"
478 	"	-7.2 -7.2 rmoveto\n"
479 	"	0 14.4 rlineto\n"
480 	"	closepath\n"
481 	"	fill\n"
482 	"} bind def\n", fp);
483   fputs("/TEXT {\n"
484         "	14.4 mul neg 720 add 4 add exch\n"
485 	"	14.4 mul 72 add 7.2 add exch\n"
486 	"	moveto\n"
487 	"	dup stringwidth pop 0.5 mul neg 0 rmoveto\n"
488 	"	show\n"
489 	"} bind def\n", fp);
490 }
491 
492 
493 void
send_pass1(FILE * fp)494 send_pass1(FILE *fp)
495 {
496   int			i;
497   float			p;
498   static const char	*hex = "FEDCBA9876543210";
499 
500 
501   fputs("0 setgray", fp);
502   fputs("(K) 0 1 TEXT\n", fp);
503   fputs("(Y) 0 2 TEXT\n", fp);
504   fputs("(R) 0 4 TEXT\n", fp);
505 
506   for (i = 0; i < 16; i ++)
507   {
508     fprintf(fp, "(%d) %d 0 TEXT\n", 100 - i * 5, i * 2 + 2);
509     fprintf(fp, "(%c) %d 3 TEXT\n", hex[i], i * 2 + 2);
510     fprintf(fp, "(%d) %d 5 TEXT\n", 200 - i * 10, i * 2 + 2);
511   }
512 
513   for (i = 0; i < 16; i ++)
514   {
515     p = 0.01f * (100 - i * 5);
516 
517     fprintf(fp, "%.2f setgray %d 1 BLOCK\n", 1.0 - p, i * 2 + 2);
518     fprintf(fp, "0 0 %.2f 0 setcmykcolor %d 2 BLOCK\n", p, i * 2 + 2);
519     fprintf(fp, "0 %.2f %.2f 0 setcmykcolor %d 4 BLOCK\n", p, p, i * 2 + 2);
520   }
521 }
522 
523 
524 void
send_pass2(FILE * fp,float kd,float rd,float yellow)525 send_pass2(FILE  *fp,
526 	   float kd,
527 	   float rd,
528 	   float yellow)
529 {
530   int			i;
531   float			p;
532   float			g;
533   static const char	*hex = "FEDCBA9876543210";
534 
535 
536   rd *= 0.5f;
537 
538   fprintf(fp, "0 0 %.2f 0 setcmykcolor\n", yellow);
539   fprintf(fp, "1 %.2f 6 DIAMOND\n", 2.0f + 30.0f * (1.0f - yellow) / 0.75f);
540 
541   fprintf(fp, "0 %.2f %.2f 0 setcmykcolor\n", rd, rd);
542   fprintf(fp, "%d %.2f 6 DIAMOND\n", rd != yellow,
543           2.0f + 30.0f * (1.0f - rd) / 0.75f);
544 
545   p = 1.0f - kd;
546   fprintf(fp, "%.2f setgray\n", p);
547 
548   if (kd == rd && kd == yellow)
549     fprintf(fp, "%.2f 6 PLUS\n", 2.0f + 30.0f * (1.0f - kd) / 0.75f);
550   else
551     fprintf(fp, "%d %.2f 6 DIAMOND\n", kd != yellow && kd != rd,
552             2.0f + 30.0f * (1.0f - kd) / 0.75f);
553 
554   fputs("(100%) 0 9 TEXT\n", fp);
555   fputs("(50%) 0 10 TEXT\n", fp);
556   fputs("(25%) 0 11 TEXT\n", fp);
557 
558   for (i = 0; i < 16; i ++)
559   {
560     fprintf(fp, "(%.1f) %d 8 TEXT\n", 0.2f * (20 - i), i * 2 + 2);
561     fprintf(fp, "(%c) %d 12 TEXT\n", hex[i], i * 2 + 2);
562   }
563 
564   for (i = 0; i < 16; i ++)
565   {
566     g = 0.2f * (20 - i);
567 
568     p = 1.0f - kd * (float)pow(1.0f, g);
569     fprintf(fp, "%.2f setgray %d 9 BLOCK\n", p, i * 2 + 2);
570 
571     p = 1.0f - kd * (float)pow(0.5f, g);
572     fprintf(fp, "%.2f setgray %d 10 BLOCK\n", p, i * 2 + 2);
573 
574     p = 1.0f - kd * (float)pow(0.25f, g);
575     fprintf(fp, "%.2f setgray %d 11 BLOCK\n", p, i * 2 + 2);
576   }
577 }
578 
579 
580 void
send_pass3(FILE * fp,float kd,float rd,float g,float yellow)581 send_pass3(FILE  *fp,
582 	   float kd,
583 	   float rd,
584 	   float g,
585 	   float yellow)
586 {
587   int	i;
588   float p;
589   float	color;
590   float	c, m, y;
591   float	adj;
592   static const char	*hex = "FEDCBA9876543210";
593 
594 
595   p = 1.0f - kd;
596   fprintf(fp, "%.2f setgray\n", p);
597   fprintf(fp, "1 %.2f 13 DIAMOND\n", 2.0f + 30.0f * (4.0f - g) / 3.0f);
598 
599   color  = 0.5f * rd / kd - kd;
600   yellow /= kd;
601 
602   fputs("(R) 2 16 TEXT\n", fp);
603   fputs("(G) 2 17 TEXT\n", fp);
604   fputs("(B) 2 18 TEXT\n", fp);
605 
606   for (i = 1; i < 16; i ++)
607   {
608     fprintf(fp, "(%+d) %d 15 TEXT\n", i * 5 - 40, i * 2 + 2);
609     fprintf(fp, "(%c) %d 19 TEXT\n", hex[i], i * 2 + 2);
610   }
611 
612   for (i = 1; i < 16; i ++)
613   {
614    /* Adjustment value */
615     adj = i * 0.05f - 0.40f;
616 
617    /* RED */
618     c = 0.0f;
619     m = color + adj;
620     y = color - adj;
621     if (m > 0)
622     {
623       y -= m;
624       m = 0;
625     }
626     else if (y > 0)
627     {
628       m -= y;
629       y = 0;
630     }
631 
632     m += 1.0f;
633     y = yellow * (1.0f + y);
634 
635     if (c <= 0.0f)
636       c = 0.0f;
637     else if (c >= 1.0f)
638       c = 1.0f;
639     else
640       c = (float)pow(c, g);
641 
642     if (m <= 0.0f)
643       m = 0.0f;
644     else if (m >= 1.0f)
645       m = 1.0f;
646     else
647       m = (float)pow(m, g);
648 
649     if (y <= 0.0f)
650       y = 0.0f;
651     else if (y >= 1.0f)
652       y = 1.0f;
653     else
654       y = (float)pow(y, g);
655 
656     fprintf(fp, "%.2f %.2f %.2f 0 setcmykcolor %d 16 BLOCK\n",
657             c * kd, m * kd, y * kd, i * 2 + 2);
658 
659    /* GREEN */
660     c = color - adj;
661     m = 0.0f;
662     y = color + adj;
663 
664     if (c > 0)
665     {
666       y -= c;
667       c = 0;
668     }
669     else if (y > 0)
670     {
671       c -= y;
672       y = 0;
673     }
674 
675     c += 1.0f;
676     y = yellow * (1.0f + y);
677 
678     if (c <= 0.0f)
679       c = 0.0f;
680     else if (c >= 1.0f)
681       c = 1.0f;
682     else
683       c = (float)pow(c, g);
684 
685     if (m <= 0.0f)
686       m = 0.0f;
687     else if (m >= 1.0f)
688       m = 1.0f;
689     else
690       m = (float)pow(m, g);
691 
692     if (y <= 0.0f)
693       y = 0.0f;
694     else if (y >= 1.0f)
695       y = 1.0f;
696     else
697       y = (float)pow(y, g);
698 
699     fprintf(fp, "%.2f %.2f %.2f 0 setcmykcolor %d 17 BLOCK\n",
700             c * kd, m * kd, y * kd, i * 2 + 2);
701 
702    /* BLUE */
703     c = color + adj;
704     m = color - adj;
705     y = 0.0f;
706 
707     if (c > 0)
708     {
709       m -= c;
710       c = 0;
711     }
712     else if (m > 0)
713     {
714       c -= m;
715       m = 0;
716     }
717 
718     c += 1.0f;
719     m += 1.0f;
720 
721     if (c <= 0.0f)
722       c = 0.0f;
723     else if (c >= 1.0f)
724       c = 1.0f;
725     else
726       c = (float)pow(c, g);
727 
728     if (m <= 0.0f)
729       m = 0.0f;
730     else if (m >= 1.0f)
731       m = 1.0f;
732     else
733       m = (float)pow(m, g);
734 
735     if (y <= 0.0f)
736       y = 0.0f;
737     else if (y >= 1.0f)
738       y = 1.0f;
739     else
740       y = (float)pow(y, g);
741 
742     fprintf(fp, "%.2f %.2f %.2f 0 setcmykcolor %d 18 BLOCK\n",
743             c * kd, m * kd, y * kd, i * 2 + 2);
744   }
745 }
746 
747 
748 void
send_pass4(FILE * fp,float red,float green,float blue,const char * profile)749 send_pass4(FILE       *fp,
750 	   float      red,
751 	   float      green,
752 	   float      blue,
753 	   const char *profile)
754 {
755   FILE	*ppm;
756   int	x, y, col, width, height;
757   int	r, g, b;
758   char	line[255];
759 
760 
761   fprintf(fp, "0 0 1 setrgbcolor 1 %.2f 20 DIAMOND\n", /* BLUE */
762           2.0f + 30.0f * (0.40f + blue) / 0.75f);
763   fprintf(fp, "1 0 0 setrgbcolor %d %.2f 20 DIAMOND\n", /* RED */
764           red != blue, 2.0f + 30.0f * (0.40f + red) / 0.75f);
765   if (green == red && green == blue)
766     fprintf(fp, "0 1 0 setrgbcolor %.2f 20 PLUS\n", /* GREEN */
767             2.0f + 30.0f * (0.40f + green) / 0.75f);
768   else
769     fprintf(fp, "0 1 0 setrgbcolor %d %.2f 20 DIAMOND\n", /* GREEN */
770             green != red && green != blue,
771 	    2.0f + 30.0f * (0.40f + green) / 0.75f);
772 
773   fputs("0 setgray\n", fp);
774   fputs("currentfont 0.8 scalefont setfont\n", fp);
775 
776   fprintf(fp, "(%s) 16 22 TEXT\n", profile);
777 
778   if ((ppm = fopen(CUPS_DATADIR "/calibrate.ppm", "rb")) == NULL)
779     if ((ppm = fopen("calibrate.ppm", "rb")) == NULL)
780       return;
781 
782   fgets(line, sizeof(line), ppm);
783   while (fgets(line, sizeof(line), ppm))
784     if (line[0] != '#')
785       break;
786 
787   sscanf(line, "%d%d", &width, &height);
788 
789   fgets(line, sizeof(line), ppm);
790 
791   fputs("gsave\n", fp);
792   fprintf(fp, "72 %d translate\n", 504 * height / width + 72);
793   fprintf(fp, "504 -%d scale\n", 504 * height / width);
794 
795   fprintf(fp, "/scanline %d string def\n", width * 3);
796 
797   fprintf(fp, "%d %d 8\n", width, height);
798   fprintf(fp, "[%d 0 0 %d 0 0]\n", width, height);
799   fputs("{ currentfile scanline readhexstring pop } false 3 colorimage\n", fp);
800 
801   for (y = 0, col = 0; y < height; y ++)
802   {
803     printf("Sending scanline %d of %d...\r", y + 1, height);
804     fflush(stdout);
805 
806     for (x = 0; x < width; x ++)
807     {
808       r = getc(ppm);
809       g = getc(ppm);
810       b = getc(ppm);
811 
812       fprintf(fp, "%02X%02X%02X", r, g, b);
813       col ++;
814       if (col >= 12)
815       {
816         col = 0;
817 	putc('\n', fp);
818       }
819     }
820   }
821 
822   if (col)
823     putc('\n', fp);
824 
825   printf("                                    \r");
826 
827   fclose(ppm);
828 
829   fputs("grestore\n", fp);
830 }
831