rtoss - Rev 338

Subversion Repositories:
Rev:
/*
 * bmp2bdf  --  enable to modify glyphs in a bdf font
 * version: 0.2
 * date:    Wed Feb 07 00:32:31 2001
 * author:  ITOU Hiroki (itouh@lycos.ne.jp)
 */


/*
 * Copyright (c) 2001 ITOU Hiroki
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */



#include <stdio.h>  /* printf(), fopen(), fwrite() */
#include <stdlib.h> /* malloc(), EXIT_SUCCESS, strtol(), exit() */
#include <string.h> /* strncmp(), strcpy() */
#include <limits.h> /* strtol() */
#include <sys/stat.h> /* stat() */
#include <ctype.h> /* isspace() */

#define FILENAMECHARMAX 256
#define LINECHARMAX 1024
#define SIZEMAX 8
#define exiterror(msg,arg) {printf("error: " msg, arg); exitrm();}

#ifdef DEBUG
#define d_printf(msg,arg) printf(msg, arg)
#else /* not DEBUG */
#define d_printf(msg,arg)
#endif /* DEBUG */


/* global var */
struct imageinfo{
        long w;
        long h;
        int size;
        int offset; /* bfOffBits: offset to image-data array */
        int depth; /* bitdepth */
        int sp; /* width of spacing */

} img;

struct {
        int w; /* width (pixel) */
        int h; /* height */
        int offx; /* offset y (pixel) */
        int offy; /* offset y */
} font;
int numcol; /*  numberOfColumns */
int dwidth;
int bbx_w, bbx_h, bbx_offx, bbx_offy;
char outfileBdfP[FILENAMECHARMAX];
FILE *outP;


/* func prototype */
int main(int argc, char *argv[]);
char *readBmp(char *infileBmpP);
void readwriteBdf(char *infileBdfP, char *imgP, char *outfileBdfP);
int getline(char* lineP, int max, FILE* inputP);
char *gettoken(char *strP);
void newglyphwrite(FILE *outP, int nglyph, char *imgP);
long mread(const void *varP, int size);
void exitrm(void);
void bmp8bit2img(unsigned char *bmpP, struct imageinfo bmp, char *imgP);
void bmp24bit2img(unsigned char *bmpP, struct imageinfo bmp, char *imgP);
void bmp4bit2img(unsigned char *bmpP, struct imageinfo bmp, char *imgP);

int main(int argc, char *argv[]){
        char *imgP;
        int arg;
        char infileBdfP[FILENAMECHARMAX];
        char infileBmpP[FILENAMECHARMAX];

        /* handling arguments */
        if((argc<4) || (argc>5)){
                printf("bmp2bdf version 0.2\n");
                printf("Usage: bmp2bdf [numberOfColumns] input.bdf input.bmp output.bdf\n");
                exit(EXIT_FAILURE);
        }
        if(argc == 4){
                numcol = 32;
                arg = 0;
        }else{
                numcol = atoi(argv[1]);
                arg = 1;
        }
        if((numcol<1) || (numcol>1024))
                exiterror("numberOfColumns(%d) is over the limit\n", numcol);
        strcpy(infileBdfP, argv[1+arg]);
        strcpy(infileBmpP, argv[2+arg]);
        strcpy(outfileBdfP, argv[3+arg]);

        if(strcmp(infileBdfP, outfileBdfP) == 0)
                exiterror("'%s' appears twice\n", infileBdfP);

        /* read BMP file and change to IMG file */
        imgP = readBmp(infileBmpP);

        /* read and write BDF file*/
        readwriteBdf(infileBdfP, imgP, outfileBdfP);

        return EXIT_SUCCESS;
}


