]> git.ipfire.org Git - thirdparty/cups.git/blame - filter/cupsxform.c
Save initial work on private xform API.
[thirdparty/cups.git] / filter / cupsxform.c
CommitLineData
1e01455a
MS
1/*
2 * Utility for converting PDF and JPEG files to raster data, PCL, PDF, or PS.
3 *
4 * Copyright © 2016-2018 by Apple Inc..
5 *
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
7 * information.
8 */
9
10/*
11 * Include necessayr headers...
12 */
13
14#include <cups/xform-private.h>
15
16
17/*
18 * 'main()' - Main entry for transform utility.
19 */
20
21int /* O - Exit status */
22main(int argc, /* I - Number of command-line args */
23 char *argv[]) /* I - Command-line arguments */
24{
25#if 0
26 int i; /* Looping var */
27 const char *filename = NULL, /* File to transform */
28 *content_type, /* Source content type */
29 *device_uri, /* Destination URI */
30 *output_type, /* Destination content type */
31 *resolutions, /* pwg-raster-document-resolution-supported */
32 *sheet_back, /* pwg-raster-document-sheet-back */
33 *types, /* pwg-raster-document-type-supported */
34 *opt; /* Option character */
35 int num_options; /* Number of options */
36 cups_option_t *options; /* Options */
37 int fd = 1; /* Output file/socket */
38 http_t *http = NULL; /* Output HTTP connection */
39 void *write_ptr = &fd; /* Pointer to file/socket/HTTP connection */
40 char resource[1024]; /* URI resource path */
41 xform_write_cb_t write_cb = (xform_write_cb_t)write_fd;
42 /* Write callback */
43 int status = 0; /* Exit status */
44 _cups_thread_t monitor = 0; /* Monitoring thread ID */
45
46
47 /*
48 * Process the command-line...
49 */
50
51 num_options = load_env_options(&options);
52 content_type = getenv("CONTENT_TYPE");
53 device_uri = getenv("DEVICE_URI");
54 output_type = getenv("OUTPUT_TYPE");
55 resolutions = getenv("PWG_RASTER_DOCUMENT_RESOLUTION_SUPPORTED");
56 sheet_back = getenv("PWG_RASTER_DOCUMENT_SHEET_BACK");
57 types = getenv("PWG_RASTER_DOCUMENT_TYPE_SUPPORTED");
58
59 if ((opt = getenv("SERVER_LOGLEVEL")) != NULL)
60 {
61 if (!strcmp(opt, "debug"))
62 Verbosity = 2;
63 else if (!strcmp(opt, "info"))
64 Verbosity = 1;
65 }
66
67 for (i = 1; i < argc; i ++)
68 {
69 if (!strncmp(argv[i], "--", 2))
70 {
71 if (!strcmp(argv[i], "--help"))
72 {
73 usage(0);
74 }
75 else if (!strcmp(argv[i], "--version"))
76 {
77 puts(CUPS_SVERSION);
78 }
79 else
80 {
81 fprintf(stderr, "ERROR: Unknown option '%s'.\n", argv[i]);
82 usage(1);
83 }
84 }
85 else if (argv[i][0] == '-')
86 {
87 for (opt = argv[i] + 1; *opt; opt ++)
88 {
89 switch (*opt)
90 {
91 case 'd' :
92 i ++;
93 if (i >= argc)
94 usage(1);
95
96 device_uri = argv[i];
97 break;
98
99 case 'i' :
100 i ++;
101 if (i >= argc)
102 usage(1);
103
104 content_type = argv[i];
105 break;
106
107 case 'm' :
108 i ++;
109 if (i >= argc)
110 usage(1);
111
112 output_type = argv[i];
113 break;
114
115 case 'o' :
116 i ++;
117 if (i >= argc)
118 usage(1);
119
120 num_options = cupsParseOptions(argv[i], num_options, &options);
121 break;
122
123 case 'r' : /* pwg-raster-document-resolution-supported values */
124 i ++;
125 if (i >= argc)
126 usage(1);
127
128 resolutions = argv[i];
129 break;
130
131 case 's' : /* pwg-raster-document-sheet-back value */
132 i ++;
133 if (i >= argc)
134 usage(1);
135
136 sheet_back = argv[i];
137 break;
138
139 case 't' : /* pwg-raster-document-type-supported values */
140 i ++;
141 if (i >= argc)
142 usage(1);
143
144 types = argv[i];
145 break;
146
147 case 'v' : /* Be verbose... */
148 Verbosity ++;
149 break;
150
151 default :
152 fprintf(stderr, "ERROR: Unknown option '-%c'.\n", *opt);
153 usage(1);
154 break;
155 }
156 }
157 }
158 else if (!filename)
159 filename = argv[i];
160 else
161 usage(1);
162 }
163
164 /*
165 * Check that we have everything we need...
166 */
167
168 if (!filename)
169 usage(1);
170
171 if (!content_type)
172 {
173 if ((opt = strrchr(filename, '.')) != NULL)
174 {
175 if (!strcmp(opt, ".pdf"))
176 content_type = "application/pdf";
177 else if (!strcmp(opt, ".jpg") || !strcmp(opt, ".jpeg"))
178 content_type = "image/jpeg";
179 }
180 }
181
182 if (!content_type)
183 {
184 fprintf(stderr, "ERROR: Unknown format for \"%s\", please specify with '-i' option.\n", filename);
185 usage(1);
186 }
187 else if (strcmp(content_type, "application/pdf") && strcmp(content_type, "image/jpeg"))
188 {
189 fprintf(stderr, "ERROR: Unsupported format \"%s\" for \"%s\".\n", content_type, filename);
190 usage(1);
191 }
192
193 if (!output_type)
194 {
195 fputs("ERROR: Unknown output format, please specify with '-m' option.\n", stderr);
196 usage(1);
197 }
198 else if (strcmp(output_type, "application/vnd.hp-pcl") && strcmp(output_type, "image/pwg-raster") && strcmp(output_type, "image/urf"))
199 {
200 fprintf(stderr, "ERROR: Unsupported output format \"%s\".\n", output_type);
201 usage(1);
202 }
203
204 if (!resolutions)
205 resolutions = "300dpi";
206 if (!sheet_back)
207 sheet_back = "normal";
208 if (!types)
209 types = "sgray_8";
210
211 /*
212 * If the device URI is specified, open the connection...
213 */
214
215 if (device_uri)
216 {
217 char scheme[32], /* URI scheme */
218 userpass[256], /* URI user:pass */
219 host[256], /* URI host */
220 service[32]; /* Service port */
221 int port; /* URI port number */
222 http_addrlist_t *list; /* Address list for socket */
223
224 if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
225 {
226 fprintf(stderr, "ERROR: Invalid device URI \"%s\".\n", device_uri);
227 usage(1);
228 }
229
230 if (strcmp(scheme, "socket") && strcmp(scheme, "ipp") && strcmp(scheme, "ipps"))
231 {
232 fprintf(stderr, "ERROR: Unsupported device URI scheme \"%s\".\n", scheme);
233 usage(1);
234 }
235
236 snprintf(service, sizeof(service), "%d", port);
237 if ((list = httpAddrGetList(host, AF_UNSPEC, service)) == NULL)
238 {
239 fprintf(stderr, "ERROR: Unable to lookup device URI host \"%s\": %s\n", host, cupsLastErrorString());
240 return (1);
241 }
242
243 if (!strcmp(scheme, "socket"))
244 {
245 /*
246 * AppSocket connection...
247 */
248
249 if (!httpAddrConnect2(list, &fd, 30000, NULL))
250 {
251 fprintf(stderr, "ERROR: Unable to connect to \"%s\" on port %d: %s\n", host, port, cupsLastErrorString());
252 return (1);
253 }
254 }
255 else
256 {
257 http_encryption_t encryption; /* Encryption mode */
258 ipp_t *request, /* IPP request */
259 *response; /* IPP response */
260 ipp_attribute_t *attr; /* operations-supported */
261 int create_job = 0; /* Support for Create-Job/Send-Document? */
262 const char *job_name; /* Title of job */
263 const char *media; /* Value of "media" option */
264 const char *sides; /* Value of "sides" option */
265
266 /*
267 * Connect to the IPP/IPPS printer...
268 */
269
270 if (port == 443 || !strcmp(scheme, "ipps"))
271 encryption = HTTP_ENCRYPTION_ALWAYS;
272 else
273 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
274
275 if ((http = httpConnect2(host, port, list, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
276 {
277 fprintf(stderr, "ERROR: Unable to connect to \"%s\" on port %d: %s\n", host, port, cupsLastErrorString());
278 return (1);
279 }
280
281 /*
282 * See if it supports Create-Job + Send-Document...
283 */
284
285 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
286 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri);
287 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
288 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "operations-supported");
289
290 response = cupsDoRequest(http, request, resource);
291 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
292 {
293 fprintf(stderr, "ERROR: Unable to get printer capabilities: %s\n", cupsLastErrorString());
294 return (1);
295 }
296
297 if ((attr = ippFindAttribute(response, "operations-supported", IPP_TAG_ENUM)) == NULL)
298 {
299 fputs("ERROR: Unable to get list of supported operations from printer.\n", stderr);
300 return (1);
301 }
302
303 create_job = ippContainsInteger(attr, IPP_OP_CREATE_JOB) && ippContainsInteger(attr, IPP_OP_SEND_DOCUMENT);
304
305 ippDelete(response);
306
307 /*
308 * Create the job and start printing...
309 */
310
311 if ((job_name = getenv("IPP_JOB_NAME")) == NULL)
312 {
313 if ((job_name = strrchr(filename, '/')) != NULL)
314 job_name ++;
315 else
316 job_name = filename;
317 }
318
319 if (create_job)
320 {
321 int job_id = 0; /* Job ID */
322
323 request = ippNewRequest(IPP_OP_CREATE_JOB);
324 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri);
325 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
326 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, job_name);
327
328 response = cupsDoRequest(http, request, resource);
329 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
330 job_id = ippGetInteger(attr, 0);
331 ippDelete(response);
332
333 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
334 {
335 fprintf(stderr, "ERROR: Unable to create print job: %s\n", cupsLastErrorString());
336 return (1);
337 }
338 else if (job_id <= 0)
339 {
340 fputs("ERROR: No job-id for created print job.\n", stderr);
341 return (1);
342 }
343
344 request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
345 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri);
346 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
347 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
348 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, output_type);
349 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
350 }
351 else
352 {
353 request = ippNewRequest(IPP_OP_PRINT_JOB);
354 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri);
355 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
356 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, output_type);
357 }
358
359 if ((media = cupsGetOption("media", num_options, options)) != NULL)
360 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media", NULL, media);
361
362 if ((sides = cupsGetOption("sides", num_options, options)) != NULL)
363 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, sides);
364
365 if (cupsSendRequest(http, request, resource, 0) != HTTP_STATUS_CONTINUE)
366 {
367 fprintf(stderr, "ERROR: Unable to send print data: %s\n", cupsLastErrorString());
368 return (1);
369 }
370
371 ippDelete(request);
372
373 write_cb = (xform_write_cb_t)httpWrite2;
374 write_ptr = http;
375
376 monitor = _cupsThreadCreate((_cups_thread_func_t)monitor_ipp, (void *)device_uri);
377 }
378
379 httpAddrFreeList(list);
380 }
381
382 /*
383 * Do transform...
384 */
385
386 status = xform_document(filename, content_type, output_type, resolutions, sheet_back, types, num_options, options, write_cb, write_ptr);
387
388 if (http)
389 {
390 ippDelete(cupsGetResponse(http, resource));
391
392 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
393 {
394 fprintf(stderr, "ERROR: Unable to send print data: %s\n", cupsLastErrorString());
395 status = 1;
396 }
397
398 httpClose(http);
399 }
400 else if (fd != 1)
401 close(fd);
402
403 if (monitor)
404 _cupsThreadCancel(monitor);
405
406 return (status);
407#endif /* 0 */
408
409 (void)argc;
410 (void)argv;
411
412 return (0);
413}
414
415
416/*
417 * 'pcl_end_job()' - End a PCL "job".
418 */
419
420static void
421pcl_end_job(xform_raster_t *ras, /* I - Raster information */
422 xform_write_cb_t cb, /* I - Write callback */
423 void *ctx) /* I - Write context */
424{
425 (void)ras;
426
427 /*
428 * Send a PCL reset sequence.
429 */
430
431 (*cb)(ctx, (const unsigned char *)"\033E", 2);
432}
433
434
435/*
436 * 'pcl_end_page()' - End of PCL page.
437 */
438
439static void
440pcl_end_page(xform_raster_t *ras, /* I - Raster information */
441 unsigned page, /* I - Current page */
442 xform_write_cb_t cb, /* I - Write callback */
443 void *ctx) /* I - Write context */
444{
445 /*
446 * End graphics...
447 */
448
449 (*cb)(ctx, (const unsigned char *)"\033*r0B", 5);
450
451 /*
452 * Formfeed as needed...
453 */
454
455 if (!(ras->header.Duplex && (page & 1)))
456 (*cb)(ctx, (const unsigned char *)"\014", 1);
457
458 /*
459 * Free the output buffer...
460 */
461
462 free(ras->out_buffer);
463 ras->out_buffer = NULL;
464}
465
466
467/*
468 * 'pcl_init()' - Initialize callbacks for PCL output.
469 */
470
471static void
472pcl_init(xform_raster_t *ras) /* I - Raster information */
473{
474 ras->end_job = pcl_end_job;
475 ras->end_page = pcl_end_page;
476 ras->start_job = pcl_start_job;
477 ras->start_page = pcl_start_page;
478 ras->write_line = pcl_write_line;
479}
480
481
482/*
483 * 'pcl_printf()' - Write a formatted string.
484 */
485
486static void
487pcl_printf(xform_write_cb_t cb, /* I - Write callback */
488 void *ctx, /* I - Write context */
489 const char *format, /* I - Printf-style format string */
490 ...) /* I - Additional arguments as needed */
491{
492 va_list ap; /* Argument pointer */
493 char buffer[8192]; /* Buffer */
494
495
496 va_start(ap, format);
497 vsnprintf(buffer, sizeof(buffer), format, ap);
498 va_end(ap);
499
500 (*cb)(ctx, (const unsigned char *)buffer, strlen(buffer));
501}
502
503
504/*
505 * 'pcl_start_job()' - Start a PCL "job".
506 */
507
508static void
509pcl_start_job(xform_raster_t *ras, /* I - Raster information */
510 xform_write_cb_t cb, /* I - Write callback */
511 void *ctx) /* I - Write context */
512{
513 (void)ras;
514
515 /*
516 * Send a PCL reset sequence.
517 */
518
519 (*cb)(ctx, (const unsigned char *)"\033E", 2);
520}
521
522
523/*
524 * 'pcl_start_page()' - Start a PCL page.
525 */
526
527static void
528pcl_start_page(xform_raster_t *ras, /* I - Raster information */
529 unsigned page, /* I - Current page */
530 xform_write_cb_t cb, /* I - Write callback */
531 void *ctx) /* I - Write context */
532{
533 /*
534 * Setup margins to be 1/6" top and bottom and 1/4" or .135" on the
535 * left and right.
536 */
537
538 ras->top = ras->header.HWResolution[1] / 6;
539 ras->bottom = ras->header.cupsHeight - ras->header.HWResolution[1] / 6 - 1;
540
541 if (ras->header.PageSize[1] == 842)
542 {
543 /* A4 gets special side margins to expose an 8" print area */
544 ras->left = (ras->header.cupsWidth - 8 * ras->header.HWResolution[0]) / 2;
545 ras->right = ras->left + 8 * ras->header.HWResolution[0] - 1;
546 }
547 else
548 {
549 /* All other sizes get 1/4" margins */
550 ras->left = ras->header.HWResolution[0] / 4;
551 ras->right = ras->header.cupsWidth - ras->header.HWResolution[0] / 4 - 1;
552 }
553
554 if (!ras->header.Duplex || (page & 1))
555 {
556 /*
557 * Set the media size...
558 */
559
560 pcl_printf(cb, ctx, "\033&l12D\033&k12H");
561 /* Set 12 LPI, 10 CPI */
562 pcl_printf(cb, ctx, "\033&l0O"); /* Set portrait orientation */
563
564 switch (ras->header.PageSize[1])
565 {
566 case 540 : /* Monarch Envelope */
567 pcl_printf(cb, ctx, "\033&l80A");
568 break;
569
570 case 595 : /* A5 */
571 pcl_printf(cb, ctx, "\033&l25A");
572 break;
573
574 case 624 : /* DL Envelope */
575 pcl_printf(cb, ctx, "\033&l90A");
576 break;
577
578 case 649 : /* C5 Envelope */
579 pcl_printf(cb, ctx, "\033&l91A");
580 break;
581
582 case 684 : /* COM-10 Envelope */
583 pcl_printf(cb, ctx, "\033&l81A");
584 break;
585
586 case 709 : /* B5 Envelope */
587 pcl_printf(cb, ctx, "\033&l100A");
588 break;
589
590 case 756 : /* Executive */
591 pcl_printf(cb, ctx, "\033&l1A");
592 break;
593
594 case 792 : /* Letter */
595 pcl_printf(cb, ctx, "\033&l2A");
596 break;
597
598 case 842 : /* A4 */
599 pcl_printf(cb, ctx, "\033&l26A");
600 break;
601
602 case 1008 : /* Legal */
603 pcl_printf(cb, ctx, "\033&l3A");
604 break;
605
606 case 1191 : /* A3 */
607 pcl_printf(cb, ctx, "\033&l27A");
608 break;
609
610 case 1224 : /* Tabloid */
611 pcl_printf(cb, ctx, "\033&l6A");
612 break;
613 }
614
615 /*
616 * Set top margin and turn off perforation skip...
617 */
618
619 pcl_printf(cb, ctx, "\033&l%uE\033&l0L", 12 * ras->top / ras->header.HWResolution[1]);
620
621 if (ras->header.Duplex)
622 {
623 int mode = ras->header.Duplex ? 1 + ras->header.Tumble != 0 : 0;
624
625 pcl_printf(cb, ctx, "\033&l%dS", mode);
626 /* Set duplex mode */
627 }
628 }
629 else if (ras->header.Duplex)
630 pcl_printf(cb, ctx, "\033&a2G"); /* Print on back side */
631
632 /*
633 * Set graphics mode...
634 */
635
636 pcl_printf(cb, ctx, "\033*t%uR", ras->header.HWResolution[0]);
637 /* Set resolution */
638 pcl_printf(cb, ctx, "\033*r%uS", ras->right - ras->left + 1);
639 /* Set width */
640 pcl_printf(cb, ctx, "\033*r%uT", ras->bottom - ras->top + 1);
641 /* Set height */
642 pcl_printf(cb, ctx, "\033&a0H\033&a%uV", 720 * ras->top / ras->header.HWResolution[1]);
643 /* Set position */
644
645 pcl_printf(cb, ctx, "\033*b2M"); /* Use PackBits compression */
646 pcl_printf(cb, ctx, "\033*r1A"); /* Start graphics */
647
648 /*
649 * Allocate the output buffer...
650 */
651
652 ras->out_blanks = 0;
653 ras->out_length = (ras->right - ras->left + 8) / 8;
654 ras->out_buffer = malloc(ras->out_length);
655 ras->comp_buffer = malloc(2 * ras->out_length + 2);
656}
657
658
659/*
660 * 'pcl_write_line()' - Write a line of raster data.
661 */
662
663static void
664pcl_write_line(
665 xform_raster_t *ras, /* I - Raster information */
666 unsigned y, /* I - Line number */
667 const unsigned char *line, /* I - Pixels on line */
668 xform_write_cb_t cb, /* I - Write callback */
669 void *ctx) /* I - Write context */
670{
671 unsigned x; /* Column number */
672 unsigned char bit, /* Current bit */
673 byte, /* Current byte */
674 *outptr, /* Pointer into output buffer */
675 *outend, /* End of output buffer */
676 *start, /* Start of sequence */
677 *compptr; /* Pointer into compression buffer */
678 unsigned count; /* Count of bytes for output */
679
680
681 if (line[0] == 255 && !memcmp(line, line + 1, ras->right - ras->left))
682 {
683 /*
684 * Skip blank line...
685 */
686
687 ras->out_blanks ++;
688 return;
689 }
690
691 /*
692 * Dither the line into the output buffer...
693 */
694
695 y &= 63;
696
697 for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++)
698 {
699 if (*line <= threshold[x & 63][y])
700 byte |= bit;
701
702 if (bit == 1)
703 {
704 *outptr++ = byte;
705 byte = 0;
706 bit = 128;
707 }
708 else
709 bit >>= 1;
710 }
711
712 if (bit != 128)
713 *outptr++ = byte;
714
715 /*
716 * Apply compression...
717 */
718
719 compptr = ras->comp_buffer;
720 outend = outptr;
721 outptr = ras->out_buffer;
722
723 while (outptr < outend)
724 {
725 if ((outptr + 1) >= outend)
726 {
727 /*
728 * Single byte on the end...
729 */
730
731 *compptr++ = 0x00;
732 *compptr++ = *outptr++;
733 }
734 else if (outptr[0] == outptr[1])
735 {
736 /*
737 * Repeated sequence...
738 */
739
740 outptr ++;
741 count = 2;
742
743 while (outptr < (outend - 1) &&
744 outptr[0] == outptr[1] &&
745 count < 127)
746 {
747 outptr ++;
748 count ++;
749 }
750
751 *compptr++ = (unsigned char)(257 - count);
752 *compptr++ = *outptr++;
753 }
754 else
755 {
756 /*
757 * Non-repeated sequence...
758 */
759
760 start = outptr;
761 outptr ++;
762 count = 1;
763
764 while (outptr < (outend - 1) &&
765 outptr[0] != outptr[1] &&
766 count < 127)
767 {
768 outptr ++;
769 count ++;
770 }
771
772 *compptr++ = (unsigned char)(count - 1);
773
774 memcpy(compptr, start, count);
775 compptr += count;
776 }
777 }
778
779 /*
780 * Output the line...
781 */
782
783 if (ras->out_blanks > 0)
784 {
785 /*
786 * Skip blank lines first...
787 */
788
789 pcl_printf(cb, ctx, "\033*b%dY", ras->out_blanks);
790 ras->out_blanks = 0;
791 }
792
793 pcl_printf(cb, ctx, "\033*b%dW", (int)(compptr - ras->comp_buffer));
794 (*cb)(ctx, ras->comp_buffer, (size_t)(compptr - ras->comp_buffer));
795}
796
797
798/*
799 * 'raster_end_job()' - End a raster "job".
800 */
801
802static void
803raster_end_job(xform_raster_t *ras, /* I - Raster information */
804 xform_write_cb_t cb, /* I - Write callback */
805 void *ctx) /* I - Write context */
806{
807 (void)cb;
808 (void)ctx;
809
810 cupsRasterClose(ras->ras);
811}
812
813
814/*
815 * 'raster_end_page()' - End of raster page.
816 */
817
818static void
819raster_end_page(xform_raster_t *ras, /* I - Raster information */
820 unsigned page, /* I - Current page */
821 xform_write_cb_t cb, /* I - Write callback */
822 void *ctx) /* I - Write context */
823{
824 (void)page;
825 (void)cb;
826 (void)ctx;
827
828 if (ras->header.cupsBitsPerPixel == 1)
829 {
830 free(ras->out_buffer);
831 ras->out_buffer = NULL;
832 }
833}
834
835
836/*
837 * 'raster_init()' - Initialize callbacks for raster output.
838 */
839
840static void
841raster_init(xform_raster_t *ras) /* I - Raster information */
842{
843 ras->end_job = raster_end_job;
844 ras->end_page = raster_end_page;
845 ras->start_job = raster_start_job;
846 ras->start_page = raster_start_page;
847 ras->write_line = raster_write_line;
848}
849
850
851/*
852 * 'raster_start_job()' - Start a raster "job".
853 */
854
855static void
856raster_start_job(xform_raster_t *ras, /* I - Raster information */
857 xform_write_cb_t cb, /* I - Write callback */
858 void *ctx) /* I - Write context */
859{
860 ras->ras = cupsRasterOpenIO((cups_raster_iocb_t)cb, ctx, !strcmp(ras->format, "image/pwg-raster") ? CUPS_RASTER_WRITE_PWG : CUPS_RASTER_WRITE_APPLE);
861}
862
863
864/*
865 * 'raster_start_page()' - Start a raster page.
866 */
867
868static void
869raster_start_page(xform_raster_t *ras,/* I - Raster information */
870 unsigned page,/* I - Current page */
871 xform_write_cb_t cb, /* I - Write callback */
872 void *ctx)/* I - Write context */
873{
874 (void)cb;
875 (void)ctx;
876
877 ras->left = 0;
878 ras->top = 0;
879 ras->right = ras->header.cupsWidth - 1;
880 ras->bottom = ras->header.cupsHeight - 1;
881
882 if (ras->header.Duplex && !(page & 1))
883 cupsRasterWriteHeader2(ras->ras, &ras->back_header);
884 else
885 cupsRasterWriteHeader2(ras->ras, &ras->header);
886
887 if (ras->header.cupsBitsPerPixel == 1)
888 {
889 ras->out_length = ras->header.cupsBytesPerLine;
890 ras->out_buffer = malloc(ras->header.cupsBytesPerLine);
891 }
892}
893
894
895/*
896 * 'raster_write_line()' - Write a line of raster data.
897 */
898
899static void
900raster_write_line(
901 xform_raster_t *ras, /* I - Raster information */
902 unsigned y, /* I - Line number */
903 const unsigned char *line, /* I - Pixels on line */
904 xform_write_cb_t cb, /* I - Write callback */
905 void *ctx) /* I - Write context */
906{
907 (void)cb;
908 (void)ctx;
909
910 if (ras->header.cupsBitsPerPixel == 1)
911 {
912 /*
913 * Dither the line into the output buffer...
914 */
915
916 unsigned x; /* Column number */
917 unsigned char bit, /* Current bit */
918 byte, /* Current byte */
919 *outptr; /* Pointer into output buffer */
920
921 y &= 63;
922
923 if (ras->header.cupsColorSpace == CUPS_CSPACE_SW)
924 {
925 for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++)
926 {
927 if (*line > threshold[x % 25][y])
928 byte |= bit;
929
930 if (bit == 1)
931 {
932 *outptr++ = byte;
933 byte = 0;
934 bit = 128;
935 }
936 else
937 bit >>= 1;
938 }
939 }
940 else
941 {
942 for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++)
943 {
944 if (*line <= threshold[x & 63][y])
945 byte |= bit;
946
947 if (bit == 1)
948 {
949 *outptr++ = byte;
950 byte = 0;
951 bit = 128;
952 }
953 else
954 bit >>= 1;
955 }
956 }
957
958 if (bit != 128)
959 *outptr++ = byte;
960
961 cupsRasterWritePixels(ras->ras, ras->out_buffer, ras->header.cupsBytesPerLine);
962 }
963 else
964 cupsRasterWritePixels(ras->ras, (unsigned char *)line, ras->header.cupsBytesPerLine);
965}
966
967
968/*
969 * 'write_fd()' - Write to a file/socket.
970 */
971
972static ssize_t /* O - Number of bytes written or -1 on error */
973write_fd(int *fd, /* I - File descriptor */
974 const unsigned char *buffer, /* I - Buffer */
975 size_t bytes) /* I - Number of bytes to write */
976{
977 ssize_t temp, /* Temporary byte count */
978 total = 0; /* Total bytes written */
979
980
981 while (bytes > 0)
982 {
983 if ((temp = write(*fd, buffer, bytes)) < 0)
984 {
985 if (errno == EINTR || errno == EAGAIN)
986 continue;
987 else
988 return (-1);
989 }
990
991 total += temp;
992 bytes -= (size_t)temp;
993 buffer += temp;
994 }
995
996 return (total);
997}