/* Textorizer2.c: writes a picture with text * see: http://www.w3.org/People/maxf/textorizer/ * Copyright Max Froumentin 2006 * This software is distributed under the * W3C® SOFTWARE NOTICE AND LICENSE: * http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 */ #include #include #include #include #include #include #include char *InFileName="in.png",*OutFileName="out.svg",*TextFileName="text.txt"; #define MAX_TEXT_LENGTH 1000000 char Text[MAX_TEXT_LENGTH]; struct pixel { unsigned char r,g,b,a; }; long Width, Height; long svgWidth, svgHeight; struct pixel *Pixels; double FontSize=10; double saturationAdjust=0.2; /* we need a table of character widths. Ugly, but it does'nt seen we can * do better than that in SVG for this purpose. TODO use the sera SVG font * and parse the file to retrieve * each character's widths */ double CharWidths[256]; void setCharWidths(void) { CharWidths[' ']=651/2048.0; CharWidths['!']=821/2048.0; CharWidths['"']=942/2048.0; CharWidths['#']=1716/2048.0; CharWidths['$']=1303/2048.0; CharWidths['%']=1946/2048.0; CharWidths['&']=1597/2048.0; CharWidths[44]=563/2048.0; /* ' */ CharWidths['(']=799/2048.0; CharWidths[')']=799/2048.0; CharWidths['*']=1024/2048.0; CharWidths['+']=1716/2048.0; CharWidths[',']=651/2048.0; CharWidths['-']=739/2048.0; CharWidths['.']=651/2048.0; CharWidths['/']=690/2048.0; CharWidths['0']=1303/2048.0; CharWidths['1']=1303/2048.0; CharWidths['2']=1303/2048.0; CharWidths['3']=1303/2048.0; CharWidths['4']=1303/2048.0; CharWidths['5']=1303/2048.0; CharWidths['6']=1303/2048.0; CharWidths['7']=1303/2048.0; CharWidths['8']=1303/2048.0; CharWidths['9']=1303/2048.0; CharWidths[':']=690/2048.0; CharWidths[';']=690/2048.0; CharWidths['<']=1716/2048.0; CharWidths['=']=1716/2048.0; CharWidths['>']=1716/2048.0; CharWidths['?']=1087/2048.0; CharWidths['@']=2048/2048.0; CharWidths['A']=1401/2048.0; CharWidths['B']=1405/2048.0; CharWidths['C']=1430/2048.0; CharWidths['D']=1577/2048.0; CharWidths['E']=1294/2048.0; CharWidths['F']=1178/2048.0; CharWidths['G']=1587/2048.0; CharWidths['H']=1540/2048.0; CharWidths['I']=604/2048.0; CharWidths['J']=604/2048.0; CharWidths['K']=1343/2048.0; CharWidths['L']=1141/2048.0; CharWidths['M']=1767/2048.0; CharWidths['N']=1532/2048.0; CharWidths['O']=1612/2048.0; CharWidths['P']=1235/2048.0; CharWidths['Q']=1612/2048.0; CharWidths['R']=1423/2048.0; CharWidths['S']=1300/2048.0; CharWidths['T']=1251/2048.0; CharWidths['U']=1499/2048.0; CharWidths['V']=1401/2048.0; CharWidths['W']=2025/2048.0; CharWidths['X']=1403/2048.0; CharWidths['Y']=1251/2048.0; CharWidths['Z']=1403/2048.0; CharWidths['[']=799/2048.0; CharWidths[92]=690/2048.0; /* '\' */ CharWidths[']']=799/2048.0; CharWidths['^']=1716/2048.0; CharWidths['_']=1024/2048.0; CharWidths['`']=1024/2048.0; CharWidths['a']=1255/2048.0; CharWidths['b']=1300/2048.0; CharWidths['c']=1126/2048.0; CharWidths['d']=1300/2048.0; CharWidths['e']=1260/2048.0; CharWidths['f']=721/2048.0; CharWidths['g']=1300/2048.0; CharWidths['h']=1298/2048.0; CharWidths['i']=569/2048.0; CharWidths['j']=569/2048.0; CharWidths['k']=1186/2048.0; CharWidths['l']=569/2048.0; CharWidths['m']=1995/2048.0; CharWidths['n']=1298/2048.0; CharWidths['o']=1253/2048.0; CharWidths['p']=1300/2048.0; CharWidths['q']=1300/2048.0; CharWidths['r']=842/2048.0; CharWidths['s']=1067/2048.0; CharWidths['t']=803/2048.0; CharWidths['u']=1298/2048.0; CharWidths['v']=1212/2048.0; CharWidths['w']=1675/2048.0; CharWidths['x']=1212/2048.0; CharWidths['y']=1212/2048.0; CharWidths['z']=1075/2048.0; CharWidths['{']=1303/2048.0; CharWidths['|']=690/2048.0; CharWidths['}']=1303/2048.0; CharWidths['~']=1716/2048.0; } #ifndef png_jmpbuf # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) #endif #define PNG_BYTES_TO_CHECK 8 int check_if_png(char *file_name, FILE **fp) { unsigned char buf[PNG_BYTES_TO_CHECK]; /* Open the prospective PNG file. */ if ((*fp = fopen(file_name, "rb")) == NULL) return 0; /* Read in some of the signature bytes */ if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) return 0; /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature. Return nonzero (true) if they match */ /* extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); */ return(!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK)); } int loadpng(FILE *fp) { unsigned long i,j,k; png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height; png_bytep *row_pointers; int bit_depth, color_type; int interlace_type, compression_type, filter_type; /* create the png data structures */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return 0; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return 0; } png_init_io(png_ptr, fp); /* read file information */ /* If we have already read some of the signature */ png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); printf("* reading PNG image ("); printf("%ldx%ld, %d bit, ",width,height,bit_depth); switch (color_type) { case PNG_COLOR_TYPE_GRAY: printf("grey levels, "); break; case PNG_COLOR_TYPE_PALETTE: printf("palette, "); break; case PNG_COLOR_TYPE_RGB: printf("RGB, "); break; case PNG_COLOR_TYPE_RGB_ALPHA: printf("RGBA, "); break; case PNG_COLOR_TYPE_GRAY_ALPHA: printf("grey levels + alpha, "); break; } switch (interlace_type) { case PNG_INTERLACE_NONE: printf("not interlaced, "); break; case PNG_INTERLACE_ADAM7: printf("ADAM7-interlaced, "); break; } switch (compression_type) { case PNG_COMPRESSION_TYPE_BASE: printf(" compressed, "); break; } switch (filter_type) { case PNG_FILTER_TYPE_BASE: printf(" filtered, "); } printf(")\n"); /* tell libpng to strip 16 bit/color files down to 8 bits/color */ png_set_strip_16(png_ptr); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ png_set_packing(png_ptr); /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); /* Expand paletted colors into true RGB triplets */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); /* Expand paletted or RGB images with transparency to full alpha channels * so the data will be available as RGBA quartets. */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); /* Add filler (or alpha) byte (before/after each RGB triplet) */ /* if (bit_depth == 8 && */ /* (color_type == PNG_COLOR_TYPE_RGB||color_type == PNG_COLOR_TYPE_GRAY)) */ png_set_filler(png_ptr, 0x000000ff, PNG_FILLER_AFTER); /* update info_ptr from the transforms. used to get a correct value for * png_get_rowbytes */ png_read_update_info(png_ptr, info_ptr); /* allocate the memory to hold a row of the image */ row_pointers = (png_bytep *)malloc(height*sizeof(png_bytep)); for (i = 0; i < height; i++) row_pointers[i] = malloc(png_get_rowbytes(png_ptr, info_ptr)); png_read_image(png_ptr, row_pointers); /* malloc pixmap data */ Width = width; Height = height; assert(Pixels = (struct pixel *)malloc((size_t)Width* (size_t)Height* sizeof(struct pixel))); k=0; for (i=0;i=Width) x=Width-1; if (y<0) y=0; if (y>=Height) y=Height-1; return (int)rint((Pixels[y*Width+x].r+Pixels[y*Width+x].g+Pixels[y*Width+x].b)/3); } int rAt(int x, int y) { return Pixels[y*Width+x].r; } int gAt(int x, int y) { return Pixels[y*Width+x].g; } int bAt(int x, int y) { return Pixels[y*Width+x].b; } /* @@TODO average 9 neighbouring pixels int rAtD(int x, int y) { return MAX(Pixels[y*Width+x].r-30,0); } int gAtD(int x, int y) { return MAX(Pixels[y*Width+x].g-30,0); } int bAtD(int x, int y) { return MAX(Pixels[y*Width+x].b-30,0); } */ /* convert RGB to HSV */ #define MAX(x,y,z) (x>y&&x>z)?x:((y>z)?y:z) #define MIN(x,y,z) (x=B) *h=60.0*(G-B)/(max-min); else if (max==R && G\n"); fprintf(svgf,"\n"); fprintf(svgf,"\n"); fprintf(svgf," Bitmap to Letters demo\n"); fprintf(svgf,"\n"); fprintf(svgf,"\n",FontSize); int stepy=FontSize*1.2; int nbletters=strlen(Text); /* and now for the meaty stuff */ for (y=0;y1.0?1.0:(s+saturationAdjust); hsv2rgb(&r,&g,&b,h,s,v); } fprintf(svgf,"%c\n",rx,y,FontSize*scale,r,g,b,c); rx+=scale*CharWidths[(int)c]*FontSize; ti++; /* next letter */ } else /* advance one em */ rx+=scale*CharWidths['m']*FontSize; } } fprintf(svgf," \n"); fprintf(svgf,"\n"); fclose(svgf); } int main(int argc, char **argv) { FILE *f; int i; if (argc==1 || !strcmp(argv[1],"-h")) { fprintf(stderr, "textorizer2 version $Id: textorizer2.c,v 1.4 2006/04/27 10:29:01 mf Exp $\n\n"); fprintf(stderr,"usage: %s [options]\n",argv[0]); fprintf(stderr," -o : SVG output file name (default: out.svg)\n"); fprintf(stderr," -i : PNG input file name (default: in.png)\n"); fprintf(stderr," -f : font size (default: 10)\n"); fprintf(stderr," -t : text file name (default: text.txt)\n"); fprintf(stderr," -s : colour adjustment (default: 0.2)\n"); fprintf(stderr," -w : width of SVG image in pixels (default: input width)\n"); fprintf(stderr," -h : height of SVG image in pixels (default: input height)\n"); return 0; } for (i=1;i