📄 pclgen.c
字号:
/****************************************************************************** File: $Id: pclgen.c,v 1.21 2001/04/29 10:37:08 Martin Rel $ Contents: PCL-generating routines Author: Martin Lottermoser, Greifswaldstrasse 28, 38124 Braunschweig, Germany. E-mail: Martin.Lottermoser@t-online.de.******************************************************************************** ** Copyright (C) 1999, 2000, 2001 by Martin Lottermoser ** All rights reserved ** ******************************************************************************** In the implementation of these and other functions I have mainly used the following documents: - Hewlett-Packard "Technical Reference Guide for the HP DeskJet 500 Series Printers" First edition, October 1994 Manual Part Number: C2170-90099 (Quoted as "TRG500") - Hewlett-Packard "Hewlett-Packard 300 and 400 Series DeskJet Printers - Software Developer's Guide" January 1996 (Quoted as "DJ3/4") - Hewlett-Packard "HP DeskJet 600/800 Series Printers - Software Developer's PCL Guide" Fifth edition, October 1997 (Quoted as "DJ6/8") - Hewlett-Packard "DeskJet 1120C Printer - Software Developer's PCL Guide" First printing, December 1997. Version 1.0. (Quoted as "DJ1120C") - Hewlett-Packard "Printer Job Language Technical Reference Manual" Edition 10, October 1997. HP Part No. 5021-0380. (Quoted as "PJLTRM") - Lexmark "Printer Technical Reference, Version 1.1" First edition, February 1999 (Quoted as "Lexmark-PTR". It deals with PCL 5 and PCL 6.) In addition, some other documents are quoted in a form like "BPD02926". These were obtained from http://www.hp.com, usually from the directory cposupport/printers/support_doc. BPD02926, for example, (a short description of PCL commands for series-800 DeskJets) could be found as the file bpd02926.html in that directory.******************************************************************************//* Configuration management identification */#ifndef lintstatic const char cm_id[] = "@(#)$Id: pclgen.c,v 1.21 2001/04/29 10:37:08 Martin Rel $";#endif/*****************************************************************************/#ifndef _XOPEN_SOURCE#define _XOPEN_SOURCE 500#endif/* Standard headers */#include <assert.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <string.h>/* Special headers */#include "pclgen.h"/*****************************************************************************//* Prefix for error messages */#define ERRPREF "? pclgen: "/* The usual array-size macro */#define array_size(a) (sizeof(a)/sizeof(a[0]))/* Macro to check whether an octet is an ASCII letter. Note that we can't use the isalpha() function because we might be operating in an internationalized environment. */#define is_letter(c) (0x41 <= (c) && (c) <= 0x5A || 0x61 <= (c) && (c) <= 0x7A)/* Same for digits */#define is_digit(c) (0x30 <= (c) && (c) <= 0x39)/****************************************************************************** Function: pcl3_levels_to_planes This function returns ceiling(ld(levels)), the number of bitplanes needed to store the specified number of intensity levels, or 0 if 'levels' is zero. 'levels' must be <= (ULONG_MAX+1)/2.******************************************************************************/unsigned int pcl3_levels_to_planes(unsigned int levels){ unsigned long power = 1; unsigned int planes = 0; /* power == 2^planes */ while (power < levels) { power *= 2; /* multiplication is faster than division */ planes++; } /* levels == 0 or 2^(planes-1) < levels <= 2^planes */ return planes;}/****************************************************************************** Function: send_ERG This function sends an "End Raster Graphics" command to 'out', choosing its form according to 'level'.******************************************************************************/static void send_ERG(FILE *out, pcl_Level level){ /* PCL: End Raster Graphics/End Raster */ fputs("\033*r", out); if (pcl_use_oldERG(level)) fputc('B', out); else if (level == pcl_level_3plus_ERG_both) fputs("bC", out); else fputc('C', out); return;}/****************************************************************************** Function: pcl3_init_file This function must be called to initialize the printer before the first page. The 'data' should remain constant within a page. Whenever one changes 'data' between pages, this function should be called again. The function follows the return code rules described in pclgen.h. It will perform a number of validity checks on 'data'. No checks will be performed on fields like 'print_quality' which can be passed to the printer without having to be interpreted by this component. The routine will configure the printer such that the top left corner of the logical page is the top left corner of the printable area in raster space. The file 'out' should be a binary file for this and all other output functions.******************************************************************************/int pcl3_init_file(FILE *out, pcl_FileData *data){ pcl_bool needs_CRD = (data->level == pcl_level_3plus_CRD_only); /* Do we need Configure Raster Data? */ int j; const pcl_ColorantState *colorant = NULL; unsigned int maxhres = 0, maxvres = 0; /* maximal resolutions in ppi */ /* Check validity of the arguments */ { pcl_bool invalid; invalid = (out == NULL || data == NULL); if (invalid) fputs(ERRPREF "Null pointer passed to pcl3_init_file().\n", stderr); else { /* Palette und colorants */ switch(data->palette) { case pcl_no_palette: /*FALLTHROUGH*/ case pcl_black: invalid = data->number_of_colorants != 1; break; case pcl_CMY: invalid = data->number_of_colorants != 3; break; case pcl_RGB: invalid = data->number_of_colorants != 3; break; case pcl_CMYK: invalid = data->number_of_colorants != 4; break; default: invalid = data->number_of_colorants <= 0; } if (invalid) fputs(ERRPREF "Palette specification and number of colorants are inconsistent.\n", stderr); else { if (data->colorant == NULL) colorant = data->colorant_array; else colorant = data->colorant; /* First pass over colorants: find minimal and maximal resolutions, check the number of intensity levels */ data->minvres = colorant[0].vres; for (j = 0; j < data->number_of_colorants; j++) { if (colorant[j].hres <= 0 || colorant[j].vres <= 0) { invalid = TRUE; fprintf(stderr, ERRPREF "The resolution for colorant %d is not positive: %u x %u ppi.\n", j, colorant[j].hres, colorant[j].vres); } else { if (colorant[j].vres < data->minvres) data->minvres = colorant[j].vres; if (colorant[j].hres > maxhres) maxhres = colorant[j].hres; if (colorant[j].vres > maxvres) maxvres = colorant[j].vres; } if (colorant[j].levels < 2 || 0xFFFF < colorant[j].levels) { invalid = TRUE; fprintf(stderr, ERRPREF "The number of intensity levels for " "colorant %d is %u instead of at least 2 and at most 65535.\n", j, colorant[j].levels); /* Actually, DJ6/8 p. 68 requires the levels to be in the range 2-255, but as there are two octets available in the CRD format this seems unnecessarily strict. */ } } /* Check for relations between resolutions and the need for CRD */ if (!invalid) /* this implies that all resolutions are positive */ for (j = 0; j < data->number_of_colorants; j++) { /* If there is more than one resolution or more than 2 levels we need CRD. Note that we compare with 'maxhres' in both lines: */ if (maxhres != colorant[j].hres || maxhres != colorant[j].vres || colorant[j].levels > 2) needs_CRD = TRUE; /* DJ6/8 states that the highest horizontal resolution must be a multiple of all lower horizontal resolutions and the same for the set of vertical resolutions. The reason is not given but these properties imply that it is possible to operate uniformly on a grid at the highest resolution. In contrast, the way in which raster data are transferred (strips with a height equal to the reciprocal of the lowest vertical resolution) logically requires that all vertical resolutions are multiples of the lowest vertical resolution. This condition is not stated in DJ6/8 but it is satisfied by all examples given in that document. This includes example 4 on page 72 which is meant to illustrate the maximum flexibility of format 2 and which describes a situation where the corresponding condition does not hold for the horizontal resolutions. */ if (colorant[j].vres % data->minvres != 0) { invalid = TRUE; fprintf(stderr, ERRPREF "The vertical resolution for colorant %d (%u ppi) is not a " "multiple of the lowest vertical resolution (%u ppi).\n", j, colorant[j].vres, data->minvres); } if (maxhres % colorant[j].hres != 0) { invalid = TRUE; fprintf(stderr, ERRPREF "The highest horizontal resolution (%u ppi) is not a multiple " "of the horizontal resolution for colorant %d (%u ppi).\n", maxhres, j, colorant[j].hres); } if (maxvres % colorant[j].vres != 0) { invalid = TRUE; fprintf(stderr, ERRPREF "The highest vertical resolution (%u ppi) is not a multiple " "of the vertical resolution for colorant %d (%u ppi).\n", maxvres, j, colorant[j].vres); } } } if (needs_CRD && data->palette == pcl_RGB) { invalid = TRUE; if (data->level == pcl_level_3plus_CRD_only) fputs(ERRPREF "You can't use an RGB palette at the requested PCL level.\n", stderr); else fputs(ERRPREF "The specified structure of resolutions and intensity " "levels is not possible with an RGB palette.\n", stderr); } if (needs_CRD && !pcl_has_CRD(data->level)) { invalid = TRUE; fputs(ERRPREF "The specified structure of resolutions and intensity " "levels is not possible at the requested PCL level.\n", stderr); } if (data->palette == pcl_any_palette) { needs_CRD = TRUE; if (!pcl_has_CRD(data->level)) { invalid = TRUE; fputs(ERRPREF "The specified palette is not possible at the " "requested PCL level.\n", stderr); } } if (needs_CRD && (maxhres > 0xFFFF || maxvres > 0xFFFF)) { fputs(ERRPREF "Resolutions may be at most 65535 ppi when more than one " "resolution or more than two intensity levels are requested.\n", stderr); invalid = TRUE; } if (data->order_CMYK && data->palette != pcl_CMYK) { fputs(ERRPREF "Ordering bit planes as CMYK instead of KCMY is only meaningful\n" " for a CMYK palette.\n", stderr); invalid = TRUE; } /* Check PJL job name */ if (data->PJL_job != NULL) { const unsigned char *s = (const unsigned char *)data->PJL_job; /* Permissible characters are HT and the octets 32-255 with the exception of '"' (PJLTRM, with some corrections). */ while (*s != '\0' && (*s == '\t' || 32 <= *s && *s != '"')) s++; if (*s != '\0') { fprintf(stderr, ERRPREF "Illegal character in PJL job name (code 0x%02X).\n", *s); invalid = TRUE; } /* There is a maximum of 80 "significant characters" (PJLTRM). According to PJLTRM, p. D-8, "String too long" is a parser warning and leads to a part of the command being ignored. I play it safe. There would also be a warning for an empty string but we treat that case differently anyway (see below). */ if (strlen(data->PJL_job) > 80) { fputs(ERRPREF "PJL job name is too long (more than 80 characters).\n", stderr); invalid = TRUE; } } /* Check PJL personality name */ if (data->PJL_language != NULL) { const char *s = data->PJL_language; /* PJLTRM does not give explicit lexical conventions for personality names but from general considerations it should be an "alphanumeric variable". The latter must start with a letter and may consist of letters and digits. */ if (is_letter(*s)) do s++; while (is_letter(*s) || is_digit(*s)); if (*data->PJL_language == '\0') { fputs(ERRPREF "Empty PJL language name.\n", stderr); invalid = TRUE; } else if (*s != '\0') { fprintf(stderr, ERRPREF "Illegal character in PJL language name (code 0x%02X).\n", *s); invalid = TRUE; } } } if (invalid) return +1; } /* Practically every output file from an HP driver I have seen starts with 600 and one even with 9600 NUL characters. This seems unnecessary and is undocumented, but just in case that there are situations where it might make a difference, this module provides the capability. */ for (j = 0; j < data->NULs_to_send; j++) fputc('\0', out); /* Issue PJL commands if requested. Most newer HP drivers for PCL-3 printers follow the NULs with a PJL statement to enter the PCL language interpreter. The language name varies (so far I've seen PCL3GUI and PCLSLEEK). Some drivers also generate JOB/EOJ statements. Interestingly enough, I have seen output from two HP drivers which, at the beginning of the job, generated first Printer Reset and then UEL. This is wrong (PJLTRM). */ if (data->PJL_job != NULL || data->PJL_language != NULL) { fputs("\033%-12345X", out); /* Universal Exit Language (UEL) */ /* Start of job */ if (data->PJL_job != NULL) { fputs("@PJL JOB", out); if (*data->PJL_job != '\0') fprintf(out, " NAME=\"%s\"", data->PJL_job); fputc('\n', out); } /* Switch personality */ if (data->PJL_language != NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -