• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 Canonical Ltd.
3  * Copyright 2013 ALT Linux, Andrew V. Stepanov <stanv@altlinux.com>
4  * Copyright 2018 Sahil Arora <sahilarora.535@gmail.com>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 3, as published
8  * by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranties of
12  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13  * PURPOSE.  See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <config.h>
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <math.h>
23 
24 #ifndef HAVE_OPEN_MEMSTREAM
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 #endif
30 
31 #include <cups/cups.h>
32 #include <cups/ppd.h>
33 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6)
34 #define HAVE_CUPS_1_7 1
35 #endif
36 #ifdef HAVE_CUPS_1_7
37 #include <cups/pwg.h>
38 #endif /* HAVE_CUPS_1_7 */
39 
40 #include "banner.h"
41 #include "pdf.h"
42 
43 
get_float_option(const char * name,int noptions,cups_option_t * options,float def)44 static float get_float_option(const char *name,
45                               int noptions,
46                               cups_option_t *options,
47                               float def)
48 {
49     const char *value = cupsGetOption(name, noptions, options);
50     return value ? atof(value) : def;
51 }
52 
53 
get_int_option(const char * name,int noptions,cups_option_t * options,int def)54 static int get_int_option(const char *name,
55                           int noptions,
56                           cups_option_t *options,
57                           int def)
58 {
59     const char *value = cupsGetOption(name, noptions, options);
60     return value ? atoi(value) : def;
61 }
62 
63 
get_pagesize(ppd_file_t * ppd,int noptions,cups_option_t * options,float * width,float * length,float media_limits[4])64 static void get_pagesize(ppd_file_t *ppd,
65                          int noptions,
66                          cups_option_t *options,
67                          float *width,
68                          float *length,
69                          float media_limits[4])
70 {
71     static const ppd_size_t defaultsize = {
72         0,          /* marked */
73         "",         /* name */
74         612.0,      /* width */
75         792.0,      /* length */
76         18.0,       /* left */
77         36.0,       /* bottom */
78         594.0,      /* right */
79         756.0,      /* top */
80     };
81     const ppd_size_t *pagesize;
82 #ifdef HAVE_CUPS_1_7
83     pwg_media_t      *size_found;          /* page size found for given name */
84     const char       *val;                 /* Pointer into value */
85     char             *ptr1, *ptr2,         /* Pointer into string */
86                      s[255];               /* Temporary string */
87 #endif /* HAVE_CUPS_1_7 */
88 
89     if (!ppd || !(pagesize = ppdPageSize(ppd, NULL)))
90         pagesize = &defaultsize;
91 
92     *width = pagesize->width;
93     *length = pagesize->length;
94 
95     media_limits[0] = get_float_option("page-left",
96                                        noptions, options,
97                                        pagesize->left);
98     media_limits[1] = get_float_option("page-bottom",
99                                        noptions, options,
100                                        pagesize->bottom);
101     media_limits[2] = get_float_option("page-right",
102                                        noptions, options,
103                                        fabs(pagesize->right));
104     media_limits[3] = get_float_option("page-top",
105                                        noptions, options,
106                                        fabs(pagesize->top));
107 
108 #ifdef HAVE_CUPS_1_7
109     if (!ppd) {
110       if ((val = cupsGetOption("media-size", noptions, options)) != NULL ||
111 	  (val = cupsGetOption("MediaSize", noptions, options)) != NULL ||
112 	  (val = cupsGetOption("page-size", noptions, options)) != NULL ||
113 	  (val = cupsGetOption("PageSize", noptions, options)) != NULL ||
114 	  (val = cupsGetOption("media", noptions, options)) != NULL) {
115 	for (ptr1 = (char *)val; *ptr1;) {
116 	  for (ptr2 = s; *ptr1 && *ptr1 != ',' && (ptr2 - s) < (sizeof(s) - 1);)
117 	    *ptr2++ = *ptr1++;
118 	  *ptr2++ = '\0';
119 	  if (*ptr1 == ',')
120 	    ptr1 ++;
121 	  size_found = NULL;
122 	  if ((size_found = pwgMediaForPWG(s)) == NULL)
123 	    if ((size_found = pwgMediaForPPD(s)) == NULL)
124 	      size_found = pwgMediaForLegacy(s);
125 	  if (size_found != NULL) {
126 	    *width = size_found->width * 72.0 / 2540.0;
127 	    *length = size_found->length * 72.0 / 2540.0;
128 	    media_limits[2] += (*width - 612.0);
129 	    media_limits[3] += (*length - 792.0);
130 	  }
131 	}
132       }
133       if ((val = cupsGetOption("media-left-margin", noptions, options))
134 	  != NULL)
135 	media_limits[0] = atol(val) * 72.0 / 2540.0;
136       if ((val = cupsGetOption("media-bottom-margin", noptions, options))
137 	  != NULL)
138 	media_limits[1] = atol(val) * 72.0 / 2540.0;
139       if ((val = cupsGetOption("media-right-margin", noptions, options))
140 	  != NULL)
141 	media_limits[2] = *width - atol(val) * 72.0 / 2540.0;
142       if ((val = cupsGetOption("media-top-margin", noptions, options))
143 	  != NULL)
144 	media_limits[3] = *length - atol(val) * 72.0 / 2540.0;
145     }
146 #endif /* HAVE_CUPS_1_7 */
147 }
148 
149 
duplex_marked(ppd_file_t * ppd,int noptions,cups_option_t * options)150 static int duplex_marked(ppd_file_t *ppd,
151                          int noptions,
152                          cups_option_t *options)
153 {
154     const char       *val;                 /* Pointer into value */
155     return
156       (ppd &&
157        (ppdIsMarked(ppd, "Duplex", "DuplexNoTumble") ||
158         ppdIsMarked(ppd, "Duplex", "DuplexTumble") ||
159         ppdIsMarked(ppd, "JCLDuplex", "DuplexNoTumble") ||
160         ppdIsMarked(ppd, "JCLDuplex", "DuplexTumble") ||
161         ppdIsMarked(ppd, "EFDuplex", "DuplexNoTumble") ||
162         ppdIsMarked(ppd, "EFDuplex", "DuplexTumble") ||
163         ppdIsMarked(ppd, "KD03Duplex", "DuplexNoTumble") ||
164         ppdIsMarked(ppd, "KD03Duplex", "DuplexTumble"))) ||
165       ((val = cupsGetOption("Duplex", noptions, options))
166        != NULL &&
167        (!strcasecmp(val, "DuplexNoTumble") ||
168 	!strcasecmp(val, "DuplexTumble"))) ||
169       ((val = cupsGetOption("sides", noptions, options))
170        != NULL &&
171        (!strcasecmp(val, "two-sided-long-edge") ||
172 	!strcasecmp(val, "two-sided-short-edge")));
173 }
174 
175 
info_linef(FILE * s,const char * key,const char * valuefmt,...)176 static void info_linef(FILE *s,
177                        const char *key,
178                        const char *valuefmt, ...)
179 {
180     va_list ap;
181 
182     va_start(ap, valuefmt);
183     fprintf(s, "(%s: ", key);
184     vfprintf(s, valuefmt, ap);
185     fprintf(s, ") Tj T*\n");
186     va_end(ap);
187 }
188 
189 
info_line(FILE * s,const char * key,const char * value)190 static void info_line(FILE *s,
191                       const char *key,
192                       const char *value)
193 {
194     info_linef(s, key, "%s", value);
195 }
196 
197 
info_line_time(FILE * s,const char * key,const char * timestamp)198 static void info_line_time(FILE *s,
199                       const char *key,
200                       const char *timestamp)
201 {
202     char buf[40];
203     time_t time;
204 
205     if (timestamp) {
206         time = (time_t)atoll(timestamp);
207         strftime(buf, sizeof buf, "%c", localtime(&time));
208         info_line(s, key, buf);
209     }
210     else
211         info_line(s, key, "unknown");
212 }
213 
human_time(const char * timestamp)214 static const char *human_time(const char *timestamp)
215 {
216     time_t time;
217     int size = sizeof(char) * 40;
218     char *buf = malloc(size);
219     strcpy(buf, "unknown");
220 
221     if (timestamp) {
222         time = (time_t)atoll(timestamp);
223         strftime(buf, size, "%c", localtime(&time));
224     }
225 
226     return buf;
227 }
228 
229 /*
230  * Add new key & value.
231  */
add_opt(opt_t * in_opt,const char * key,const char * val)232 static opt_t* add_opt(opt_t *in_opt, const char *key, const char *val) {
233     if ( ! key || ! val ) {
234         return in_opt;
235     }
236 
237     if ( !strlen(key) || !strlen(val) ) {
238         return in_opt;
239     }
240 
241     opt_t *entry = malloc(sizeof(opt_t));
242     if ( ! entry ) {
243         return in_opt;
244     }
245 
246     entry->key = key;
247     entry->val = val;
248     entry->next = in_opt;
249 
250     return entry;
251 }
252 
253 /*
254  * Collect all known info about current task.
255  * Bond PDF form field name with collected info.
256  *
257  * Create PDF form's field names according above.
258  */
get_known_opts(ppd_file_t * ppd,const char * jobid,const char * user,const char * jobtitle,int noptions,cups_option_t * options)259 opt_t *get_known_opts(
260         ppd_file_t *ppd,
261         const char *jobid,
262         const char *user,
263         const char *jobtitle,
264         int noptions,
265         cups_option_t *options) {
266 
267     ppd_attr_t *attr;
268     opt_t *opt = NULL;
269 
270     /* Job ID */
271     opt = add_opt(opt, "job-id", jobid);
272 
273     /* Job title */
274     opt = add_opt(opt, "job-title", jobtitle);
275 
276     /* Printer by */
277     opt = add_opt(opt, "user", user);
278 
279     /* Printer name */
280     opt = add_opt(opt, "printer-name", getenv("PRINTER"));
281 
282     /* Printer info */
283     opt = add_opt(opt, "printer-info", getenv("PRINTER_INFO"));
284 
285     /* Time at creation */
286     opt = add_opt(opt, "time-at-creation",
287             human_time(cupsGetOption("time-at-creation", noptions, options)));
288 
289     /* Processing time */
290     opt = add_opt(opt, "time-at-processing",
291             human_time(cupsGetOption("time-at-processing", noptions, options)));
292 
293     /* Billing information */
294     opt = add_opt(opt, "job-billing",
295             cupsGetOption("job-billing", noptions, options));
296 
297     /* Source hostname */
298     opt = add_opt(opt, "job-originating-host-name",
299             cupsGetOption("job-originating-host-name", noptions, options));
300 
301     /* Banner font */
302     opt = add_opt(opt, "banner-font",
303             cupsGetOption("banner-font", noptions, options));
304 
305     /* Banner font size */
306     opt = add_opt(opt, "banner-font-size",
307             cupsGetOption("banner-font-size", noptions, options));
308 
309     /* Job UUID */
310     opt = add_opt(opt, "job-uuid",
311             cupsGetOption("job-uuid", noptions, options));
312 
313     /* Security context */
314     opt = add_opt(opt, "security-context",
315             cupsGetOption("security-context", noptions, options));
316 
317     /* Security context range part */
318     opt = add_opt(opt, "security-context-range",
319             cupsGetOption("security-context-range", noptions, options));
320 
321     /* Security context current range part */
322     const char * full_range = cupsGetOption("security-context-range", noptions, options);
323     if ( full_range ) {
324         size_t cur_size = strcspn(full_range, "-");
325         char * cur_range = strndup(full_range, cur_size);
326         opt = add_opt(opt, "security-context-range-cur", cur_range);
327     }
328 
329     /* Security context type part */
330     opt = add_opt(opt, "security-context-type",
331             cupsGetOption("security-context-type", noptions, options));
332 
333     /* Security context role part */
334     opt = add_opt(opt, "security-context-role",
335             cupsGetOption("security-context-role", noptions, options));
336 
337     /* Security context user part */
338     opt = add_opt(opt, "security-context-user",
339             cupsGetOption("security-context-user", noptions, options));
340 
341     if (ppd) {
342       /* Driver */
343       opt = add_opt(opt, "driver", ppd->pcfilename);
344 
345       /* Driver version */
346       opt = add_opt(opt, "driver-version",
347 		    (attr = ppdFindAttr(ppd, "FileVersion", NULL)) ?
348 		    attr->value : "");
349 
350       /* Make and model */
351       opt = add_opt(opt, "make-and-model", ppd->nickname);
352     }
353 
354     return opt;
355 }
356 
generate_banner_pdf(banner_t * banner,ppd_file_t * ppd,const char * jobid,const char * user,const char * jobtitle,int noptions,cups_option_t * options)357 static int generate_banner_pdf(banner_t *banner,
358                                ppd_file_t *ppd,
359                                const char *jobid,
360                                const char *user,
361                                const char *jobtitle,
362                                int noptions,
363                                cups_option_t *options)
364 {
365     char *buf;
366     size_t len;
367     FILE *s;
368     pdf_t *doc;
369     float page_width, page_length;
370     float media_limits[4];
371     float page_scale;
372     ppd_attr_t *attr;
373     unsigned copies;
374 #ifndef HAVE_OPEN_MEMSTREAM
375     struct stat st;
376 #endif
377 
378     if (!(doc = pdf_load_template(banner->template_file)))
379         return 1;
380 
381     get_pagesize(ppd, noptions, options,
382                  &page_width, &page_length, media_limits);
383 
384     pdf_resize_page (doc, 1, page_width, page_length, &page_scale);
385 
386     pdf_add_type1_font(doc, 1, "Courier");
387 
388 #ifdef HAVE_OPEN_MEMSTREAM
389     s = open_memstream(&buf, &len);
390 #else
391     if ((s = tmpfile()) == NULL) {
392         fprintf (stderr, "ERROR: bannertopdf: cannot create temp file: %s\n",
393                  strerror (errno));
394         return 1;
395     }
396 #endif
397 
398     if (banner->infos & INFO_IMAGEABLE_AREA) {
399         fprintf(s, "q\n");
400         fprintf(s, "0 0 0 RG\n");
401         fprintf(s, "%f %f %f %f re S\n", media_limits[0] + 1.0,
402                                          media_limits[1] + 1.0,
403                                          media_limits[2] - media_limits[0] - 2.0,
404                                          media_limits[3] - media_limits[1] - 2.0);
405         fprintf(s, "Q\n");
406     }
407 
408     fprintf(s, "%f 0 0 %f 0 0 cm\n", page_scale, page_scale);
409 
410     fprintf(s, "0 0 0 rg\n");
411     fprintf(s, "BT\n");
412     fprintf(s, "/bannertopdf-font 14 Tf\n");
413     fprintf(s, "83.662 335.0 Td\n");
414     fprintf(s, "17 TL\n");
415 
416     if (banner->infos & INFO_IMAGEABLE_AREA)
417         info_linef(s, "Media Limits", "%.2f x %.2f to %.2f x %.2f inches",
418                    media_limits[0] / 72.0,
419                    media_limits[1] / 72.0,
420                    media_limits[2] / 72.0,
421                    media_limits[3] / 72.0);
422 
423     if (banner->infos & INFO_JOB_BILLING)
424         info_line(s, "Billing Information\n",
425                   cupsGetOption("job-billing", noptions, options));
426 
427     if (banner->infos & INFO_JOB_ID)
428         info_linef(s, "Job ID", "%s-%s", getenv("PRINTER"), jobid);
429 
430     if (banner->infos & INFO_JOB_NAME)
431         info_line(s, "Job Title", jobtitle);
432 
433     if (banner->infos & INFO_JOB_ORIGINATING_HOST_NAME)
434         info_line(s, "Printed from",
435                   cupsGetOption("job-originating-host-name",
436                                 noptions, options));
437 
438     if (banner->infos & INFO_JOB_ORIGINATING_USER_NAME)
439         info_line(s, "Printed by", user);
440 
441     if (banner->infos & INFO_JOB_UUID)
442         info_line(s, "Job UUID",
443                   cupsGetOption("job-uuid", noptions, options));
444 
445     if (ppd && banner->infos & INFO_PRINTER_DRIVER_NAME)
446         info_line(s, "Driver", ppd->pcfilename);
447 
448     if (ppd && banner->infos & INFO_PRINTER_DRIVER_VERSION)
449         info_line(s, "Driver Version",
450                   (attr = ppdFindAttr(ppd, "FileVersion", NULL)) ? attr->value : "");
451 
452     if (banner->infos & INFO_PRINTER_INFO)
453         info_line(s, "Description", getenv("PRINTER_INFO"));
454 
455     if (banner->infos & INFO_PRINTER_LOCATION)
456         info_line(s, "Printer Location", getenv("PRINTER_LOCATION"));
457 
458     if (ppd && banner->infos & INFO_PRINTER_MAKE_AND_MODEL)
459         info_line(s, "Make and Model", ppd->nickname);
460 
461     if (banner->infos & INFO_PRINTER_NAME)
462         info_line(s, "Printer", getenv("PRINTER"));
463 
464     if (banner->infos & INFO_TIME_AT_CREATION)
465         info_line_time(s, "Created at",
466                        cupsGetOption("time-at-creation", noptions, options));
467 
468     if (banner->infos & INFO_TIME_AT_PROCESSING)
469         info_line_time(s, "Printed at",
470                        cupsGetOption("time-at-processing", noptions, options));
471 
472     fprintf(s, "ET\n");
473 #ifndef HAVE_OPEN_MEMSTREAM
474     fflush (s);
475     if (fstat (fileno (s), &st) < 0) {
476         fprintf (stderr, "ERROR: bannertopdf: cannot fstat(): %s\n", strerror(errno));
477         return 1 ;
478     }
479     fseek (s, 0L, SEEK_SET);
480     if ((buf = malloc(st.st_size + 1)) == NULL) {
481         fprintf (stderr, "ERROR: bannertopdf: cannot malloc(): %s\n", strerror(errno));
482         return 1 ;
483     }
484     size_t nbytes = fread (buf, 1, st.st_size, s);
485     buf[st.st_size] = '\0';
486     len = strlen (buf);
487 #endif /* !HAVE_OPEN_MEMSTREAM */
488     fclose(s);
489 
490     opt_t * known_opts = get_known_opts(ppd,
491             jobid,
492             user,
493             jobtitle,
494             noptions,
495             options);
496 
497     /*
498      * Try to find a PDF form in PDF template and fill it.
499      */
500     int ret = pdf_fill_form(doc, known_opts);
501 
502     /*
503      * Could we fill a PDF form? If no, just add PDF stream.
504      */
505     if ( ! ret ) {
506         pdf_prepend_stream(doc, 1, buf, len);
507     }
508 
509     copies = get_int_option("number-up", noptions, options, 1);
510     if (duplex_marked(ppd, noptions, options))
511         copies *= 2;
512 
513     if (copies > 1)
514         pdf_duplicate_page(doc, 1, copies - 1);
515 
516     pdf_write(doc, stdout);
517 
518     opt_t * opt_current = known_opts;
519     opt_t * opt_next = NULL;
520     while (opt_current != NULL)
521     {
522       opt_next = opt_current->next;
523       free(opt_current);
524       opt_current = opt_next;
525     }
526     free(buf);
527     pdf_free(doc);
528     return 0;
529 }
530 
531 
main(int argc,char * argv[])532 int main(int argc, char *argv[])
533 {
534     banner_t *banner;
535     int noptions;
536     cups_option_t *options;
537     ppd_file_t *ppd;
538     int ret;
539 
540     if (argc < 6) {
541         fprintf(stderr,
542                 "Usage: %s job-id user job-title nr-copies options [file]\n",
543                 argv[0]);
544         return 1;
545     }
546 
547     ppd = ppdOpenFile(getenv("PPD"));
548     if (!ppd)
549       fprintf(stderr, "DEBUG: Could not open PPD file '%s'\n", getenv("PPD"));
550 
551     noptions = cupsParseOptions(argv[5], 0, &options);
552     if (ppd) {
553       ppdMarkDefaults(ppd);
554       cupsMarkOptions(ppd, noptions, options);
555     }
556 
557     banner = banner_new_from_file(argc == 7 ? argv[6] : "-", &noptions, &options);
558     if (!banner) {
559         fprintf(stderr, "Error: could not read banner file\n");
560         return 1;
561     }
562 
563     ret = generate_banner_pdf(banner,
564                               ppd,
565                               argv[1],
566                               argv[2],
567                               argv[3],
568                               noptions,
569                               options);
570 
571     banner_free(banner);
572     cupsFreeOptions(noptions, options);
573     return ret;
574 }
575