char *readBmp(char *infileBmpP){
        struct stat fileinfo;
        unsigned char *bmpP;
        struct imageinfo bmp;
        int val;
        FILE *inP;
        char *imgP;
        unsigned long ul;

        /*
         * open BMP file
         */

        if(stat(infileBmpP, &fileinfo) != 0)
                exiterror("'%s' does not exist", infileBmpP);
        bmpP = malloc(fileinfo.st_size);
        if(bmpP == NULL)
                exiterror("cannot allocate memory in %s'\n", infileBmpP);

        inP = fopen(infileBmpP, "rb");
        if(inP == NULL)
                exiterror("cannot open '%s'\n", infileBmpP);

        val = fread(bmpP, 1, fileinfo.st_size, inP);
        if(val != fileinfo.st_size)
                exiterror("cannot read '%s'\n", infileBmpP);
        if(memcmp(bmpP, "BM", 2) != 0)
                exiterror("%s' is not a BMP file.\n", infileBmpP);
        fclose(inP);

        /*
         * analyze BMP file
         */

        bmp.offset = mread(bmpP+10, sizeof(long));
        ul = (unsigned long)mread(bmpP+14, sizeof(unsigned long));
        if(ul != 40)
                exiterror("'%s' is an OS/2-format BMP file\n", infileBmpP);
        bmp.w = img.w = mread(bmpP+18, sizeof(long));
        d_printf("BMP width = %ld ", bmp.w);
        bmp.h = img.h = mread(bmpP+22, sizeof(long));
        d_printf("height = %ld\n", bmp.h);
        if(bmp.h < 0)
                exiterror("bmp height(%ld) is minus-number\n", bmp.h);

        bmp.depth = (short)mread(bmpP+28, sizeof(short));
        if(bmp.depth!=4 && bmp.depth!=8 && bmp.depth!=24)
                exiterror("cannot accept %dbit BMP file\n",bmp.depth);
        ul = (unsigned long)mread(bmpP+30, sizeof(unsigned long));
        if(ul != 0L)
                exiterror("'%s' is a compressed BMP file\n", infileBmpP);
        bmp.size = fileinfo.st_size;

        /*
         * malloc IMG
         */

        img.size = img.w * img.h;
        imgP = malloc(img.size); /*  free(imageP) does not exist */
        if(imgP == NULL)
                exiterror("cannot allocate memory imageP %dbytes\n", img.size);

        /*
         * BMP bitdepth
         */

        switch(bmp.depth){
        case 4:
                bmp4bit2img(bmpP, bmp, imgP);
                break;
        case 8:
                bmp8bit2img(bmpP, bmp, imgP);
                break;
        case 24:
                bmp24bit2img(bmpP, bmp, imgP);
                break;
        default:
                exiterror("cannot accept %d bit BMP file\n",bmp.depth);
                break;
        }

        free(bmpP);
        return imgP;
}


void readwriteBdf(char *infileBdfP, char *imgP, char *outfileBdfP){
        FILE *inP;
        enum {INBITMAP, OUTBITMAP};
        struct stat fileinfo;
        char lineP[LINECHARMAX];
        char strP[LINECHARMAX]; /* for not-breaking lineP with '\0' */
        int st; /* status */
        int len, val;
        int nglyph; /* number of glyphs that appeared */
        char *tokenP; /* used for splitting tokens */

        /* file open */
        if(stat(infileBdfP, &fileinfo) != 0)
                exiterror("'%s' does not exist\n", infileBdfP);
        inP = fopen(infileBdfP, "r");
        if(inP == NULL)
                exiterror("cannot open '%s'\n", infileBdfP);

        /*if(stat(outfileBdfP, &fileinfo) == 0){
                printf("error: '%s' already exists\n", outfileBdfP);
                exit(EXIT_FAILURE);
        }*/

        outP = fopen(outfileBdfP, "wb");
        if(outP == NULL)
                exiterror("cannot write '%s'\n", outfileBdfP);

        /* file check */
        memset(lineP, 0x00, LINECHARMAX);
        len = getline(lineP, LINECHARMAX, inP);
        d_printf("len=%d\n", len);
        if((len == 0) || (strncmp(lineP, "STARTFONT", 9) != 0))
                exiterror("'%s' is not a BDF file.\n", infileBdfP);
        val = fwrite(lineP, 1, strlen(lineP), outP);
        if(val != (int)strlen(lineP))
                exiterror("cannot write '%s'\n", outfileBdfP);


        /* read bdf file */
        nglyph = 0;
        st = OUTBITMAP;
        for(;;){
                memset(lineP, 0x00, LINECHARMAX);
                len = getline(lineP, LINECHARMAX, inP);
                if(len == 0)
                        break;

                if(st == INBITMAP){
                        if(strncmp(lineP, "ENDCHAR", 7)==0){
                                st = OUTBITMAP;
                                newglyphwrite(outP, nglyph, imgP);
                                nglyph++;
                        }
                }else{ /*  stat != INBITMAP */
                        switch(lineP[0]){
                        case 'B':
                                if(strncmp(lineP, "BBX", 3)==0){
                                        strcpy(strP, lineP);
                                        tokenP = gettoken(strP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        bbx_w = atoi(tokenP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        bbx_h = atoi(tokenP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        bbx_offx = atoi(tokenP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        bbx_offy = atoi(tokenP);
                                } else if(strncmp(lineP, "BITMAP", 6)==0)
                                        st = INBITMAP;
                                break; /* lines of BBX, BITMAP is written later */
                        case 'D':
                                if(strncmp(lineP, "DWIDTH ", 7)==0){
                                        strcpy(strP, lineP);
                                        tokenP = gettoken(strP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        dwidth = atoi(tokenP);
                                }
                                break;
                        case 'F':
                                if(strncmp(lineP, "FONTBOUNDINGBOX ", 16)==0){
                                        strcpy(strP, lineP);
                                        tokenP = gettoken(strP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        font.w = atoi(tokenP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        font.h = atoi(tokenP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        font.offx = atoi(tokenP);
                                        tokenP = gettoken(tokenP + (int)strlen(tokenP) + 1);
                                        font.offy = atoi(tokenP);

                                        if( (img.w-numcol*font.w) % (numcol+1) != 0){
                                                d_printf("numcol = %d ", numcol);
                                                d_printf("numcol*font.h = %d\n", numcol*font.h);

                                                exiterror("NumberOfColumns(%d) and the BMP file do not match.\n", numcol);
                                        }
                                        img.sp = (img.w - numcol*font.w) / (numcol+1);
                                        d_printf("font.w=%d ", font.w);
                                        d_printf("font.h=%d ", font.h);
                                        d_printf("font.offx=%d ", font.offx);
                                        d_printf("font.offy=%d\n", font.offy);
                                }
                                /*  go next */
                        default:
                                val = fwrite(lineP, 1, strlen(lineP), outP);
                                if(val != (int)strlen(lineP))
                                        exiterror("cannot write '%s'\n", outfileBdfP);
                        } /* switch */
                } /* if(stat=.. */
        } /* for(;;) */

        fclose(inP);
        fclose(outP);
        d_printf("total glyphs = %d\n", nglyph);
}


/*
 * read oneline from textfile
 *    fgets returns strings included '\n'
 */

int getline(char *lineP, int max, FILE* inputP){
        char *p;

        if(fgets(lineP, max, inputP) == NULL){
                return 0;
        }else{
                /* change CR+LF to LF */
                for(p=lineP ; *p!='\0'; p++){
                        if(*p == '\r'){
                                *p = '\n';
                                *(p+1) = '\0';
                        }
                }
                return strlen(lineP);
        }
}


char *gettoken(char *strP){
        enum {BEFORE, AFTER};
        char *retP = strP;
        int st; /* status */

        st = BEFORE;
        while(*strP != '\0'){
                if((st==BEFORE) && (isspace(*strP)==0)){
                        /* before first char of token, not space */
                        retP = strP;
                        st = AFTER;
                }else if((st==AFTER) && (isspace(*strP)!=0)){
                        /* after first char of token, space */
                        *strP = '\0';
                        return retP;
                }
                strP++;
        }

        /* This may read over a given string. dangerous */
        return retP;
}


/*
 *  read glyphimage from IMG
 *     and change to BDF hexadecimal
 */

void newglyphwrite(FILE *outP, int nglyph, char *imgP){
        char *boxP;
        int i,j,k, x,y, val;
        int width, delta; /* width of a glyph (abs.) */
        int widthaligned; /* width of a glyph (byte-aligned) */
        char *bitstrP; /* BITMAP strings */
        char strP[LINECHARMAX]; /* tmp buffer */
        static char *hex2binP[]= {
                "0000","0001","0010","0011","0100","0101","0110","0111",
                "1000","1001","1010","1011","1100","1101","1110","1111"
        };

        /*
         * allocate 'box' area for a glyph image
         */

        //widthaligned = (font.w+7) / 8 * 8;
        delta = bbx_w-bbx_offx-font.offx;
        width = delta>dwidth ? delta : dwidth-(font.offx < 0 ? font.offx : 0);
        if(!width) width = 1; /* we can't have 0 width */
        widthaligned = (width+7) / 8 * 8;
        boxP = malloc(widthaligned*font.h);
        if(boxP == NULL)
                exiterror("cannot allocate memory newglyphwrite %dbytes\n", widthaligned*font.h);
        memset(boxP, '0', widthaligned*font.h);

        /*
         * read from IMG
         */

        /*   one glyph height  row */
        y = (font.h+img.sp) * (nglyph/numcol) + img.sp;
        /*   one glyph width   column */
        x = (font.w+img.sp) * (nglyph%numcol) + img.sp;

        for(i=0; i<font.h; i++)
                for(j=0; j<width; j++)
                        *(boxP + i*widthaligned + j) = *(imgP + (y+i)*img.w + x+j);

        /*
         * change to hexadecimal
         */

        bitstrP = malloc(LINECHARMAX * font.h);
        if(bitstrP == NULL)
                exiterror("cannot allocate memory: bitstrP %dbytes\n",LINECHARMAX*font.h);
        memset(bitstrP, 0x00, LINECHARMAX*font.h);

        /* write BBX , BITMAP to tmpVariable */
        sprintf(bitstrP, "DWIDTH %d 0\nBBX %d %d %d %d\nBITMAP\n",
                width, width, font.h, 0, font.offy);

        /* write bitmapData to tmpVariable */
        for(i=0; i<font.h; i++){
                for(j=0; j<widthaligned; j+=4){
                        for(k=0; k<16; k++){
                                if(strncmp(boxP+i*widthaligned+j, hex2binP[k],4)==0)
                                        sprintf(strP, "%x", k);
                        }
                        strcat(bitstrP, strP);
                }
                strcat(bitstrP, "\n");
        }
        strcat(bitstrP, "ENDCHAR\n");

        /* write tmpVariable to disk */
        val = fwrite(bitstrP, 1, strlen(bitstrP), outP);
        if(val != (int)strlen(bitstrP))
                exiterror("cannot write a file: bitstrP %dbytes", (int)strlen(bitstrP));

        free(boxP);
        free(bitstrP);
        return;
}


/*
 * read memory
 */

long mread(const void *varP, int size){
        const unsigned char *ucharP = varP;
        char strP[LINECHARMAX];
        char tmpP[SIZEMAX];
        int i;

        strcpy(strP, "0x");

        /* Numbers in BMP file are LSB-ordered. */
        for(i=size-1; i>=0; i--){
                sprintf(tmpP, "%02x", *(ucharP+i));
                strcat(strP, tmpP);
        }


        d_printf("'%s' ", strP);
        return strtol(strP,(char **)NULL, 16);
}


void exitrm(void){
        struct stat fileinfo;
        int val;

        fclose(outP);
        if(stat(outfileBdfP, &fileinfo) == 0){
                val = remove(outfileBdfP);
                if(val != 0)
                        printf("error: cannot remove '%s'\n", outfileBdfP);
        }
        exit(EXIT_FAILURE);
}


void bmp8bit2img(unsigned char *bmpP, struct imageinfo bmp, char *imgP){
        int idx;
        int nclut; /* number of CLUT(color lookup table) */
        int notblack[256]; /* index number of palette */
        long byteline; /* number of byte in BMP oneline */
        int i, j, k;
        char c;
        unsigned char *b;
        unsigned char buf;

        /* check CLUT; black(RGB=0,0,0) or not */
        /*   '54' means CLUT address */
        nclut = 0;
        for(b=bmpP+54,idx=0; b<bmpP+bmp.offset; b+=4,idx++){
                if(*b==0 && *(b+1)==0 && *(b+2)==0)
                        ; /* black; do nothing */
                else{
                        /* not black */
                        if(nclut >= 256)
                                exiterror("number of CLUT(%d) is too many\n", nclut);
                        notblack[nclut] = idx;
                        d_printf("%02x ", idx);
                        nclut++;
                }
        }
        d_printf("  Not-black CLUT: %d\n", nclut);

        /*
         * change BMP format to IMG format
         *   IMG format(this-src-only format):
         *               remove BMP-header,padding
         *               reverse down-top to top-down
         *               black pixels become '1'
         *               white and gray and violet pixels become '0'
         */

        byteline = (bmp.w + 3) / 4 * 4;

        for(i=0; i<img.h; i++){
                for(j=0; j<img.w; j++){
                        buf = *(bmpP + bmp.offset + (bmp.h-1-i)*byteline + j);

                        /* black? or not? */
                        c = '1';
                        for(k=0; k<nclut; k++)
                                if(buf == notblack[k])
                                        c = '0'; /* not black */

                        *(imgP + i*img.w + j) = c;
                }
        }
        return;
}

void bmp24bit2img(unsigned char *bmpP, struct imageinfo bmp, char *imgP){
        unsigned char *bufP;
        char c;
        int i,j;

        long byteline = (bmp.w * 3 + 3) / 4 * 4;

        for(i=0; i<img.h; i++){
                for(j=0; j<img.w; j++){
                        c = '0';
                        bufP = bmpP + bmp.offset + (bmp.h-1-i)*byteline + j*3;

                        /* black? or not? */
                        if(*bufP==0 && *(bufP+1)==0 && *(bufP+2)==0)
                                c = '1'; /* black */

                        *(imgP + i*img.w + j) = c;
                }
        }
        return;
}

void bmp4bit2img(unsigned char *bmpP, struct imageinfo bmp, char *imgP){
        char c;
        unsigned char buf;
        unsigned char *b;
        unsigned char *bufP;
        int notblack[16];
        int idx, nclut;
        int i, j, k;
        long byteline;

        /*
         * read CLUT
         */

        nclut = 0;
        for(b=bmpP+54,idx=0; b<bmpP+bmp.offset; b+=4,idx++){
                if(*b==0 && *(b+1)==0 && *(b+2)==0)
                        ;/* black */
                else{
                        /* not black */
                        if(nclut >= 16)
                                exiterror("number of CLUT(%d) is too many\n", nclut);
                        notblack[nclut] = idx;
                        d_printf("%02x ", idx);
                        nclut++;
                }
        }
        d_printf("  Not-black CLUT: %d\n", nclut);

        /*
         * change BMP to IMG
         */

        byteline = ((bmp.w/2)+(bmp.w%2!=0)+3) / 4 * 4;

        for(i=0; i<img.h; i++){
                for(j=0; j<img.w; j++){
                        bufP = bmpP + bmp.offset + (bmp.h-1-i)*byteline + j/2;
                        if(j%2==0)
                                /* left half */
                                buf = ((*bufP)>>4) & 0x0f;
                        else
                                /* right half */
                                buf = (*bufP) & 0x0f;

                        c = '1';
                        for(k=0; k<nclut; k++)
                                if(buf == notblack[k])
                                        c = '0'; /* not black */
                        *(imgP + i*img.w + j) = c;
                }
        }
        return;
}