#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#define MAX_COLORS 8
#include "pixma_parse.h"
static int nextcmd( FILE *infile,unsigned char* cmd,unsigned char *buf, unsigned int *cnt)
{
unsigned char c1,c2;
if (feof(infile))
return -1;
while (!feof(infile)){
c1 = fgetc(infile);
if(feof(infile))
return 1;
if (c1 == 27 ){
c2 = fgetc(infile);
if(feof(infile))
return 1;
if (c2=='[' || c2=='(' ){
*cmd = fgetc(infile);
if(feof(infile))
return 1;
c1 = fgetc(infile);
c2 = fgetc(infile);
*cnt = c1 + (c2<<8);
if (*cnt){
unsigned int read;
if((read=fread(buf,1,*cnt,infile)) != *cnt){
printf("nextcmd: read error - not enough data %d %d\n", read, *cnt);
return -1;
}
}
return 0;
}else if(c2 == '@'){
printf("ESC @ Return to neutral mode\n");
} else {
printf("unknown byte following ESC %x \n",c2);
}
}else if(c1==0x0c){
printf("-->Form Feed\n");
}else{
printf("UNKNOWN BYTE 0x%x @ %lu\n",c1,ftell(infile));
}
}
return -1;
}
static color_t* get_color(image_t* img,char name){
int i;
for(i=0;i<MAX_COLORS;i++)
if(img->color[i].name==name)
return &(img->color[i]);
return NULL;
}
static int valid_color(unsigned char color){
int i;
for(i=0;i<sizeof(valid_colors) / sizeof(valid_colors[0]);i++)
if(valid_colors[i] == color)
return 1;
printf("unknown color %c 0x%x\n",color,color);
return 0;
}
static int eight2ten(unsigned char* inbuffer,unsigned char* outbuffer,int num_bytes,int outbuffer_size){
PutBitContext s;
int read_pos=0;
init_put_bits(&s, outbuffer,outbuffer_size);
while(read_pos < num_bytes){
unsigned short value=Table8[inbuffer[read_pos]];
++read_pos;
put_bits(&s,10,value);
}
return s.buf_ptr-s.buf;
}
static int Raster(image_t* img,unsigned char* buffer,unsigned int len,unsigned char color_name){
color_t* color=get_color(img,color_name);
char* buf = (char*)buffer;
int size=0;
int cur_line=0;
unsigned char* dst=malloc(len*256);
unsigned char* dstr=dst;
if(!color){
printf("no matching color for %c (0x%x) in the database => ignoring %i bytes\n",color_name,color_name,len);
}
while( len > 0){
int c = *buf;
++buf;
--len;
if(c >= 128)
c -=256;
if(c== -128){
if(color && size){
if(!color->tail)
color->head = color->tail = color->pos = calloc(1,sizeof(rasterline_t));
else {
color->head->next = calloc(1,sizeof(rasterline_t));
color->head=color->head->next;
}
color->head->line = img->height + cur_line;
if(!color->compression){
color->head->buf=calloc(1,size+8);
memcpy(color->head->buf,dstr,size);
color->head->len=size;
}else{
color->head->buf=calloc(1,size*2+8);
size=color->head->len=eight2ten(dstr,color->head->buf,size,size*2);
}
}
if(color && img->width < size*8/color->bpp)
img->width = size *8/color->bpp;
size=0;
dst=dstr;
++cur_line;
}else {
int i;
if(c < 0){
i= - c + 1;
c=*buf;
++buf;
--len;
memset(dst,c,i);
dst +=i;
size+=i;
}else{
i=c+1;
len-=i;
memcpy( dst, buf, i);
buf +=i;
dst +=i;
size+=i;
}
}
}
free(dstr);
return 0;
}
static inline int inside_range(color_t* c,int x,int y){
if(c->bpp && c->pos && c->pos->line==y && c->pos->len && (c->pos->len *8 >= c->bpp*x))
return 1;
return 0;
}
static void advance(image_t* img,unsigned int to){
int i;
for(i=0;i<MAX_COLORS;i++){
while(img->color[i].pos && img->color[i].pos->line < to && img->color[i].pos->next && img->color[i].pos->next->line <= to){
img->color[i].pos = img->color[i].pos->next;
if(!img->color[i].pos->next)
break;
}
}
}
static void write_line(image_t*img,FILE* fp,int pos_y){
int i;
unsigned int x;
unsigned int written;
unsigned char* line=malloc(img->width*3);
color_t* Y=get_color(img,'Y');
color_t* C=get_color(img,'C');
color_t* M=get_color(img,'M');
color_t* K=get_color(img,'K');
color_t* k=get_color(img,'k');
color_t* c=get_color(img,'c');
color_t* m=get_color(img,'m');
color_t* y=get_color(img,'y');
GetBitContext gb[MAX_COLORS];
advance(img,pos_y);
for(i=0;i<MAX_COLORS;i++){
if(inside_range(&(img->color[i]),0,pos_y)){
init_get_bits(&(gb[i]),img->color[i].pos->buf,img->color[i].pos->len);
}
}
for(x=0;x<img->width;x++){
int lK=0,lM=0,lY=0,lC=0;
for(i=0;i<MAX_COLORS;i++){
if(inside_range(&img->color[i],x,pos_y))
img->color[i].value = get_bits(&gb[i],img->color[i].bpp);
else
img->color[i].value = 0;
(img->color[i].dots)[img->color[i].value] += 1;
}
lK=K->density * K->value/(K->level-1) + k->density * k->value/(k->level-1);
lM=M->density * M->value/(M->level-1) + m->density * m->value/(m->level-1);
lY=Y->density * Y->value/(Y->level-1) + y->density * y->value/(y->level-1);
lC=C->density * C->value/(C->level-1) + c->density * c->value/(c->level-1);
if(lK || lM || lY || lC){
if(!img->image_top)
img->image_top = pos_y;
img->image_bottom = pos_y;
if(x < img->image_left)
img->image_left = x;
if(x > img->image_right)
img->image_right = x;
}
if(lK > 255)
lK = 255;
if(lM > 255)
lM = 255;
if(lC > 255)
lC = 255;
if(lY > 255)
lY = 255;
line[x*3]=255 - lC - lK;
line[x*3+1]=255 - lM -lK;
line[x*3+2]=255 - lY -lK;
++img->dots;
}
if((written = fwrite(line,img->width,3,fp)) != 3)
printf("fwrite failed %u vs %u\n",written,img->width*3);
free(line);
}
static void write_ppm(image_t* img,FILE* fp){
int i;
for(i=0;i<MAX_COLORS;i++){
img->color[i].dots=calloc(1,sizeof(int)*(img->color[i].level+1));
}
fputs("P6\n", fp);
fprintf(fp, "%d\n%d\n255\n", img->width, img->height);
img->image_left = img->width;
for(i=0;i<img->height;i++){
write_line(img,fp,i);
}
printf("statistics:\n");
for(i=0;i<MAX_COLORS;i++){
int level;
for(level=0;level < img->color[i].level;level++)
printf("color %c level %i dots %i\n",img->color[i].name,level,img->color[i].dots[level]);
}
img->image_top = img->image_top * 72.0 / img->yres ;
img->image_bottom = img->image_bottom * 72.0 / img->yres ;
img->image_left = img->image_left * 72.0 / img->xres ;
img->image_right = img->image_right * 72.0 / img->xres ;
printf("top %u bottom %u left %u right %u\n",img->image_top,img->image_bottom,img->image_left,img->image_right);
printf("width %u height %u\n",img->image_right - img->image_left,img->image_bottom - img->image_top);
for(i=0;i<MAX_COLORS;i++){
if(img->color[i].dots)
free(img->color[i].dots);
}
}
static unsigned int read_uint32(unsigned char* a){
unsigned int value = ( a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
return value;
}
static int process(FILE* in, FILE* out,int verbose,unsigned int maxh){
image_t* img=calloc(1,sizeof(image_t));
unsigned char* buf=malloc(0xFFFF);
int returnv=0;
int i;
printf("------- parsing the printjob -------\n");
while(!returnv && !feof(in)){
unsigned char cmd;
unsigned int cnt = 0;
if((returnv = nextcmd(in,&cmd,buf,&cnt)))
break;
switch(cmd){
case 'c':
printf("ESC (c set media (len=%i):\n",cnt);
printf(" model id %x bw %x",buf[0]>> 4,buf[0]&0xf);
printf(" media %x",buf[1]);
printf(" direction %x quality %x\n",(buf[2]>>4)&3 ,buf[2]&0xf);
break;
case 'K':
if(buf[1]==0x1f){
printf("ESC [K go to command mode\n");
do{
fgets((char*)buf,0xFFFF,in);
printf(" %s",buf);
}while(strcmp((char*)buf,"BJLEND\n"));
}else if(cnt == 2 && buf[1]==0x0f){
printf("ESC [K reset printer\n");
img->width=0;
img->height=0;
}else
printf("ESC [K unsupported param (len=%i): %x\n",cnt,buf[1]);
break;
case 'b':
printf("ESC (b set data compression (len=%i): %x\n",cnt,buf[0]);
break;
case 'I':
printf("ESC (I select data transmission (len=%i): ",cnt);
if(buf[0]==0)printf("default");
else if(buf[0]==1)printf("multi raster");
else printf("unknown 0x%x",buf[0]);
printf("\n");
break;
case 'l':
printf("ESC (l select paper loading (len=%i):\n",cnt);
printf(" model id 0x%x ",buf[0]>>4);
printf(" source 0x%x",buf[0]&15);
printf(" media: %x",buf[1]);
printf(" paper gap: %x\n",buf[2]);
break;
case 'd':
img->xres = (buf[0]<<8)|buf[1];
img->yres = (buf[2]<<8)|buf[3];
printf("ESC (d set raster resolution (len=%i): %i x %i\n",cnt,img->xres,img->yres);
break;
case 't':
printf("ESC (t set image cnt %i\n",cnt);
if(buf[0]>>7){
char order[]="CMYKcmyk";
int black_found = 0;
int num_colors = (cnt - 3)/3;
printf(" bit_info: using detailed color settings for max %i colors\n",num_colors);
if(buf[1]==0x80)
printf(" format: BJ indexed color image format\n");
else{
printf(" format: settings not supported 0x%x\n",buf[1]);
returnv = -2;
}
if(buf[2] != 0x1){
printf(" ink: settings not supported 0x%x\n",buf[2]);
returnv = -2;
}
for(i=0;i<num_colors;i++){
if(i<MAX_COLORS){
img->color[i].name=order[i];
img->color[i].compression=buf[3+i*3] >> 5;
img->color[i].bpp=buf[3+i*3] & 31;
img->color[i].level=(buf[3+i*3+1] << 8) + buf[3+i*3+2];
if(i<4)
img->color[i].density = 255;
else
img->color[i].density = 128;
if((order[i] == 'K' || order[i] =='k') && img->color[i].bpp)
black_found = 1;
if(order[i] == 'y' && !black_found && img->color[i].level){
printf("iP6700 hack: treating colordefinition at the y position as k\n");
img->color[i].name = 'k';
order[i] = 'k';
order[i+1] = 'y';
black_found = 1;
img->color[i].density = 255;
}
printf(" Color %c Compression: %i bpp %i level %i\n",img->color[i].name,
img->color[i].compression,img->color[i].bpp,img->color[i].level);
}else{
printf(" Color ignoring setting %x %x %x\n",buf[3+i*3],buf[3+i*3+1],buf[3+i*3+2]);
}
}
}else if(buf[0]==0x1 && buf[1]==0x0 && buf[2]==0x1){
printf(" 1bit-per pixel\n");
for(i=0;i<MAX_COLORS;i++){
const char order[]="CMYKcmyk";
img->color[i].name=order[i];
img->color[i].compression=0;
img->color[i].bpp=1;
img->color[i].level=2;
img->color[i].density = 255;
}
}else{
printf(" bit_info: unknown settings 0x%x 0x%x 0x%x\n",buf[0],buf[1],buf[2]);
returnv=-2;
}
break;
case 'L':
printf("ESC (L set component order for F raster command (len=%i): ",cnt);
img->color_order=calloc(1,cnt+1);
for(i=0;i<cnt;i++){
if(!valid_color(buf[i]))
break;
}
cnt = i;
memcpy(img->color_order,buf,cnt);
printf("%s\n",img->color_order);
img->num_colors = cnt;
img->cur_color=0;
break;
case 'p':
printf("ESC (p set extended margin (len=%i):\n",cnt);
printf(" printed length %i left %i\n",((buf[0]<<8 )+buf[1]) *6 / 5,(buf[2]<<8) + buf[3]);
printf(" printed width %i top %i\n",((buf[4]<<8 )+buf[5]) * 6 / 5,(buf[6]<<8) + buf[7]);
if(cnt > 8){
int unit = (buf[12] << 8)| buf[13];
int area_right = read_uint32(buf+14);
int area_top = read_uint32(buf+18);
unsigned int area_width = read_uint32(buf+22);
unsigned int area_length = read_uint32(buf+26);
int paper_right = read_uint32(buf+30);
int paper_top = read_uint32(buf+34);
unsigned int paper_width = read_uint32(buf+38);
unsigned int paper_length = read_uint32(buf+42);
printf(" unknown %i\n",read_uint32(buf+8));
printf(" unit %i [1/in]\n",unit);
printf(" area_right %i %.1f mm\n",area_right,area_right * 25.4 / unit);
printf(" area_top %i %.1f mm\n",area_top,area_top * 25.4 / unit);
printf(" area_width %u %.1f mm\n",area_width, area_width * 25.4 / unit);
printf(" area_length %u %.1f mm\n",area_length,area_length * 25.4 / unit);
printf(" paper_right %i %.1f mm\n",paper_right,paper_right * 25.4 / unit);
printf(" paper_top %i %.1f mm\n",paper_top,paper_top * 25.4 / unit);
printf(" paper_width %u %.1f mm\n",paper_width,paper_width * 25.4 / unit);
printf(" paper_length %u %.1f mm\n",paper_length,paper_length * 25.4 / unit);
img->top = (float)area_top / unit;
img->left = (float)area_top / unit;
}
break;
case '$':
printf("ESC ($ set duplex (len=%i)\n",cnt);
break;
case 'J':
printf("ESC (J select number of raster lines per block (len=%i): %i\n",cnt,buf[0]);
img->lines_per_block=buf[0];
break;
case 'F':
if(verbose)
printf("ESC (F raster block (len=%i):\n",cnt);
if((returnv = Raster(img,buf,cnt,img->color_order[img->cur_color])))
break;
++img->cur_color;
if(img->cur_color >= img->num_colors){
img->cur_color=0;
img->height+=img->lines_per_block;
}
break;
case 'q':
printf("ESC (q set page id (len=%i):%i\n",cnt,buf[0]);
break;
case 'r':
printf("ESC (r printer specific command (len=%i): ",cnt);
for(i=0;i<cnt;i++)
printf("0x%x ",buf[i]);
printf("\n");
break;
case 'A':
if(verbose)
printf("ESC (A raster line (len=%i): color %c\n",cnt,buf[0]);
buf[cnt]=0x80;
returnv = Raster(img,buf+1,cnt,buf[0]);
if (fgetc(in)!=0x0d){
printf("Raster A not terminated by 0x0d\n");
returnv=-4;
}
break;
case 'e':
if(verbose)
printf("ESC (e advance (len=%i): %i\n",cnt,buf[0]*256+buf[1]);
if(img->lines_per_block){
img->height += (buf[0]*256+buf[1])*img->lines_per_block;
img->cur_color=0;
}else
img->height += (buf[0]*256+buf[1]);
break;
default:
printf("ESC (%c UNKNOWN (len=%i)\n",cmd,cnt);
for(i=0;i<cnt;i++)
printf(" 0x%x",buf[i]);
printf("\n");
}
}
printf("-------- finished parsing --------\n");
if(returnv < 0){
printf("error: parsing the printjob failed error %i\n",returnv);
} else {
printf("created bit image with width %i height %i\n",img->width,img->height);
if(maxh > 0){
printf("limiting height to %u\n",maxh);
img->height=maxh;
}
if(out)
write_ppm(img,out);
printf("dots: %u\n",img->dots);
}
free(buf);
for(i=0;i<MAX_COLORS;i++){
rasterline_t** r=&img->color[i].tail;
while(*r){
rasterline_t* tmp=(*r)->next;
free((*r)->buf);
free(*r);
*r=tmp;
}
}
if(img->color_order)
free(img->color_order);
free(img);
return returnv;
}
static void display_usage(void){
printf("usage: pixma_parse [options] infile [outfile]\n");
printf("infile: the printjob to parse\n");
printf("outfile: if specified a ppm file will be generated from the raster data\n");
printf("options:\n");
printf(" -v: verbose print ESC e),F) and A) commands\n");
printf(" -y height: cut the output ppm to the given height\n");
printf(" -h: display this help\n");
}
int main(int argc,char* argv[]){
int verbose = 0;
unsigned int maxh=0;
char* filename_in=NULL,*filename_out=NULL;
FILE *in,*out=NULL;
int i;
printf("pixma_parse - parser for Canon BJL printjobs (c) 2005-2007 Sascha Sommer <saschasommer@freenet.de>\n");
for(i=1;i<argc;i++){
if(strlen(argv[i]) >= 2 && argv[i][0] == '-'){
if(argv[i][1] == 'v'){
verbose = 1;
}else if(argv[i][1] == 'h'){
display_usage();
return 0;
}else if(argv[i][1] == 'y'){
if(argc > i+1){
++i;
maxh = atoi(argv[i]);
}else{
display_usage();
return 1;
}
}else {
printf("unknown parameter %s\n",argv[i]);
return 1;
}
}else if(!filename_in){
filename_in = argv[i];
}else if(!filename_out){
filename_out = argv[i];
}else{
display_usage();
return 1;
}
}
if(!filename_in){
display_usage();
return 1;
}
if(!(in=fopen(filename_in,"rb"))){
printf("unable to open input file %s\n",filename_in);
return 1;
}
if(filename_out && !(out=fopen(filename_out,"wb"))){
printf("can't create the output file %s\n",filename_out);
fclose(in);
return 1;
}
process(in,out,verbose,maxh);
fclose(in);
if(out)
fclose(out);
return 0;
}