1 /* 2 * Hewlett-Packard Page Control Language filter for CUPS. 3 * 4 * Copyright 2007-2015 by Apple Inc. 5 * Copyright 1993-2007 by Easy Software Products. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16 /* 17 * Include necessary headers... 18 */ 19 20 #include <cups/cups.h> 21 #include <cups/ppd.h> 22 #include <cups/string-private.h> 23 #include <cups/language-private.h> 24 #include <cups/raster.h> 25 #include <unistd.h> 26 #include <fcntl.h> 27 #include <signal.h> 28 29 30 /* 31 * Globals... 32 */ 33 34 unsigned char *Planes[4], /* Output buffers */ 35 *CompBuffer, /* Compression buffer */ 36 *BitBuffer; /* Buffer for output bits */ 37 unsigned NumPlanes, /* Number of color planes */ 38 ColorBits, /* Number of bits per color */ 39 Feed; /* Number of lines to skip */ 40 int Duplex, /* Current duplex mode */ 41 Page, /* Current page number */ 42 Canceled; /* Has the current job been canceled? */ 43 44 45 /* 46 * Prototypes... 47 */ 48 49 void Setup(void); 50 void StartPage(ppd_file_t *ppd, cups_page_header2_t *header); 51 void EndPage(void); 52 void Shutdown(void); 53 54 void CancelJob(int sig); 55 void CompressData(unsigned char *line, unsigned length, unsigned plane, unsigned type); 56 void OutputLine(cups_page_header2_t *header); 57 58 59 /* 60 * 'Setup()' - Prepare the printer for printing. 61 */ 62 63 void 64 Setup(void) 65 { 66 /* 67 * Send a PCL reset sequence. 68 */ 69 70 putchar(0x1b); 71 putchar('E'); 72 } 73 74 75 /* 76 * 'StartPage()' - Start a page of graphics. 77 */ 78 79 void 80 StartPage(ppd_file_t *ppd, /* I - PPD file */ 81 cups_page_header2_t *header) /* I - Page header */ 82 { 83 unsigned plane; /* Looping var */ 84 85 86 /* 87 * Show page device dictionary... 88 */ 89 90 fprintf(stderr, "DEBUG: StartPage...\n"); 91 fprintf(stderr, "DEBUG: Duplex = %d\n", header->Duplex); 92 fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0], header->HWResolution[1]); 93 fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", header->ImagingBoundingBox[0], header->ImagingBoundingBox[1], header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]); 94 fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0], header->Margins[1]); 95 fprintf(stderr, "DEBUG: ManualFeed = %d\n", header->ManualFeed); 96 fprintf(stderr, "DEBUG: MediaPosition = %d\n", header->MediaPosition); 97 fprintf(stderr, "DEBUG: NumCopies = %d\n", header->NumCopies); 98 fprintf(stderr, "DEBUG: Orientation = %d\n", header->Orientation); 99 fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0], header->PageSize[1]); 100 fprintf(stderr, "DEBUG: cupsWidth = %d\n", header->cupsWidth); 101 fprintf(stderr, "DEBUG: cupsHeight = %d\n", header->cupsHeight); 102 fprintf(stderr, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType); 103 fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor); 104 fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel); 105 fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine); 106 fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder); 107 fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace); 108 fprintf(stderr, "DEBUG: cupsCompression = %d\n", header->cupsCompression); 109 110 /* 111 * Setup printer/job attributes... 112 */ 113 114 Duplex = header->Duplex; 115 ColorBits = header->cupsBitsPerColor; 116 117 if ((!Duplex || (Page & 1)) && header->MediaPosition) 118 printf("\033&l%dH", /* Set media position */ 119 header->MediaPosition); 120 121 if (Duplex && ppd && ppd->model_number == 2) 122 { 123 /* 124 * Handle duplexing on new DeskJet printers... 125 */ 126 127 printf("\033&l-2H"); /* Load media */ 128 129 if (Page & 1) 130 printf("\033&l2S"); /* Set duplex mode */ 131 } 132 133 if (!Duplex || (Page & 1) || (ppd && ppd->model_number == 2)) 134 { 135 /* 136 * Set the media size... 137 */ 138 139 printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */ 140 printf("\033&l0O"); /* Set portrait orientation */ 141 142 switch (header->PageSize[1]) 143 { 144 case 540 : /* Monarch Envelope */ 145 printf("\033&l80A"); /* Set page size */ 146 break; 147 148 case 595 : /* A5 */ 149 printf("\033&l25A"); /* Set page size */ 150 break; 151 152 case 624 : /* DL Envelope */ 153 printf("\033&l90A"); /* Set page size */ 154 break; 155 156 case 649 : /* C5 Envelope */ 157 printf("\033&l91A"); /* Set page size */ 158 break; 159 160 case 684 : /* COM-10 Envelope */ 161 printf("\033&l81A"); /* Set page size */ 162 break; 163 164 case 709 : /* B5 Envelope */ 165 printf("\033&l100A"); /* Set page size */ 166 break; 167 168 case 756 : /* Executive */ 169 printf("\033&l1A"); /* Set page size */ 170 break; 171 172 case 792 : /* Letter */ 173 printf("\033&l2A"); /* Set page size */ 174 break; 175 176 case 842 : /* A4 */ 177 printf("\033&l26A"); /* Set page size */ 178 break; 179 180 case 1008 : /* Legal */ 181 printf("\033&l3A"); /* Set page size */ 182 break; 183 184 case 1191 : /* A3 */ 185 printf("\033&l27A"); /* Set page size */ 186 break; 187 188 case 1224 : /* Tabloid */ 189 printf("\033&l6A"); /* Set page size */ 190 break; 191 } 192 193 printf("\033&l%dP", /* Set page length */ 194 header->PageSize[1] / 12); 195 printf("\033&l0E"); /* Set top margin to 0 */ 196 } 197 198 if (!Duplex || (Page & 1)) 199 { 200 /* 201 * Set other job options... 202 */ 203 204 printf("\033&l%dX", header->NumCopies); /* Set number copies */ 205 206 if (header->cupsMediaType && 207 (!ppd || ppd->model_number != 2 || header->HWResolution[0] == 600)) 208 printf("\033&l%dM", /* Set media type */ 209 header->cupsMediaType); 210 211 if (!ppd || ppd->model_number != 2) 212 { 213 int mode = Duplex ? 1 + header->Tumble != 0 : 0; 214 215 printf("\033&l%dS", mode); /* Set duplex mode */ 216 printf("\033&l0L"); /* Turn off perforation skip */ 217 } 218 } 219 else if (!ppd || ppd->model_number != 2) 220 printf("\033&a2G"); /* Set back side */ 221 222 /* 223 * Set graphics mode... 224 */ 225 226 if (ppd && ppd->model_number == 2) 227 { 228 /* 229 * Figure out the number of color planes... 230 */ 231 232 if (header->cupsColorSpace == CUPS_CSPACE_KCMY) 233 NumPlanes = 4; 234 else 235 NumPlanes = 1; 236 237 /* 238 * Set the resolution and top-of-form... 239 */ 240 241 printf("\033&u%dD", header->HWResolution[0]); 242 /* Resolution */ 243 printf("\033&l0e0L"); /* Reset top and don't skip */ 244 printf("\033*p0Y\033*p0X"); /* Set top of form */ 245 246 /* 247 * Send 26-byte configure image data command with horizontal and 248 * vertical resolutions as well as a color count... 249 */ 250 251 printf("\033*g26W"); 252 putchar(2); /* Format 2 */ 253 putchar((int)NumPlanes); /* Output planes */ 254 255 putchar((int)(header->HWResolution[0] >> 8));/* Black resolution */ 256 putchar((int)header->HWResolution[0]); 257 putchar((int)(header->HWResolution[1] >> 8)); 258 putchar((int)header->HWResolution[1]); 259 putchar(0); 260 putchar(1 << ColorBits); /* # of black levels */ 261 262 putchar((int)(header->HWResolution[0] >> 8));/* Cyan resolution */ 263 putchar((int)header->HWResolution[0]); 264 putchar((int)(header->HWResolution[1] >> 8)); 265 putchar((int)header->HWResolution[1]); 266 putchar(0); 267 putchar(1 << ColorBits); /* # of cyan levels */ 268 269 putchar((int)(header->HWResolution[0] >> 8));/* Magenta resolution */ 270 putchar((int)header->HWResolution[0]); 271 putchar((int)(header->HWResolution[1] >> 8)); 272 putchar((int)header->HWResolution[1]); 273 putchar(0); 274 putchar(1 << ColorBits); /* # of magenta levels */ 275 276 putchar((int)(header->HWResolution[0] >> 8));/* Yellow resolution */ 277 putchar((int)header->HWResolution[0]); 278 putchar((int)(header->HWResolution[1] >> 8)); 279 putchar((int)header->HWResolution[1]); 280 putchar(0); 281 putchar(1 << ColorBits); /* # of yellow levels */ 282 283 printf("\033&l0H"); /* Set media position */ 284 } 285 else 286 { 287 printf("\033*t%uR", header->HWResolution[0]); 288 /* Set resolution */ 289 290 if (header->cupsColorSpace == CUPS_CSPACE_KCMY) 291 { 292 NumPlanes = 4; 293 printf("\033*r-4U"); /* Set KCMY graphics */ 294 } 295 else if (header->cupsColorSpace == CUPS_CSPACE_CMY) 296 { 297 NumPlanes = 3; 298 printf("\033*r-3U"); /* Set CMY graphics */ 299 } 300 else 301 NumPlanes = 1; /* Black&white graphics */ 302 303 /* 304 * Set size and position of graphics... 305 */ 306 307 printf("\033*r%uS", header->cupsWidth); /* Set width */ 308 printf("\033*r%uT", header->cupsHeight); /* Set height */ 309 310 printf("\033&a0H"); /* Set horizontal position */ 311 312 if (ppd) 313 printf("\033&a%.0fV", /* Set vertical position */ 314 10.0 * (ppd->sizes[0].length - ppd->sizes[0].top)); 315 else 316 printf("\033&a0V"); /* Set top-of-page */ 317 } 318 319 printf("\033*r1A"); /* Start graphics */ 320 321 if (header->cupsCompression) 322 printf("\033*b%uM", /* Set compression */ 323 header->cupsCompression); 324 325 Feed = 0; /* No blank lines yet */ 326 327 /* 328 * Allocate memory for a line of graphics... 329 */ 330 331 if ((Planes[0] = malloc(header->cupsBytesPerLine + NumPlanes)) == NULL) 332 { 333 fputs("ERROR: Unable to allocate memory\n", stderr); 334 exit(1); 335 } 336 337 for (plane = 1; plane < NumPlanes; plane ++) 338 Planes[plane] = Planes[0] + plane * header->cupsBytesPerLine / NumPlanes; 339 340 if (ColorBits > 1) 341 BitBuffer = malloc(ColorBits * ((header->cupsWidth + 7) / 8)); 342 else 343 BitBuffer = NULL; 344 345 if (header->cupsCompression) 346 CompBuffer = malloc(header->cupsBytesPerLine * 2 + 2); 347 else 348 CompBuffer = NULL; 349 } 350 351 352 /* 353 * 'EndPage()' - Finish a page of graphics. 354 */ 355 356 void 357 EndPage(void) 358 { 359 /* 360 * Eject the current page... 361 */ 362 363 if (NumPlanes > 1) 364 { 365 printf("\033*rC"); /* End color GFX */ 366 367 if (!(Duplex && (Page & 1))) 368 printf("\033&l0H"); /* Eject current page */ 369 } 370 else 371 { 372 printf("\033*r0B"); /* End GFX */ 373 374 if (!(Duplex && (Page & 1))) 375 printf("\014"); /* Eject current page */ 376 } 377 378 fflush(stdout); 379 380 /* 381 * Free memory... 382 */ 383 384 free(Planes[0]); 385 386 if (BitBuffer) 387 free(BitBuffer); 388 389 if (CompBuffer) 390 free(CompBuffer); 391 } 392 393 394 /* 395 * 'Shutdown()' - Shutdown the printer. 396 */ 397 398 void 399 Shutdown(void) 400 { 401 /* 402 * Send a PCL reset sequence. 403 */ 404 405 putchar(0x1b); 406 putchar('E'); 407 } 408 409 410 /* 411 * 'CancelJob()' - Cancel the current job... 412 */ 413 414 void 415 CancelJob(int sig) /* I - Signal */ 416 { 417 (void)sig; 418 419 Canceled = 1; 420 } 421 422 423 /* 424 * 'CompressData()' - Compress a line of graphics. 425 */ 426 427 void 428 CompressData(unsigned char *line, /* I - Data to compress */ 429 unsigned length, /* I - Number of bytes */ 430 unsigned plane, /* I - Color plane */ 431 unsigned type) /* I - Type of compression */ 432 { 433 unsigned char *line_ptr, /* Current byte pointer */ 434 *line_end, /* End-of-line byte pointer */ 435 *comp_ptr, /* Pointer into compression buffer */ 436 *start; /* Start of compression sequence */ 437 unsigned count; /* Count of bytes for output */ 438 439 440 switch (type) 441 { 442 default : 443 /* 444 * Do no compression... 445 */ 446 447 line_ptr = line; 448 line_end = line + length; 449 break; 450 451 case 1 : 452 /* 453 * Do run-length encoding... 454 */ 455 456 line_end = line + length; 457 for (line_ptr = line, comp_ptr = CompBuffer; 458 line_ptr < line_end; 459 comp_ptr += 2, line_ptr += count) 460 { 461 for (count = 1; 462 (line_ptr + count) < line_end && 463 line_ptr[0] == line_ptr[count] && 464 count < 256; 465 count ++); 466 467 comp_ptr[0] = (unsigned char)(count - 1); 468 comp_ptr[1] = line_ptr[0]; 469 } 470 471 line_ptr = CompBuffer; 472 line_end = comp_ptr; 473 break; 474 475 case 2 : 476 /* 477 * Do TIFF pack-bits encoding... 478 */ 479 480 line_ptr = line; 481 line_end = line + length; 482 comp_ptr = CompBuffer; 483 484 while (line_ptr < line_end) 485 { 486 if ((line_ptr + 1) >= line_end) 487 { 488 /* 489 * Single byte on the end... 490 */ 491 492 *comp_ptr++ = 0x00; 493 *comp_ptr++ = *line_ptr++; 494 } 495 else if (line_ptr[0] == line_ptr[1]) 496 { 497 /* 498 * Repeated sequence... 499 */ 500 501 line_ptr ++; 502 count = 2; 503 504 while (line_ptr < (line_end - 1) && 505 line_ptr[0] == line_ptr[1] && 506 count < 127) 507 { 508 line_ptr ++; 509 count ++; 510 } 511 512 *comp_ptr++ = (unsigned char)(257 - count); 513 *comp_ptr++ = *line_ptr++; 514 } 515 else 516 { 517 /* 518 * Non-repeated sequence... 519 */ 520 521 start = line_ptr; 522 line_ptr ++; 523 count = 1; 524 525 while (line_ptr < (line_end - 1) && 526 line_ptr[0] != line_ptr[1] && 527 count < 127) 528 { 529 line_ptr ++; 530 count ++; 531 } 532 533 *comp_ptr++ = (unsigned char)(count - 1); 534 535 memcpy(comp_ptr, start, count); 536 comp_ptr += count; 537 } 538 } 539 540 line_ptr = CompBuffer; 541 line_end = comp_ptr; 542 break; 543 } 544 545 /* 546 * Set the length of the data and write a raster plane... 547 */ 548 549 printf("\033*b%d%c", (int)(line_end - line_ptr), plane); 550 fwrite(line_ptr, (size_t)(line_end - line_ptr), 1, stdout); 551 } 552 553 554 /* 555 * 'OutputLine()' - Output a line of graphics. 556 */ 557 558 void 559 OutputLine(cups_page_header2_t *header) /* I - Page header */ 560 { 561 unsigned plane, /* Current plane */ 562 bytes, /* Bytes to write */ 563 count; /* Bytes to convert */ 564 unsigned char bit, /* Current plane data */ 565 bit0, /* Current low bit data */ 566 bit1, /* Current high bit data */ 567 *plane_ptr, /* Pointer into Planes */ 568 *bit_ptr; /* Pointer into BitBuffer */ 569 570 571 /* 572 * Output whitespace as needed... 573 */ 574 575 if (Feed > 0) 576 { 577 printf("\033*b%dY", Feed); 578 Feed = 0; 579 } 580 581 /* 582 * Write bitmap data as needed... 583 */ 584 585 bytes = (header->cupsWidth + 7) / 8; 586 587 for (plane = 0; plane < NumPlanes; plane ++) 588 if (ColorBits == 1) 589 { 590 /* 591 * Send bits as-is... 592 */ 593 594 CompressData(Planes[plane], bytes, plane < (NumPlanes - 1) ? 'V' : 'W', 595 header->cupsCompression); 596 } 597 else 598 { 599 /* 600 * Separate low and high bit data into separate buffers. 601 */ 602 603 for (count = header->cupsBytesPerLine / NumPlanes, 604 plane_ptr = Planes[plane], bit_ptr = BitBuffer; 605 count > 0; 606 count -= 2, plane_ptr += 2, bit_ptr ++) 607 { 608 bit = plane_ptr[0]; 609 610 bit0 = (unsigned char)(((bit & 64) << 1) | ((bit & 16) << 2) | ((bit & 4) << 3) | ((bit & 1) << 4)); 611 bit1 = (unsigned char)((bit & 128) | ((bit & 32) << 1) | ((bit & 8) << 2) | ((bit & 2) << 3)); 612 613 if (count > 1) 614 { 615 bit = plane_ptr[1]; 616 617 bit0 |= (unsigned char)((bit & 1) | ((bit & 4) >> 1) | ((bit & 16) >> 2) | ((bit & 64) >> 3)); 618 bit1 |= (unsigned char)(((bit & 2) >> 1) | ((bit & 8) >> 2) | ((bit & 32) >> 3) | ((bit & 128) >> 4)); 619 } 620 621 bit_ptr[0] = bit0; 622 bit_ptr[bytes] = bit1; 623 } 624 625 /* 626 * Send low and high bits... 627 */ 628 629 CompressData(BitBuffer, bytes, 'V', header->cupsCompression); 630 CompressData(BitBuffer + bytes, bytes, plane < (NumPlanes - 1) ? 'V' : 'W', 631 header->cupsCompression); 632 } 633 634 fflush(stdout); 635 } 636 637 638 /* 639 * 'main()' - Main entry and processing of driver. 640 */ 641 642 int /* O - Exit status */ 643 main(int argc, /* I - Number of command-line arguments */ 644 char *argv[]) /* I - Command-line arguments */ 645 { 646 int fd; /* File descriptor */ 647 cups_raster_t *ras; /* Raster stream for printing */ 648 cups_page_header2_t header; /* Page header from file */ 649 unsigned y; /* Current line */ 650 ppd_file_t *ppd; /* PPD file */ 651 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) 652 struct sigaction action; /* Actions for POSIX signals */ 653 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ 654 655 656 /* 657 * Make sure status messages are not buffered... 658 */ 659 660 setbuf(stderr, NULL); 661 662 /* 663 * Check command-line... 664 */ 665 666 if (argc < 6 || argc > 7) 667 { 668 /* 669 * We don't have the correct number of arguments; write an error message 670 * and return. 671 */ 672 673 _cupsLangPrintFilter(stderr, "ERROR", 674 _("%s job-id user title copies options [file]"), 675 "rastertohp"); 676 return (1); 677 } 678 679 /* 680 * Open the page stream... 681 */ 682 683 if (argc == 7) 684 { 685 if ((fd = open(argv[6], O_RDONLY)) == -1) 686 { 687 _cupsLangPrintError("ERROR", _("Unable to open raster file")); 688 sleep(1); 689 return (1); 690 } 691 } 692 else 693 fd = 0; 694 695 ras = cupsRasterOpen(fd, CUPS_RASTER_READ); 696 697 /* 698 * Register a signal handler to eject the current page if the 699 * job is cancelled. 700 */ 701 702 Canceled = 0; 703 704 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ 705 sigset(SIGTERM, CancelJob); 706 #elif defined(HAVE_SIGACTION) 707 memset(&action, 0, sizeof(action)); 708 709 sigemptyset(&action.sa_mask); 710 action.sa_handler = CancelJob; 711 sigaction(SIGTERM, &action, NULL); 712 #else 713 signal(SIGTERM, CancelJob); 714 #endif /* HAVE_SIGSET */ 715 716 /* 717 * Initialize the print device... 718 */ 719 720 ppd = ppdOpenFile(getenv("PPD")); 721 if (!ppd) 722 { 723 ppd_status_t status; /* PPD error */ 724 int linenum; /* Line number */ 725 726 _cupsLangPrintFilter(stderr, "ERROR", 727 _("The PPD file could not be opened.")); 728 729 status = ppdLastError(&linenum); 730 731 fprintf(stderr, "DEBUG: %s on line %d.\n", ppdErrorString(status), linenum); 732 733 return (1); 734 } 735 736 Setup(); 737 738 /* 739 * Process pages as needed... 740 */ 741 742 Page = 0; 743 744 while (cupsRasterReadHeader2(ras, &header)) 745 { 746 /* 747 * Write a status message with the page number and number of copies. 748 */ 749 750 if (Canceled) 751 break; 752 753 Page ++; 754 755 fprintf(stderr, "PAGE: %d %d\n", Page, header.NumCopies); 756 _cupsLangPrintFilter(stderr, "INFO", _("Starting page %d."), Page); 757 758 /* 759 * Start the page... 760 */ 761 762 StartPage(ppd, &header); 763 764 /* 765 * Loop for each line on the page... 766 */ 767 768 for (y = 0; y < header.cupsHeight; y ++) 769 { 770 /* 771 * Let the user know how far we have progressed... 772 */ 773 774 if (Canceled) 775 break; 776 777 if ((y & 127) == 0) 778 { 779 _cupsLangPrintFilter(stderr, "INFO", 780 _("Printing page %d, %u%% complete."), 781 Page, 100 * y / header.cupsHeight); 782 fprintf(stderr, "ATTR: job-media-progress=%u\n", 783 100 * y / header.cupsHeight); 784 } 785 786 /* 787 * Read a line of graphics... 788 */ 789 790 if (cupsRasterReadPixels(ras, Planes[0], header.cupsBytesPerLine) < 1) 791 break; 792 793 /* 794 * See if the line is blank; if not, write it to the printer... 795 */ 796 797 if (Planes[0][0] || 798 memcmp(Planes[0], Planes[0] + 1, header.cupsBytesPerLine - 1)) 799 OutputLine(&header); 800 else 801 Feed ++; 802 } 803 804 /* 805 * Eject the page... 806 */ 807 808 _cupsLangPrintFilter(stderr, "INFO", _("Finished page %d."), Page); 809 810 EndPage(); 811 812 if (Canceled) 813 break; 814 } 815 816 /* 817 * Shutdown the printer... 818 */ 819 820 Shutdown(); 821 822 if (ppd) 823 ppdClose(ppd); 824 825 /* 826 * Close the raster stream... 827 */ 828 829 cupsRasterClose(ras); 830 if (fd != 0) 831 close(fd); 832 833 /* 834 * If no pages were printed, send an error message... 835 */ 836 837 if (Page == 0) 838 { 839 _cupsLangPrintFilter(stderr, "ERROR", _("No pages were found.")); 840 return (1); 841 } 842 else 843 return (0); 844 } 845