491 lines
13 KiB
C
491 lines
13 KiB
C
/* (c) 2000 Karel Kulhavy, Clocksoft
|
|
* clock@atrey.karlin.mff.cuni.cz
|
|
* This program is a stdin filter that performs these operations:
|
|
* 1) Downconverts 17x15 oversampled binary image data (black-or-white
|
|
* pixels, 1 bit per pixel, pbmraw format) to 256-level grayscale
|
|
* pgmraw format and saves it to basename_%d.png.
|
|
* 2) At the end, generates necessary html's
|
|
* 3) The input pages must be concatenated
|
|
* 4) Recommended source of the pbm: Aladdin Ghostscript
|
|
* If there is an input argument (one number as first arg), it's treated
|
|
* as number of first page that comes at fd #0. Default value is 0.
|
|
* Parameters: <hundred_dpi> <basename_> <titlestring> <bottom_html_code>
|
|
* <pageoffset> <input_filename> [first page number]
|
|
|
|
* Little benchmarks: Normal (gs+pbm2png) 1:32
|
|
Without pbm2png 0:28
|
|
Without libpng 0:51
|
|
Without grayscaling 0:32
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <png.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
unsigned char *basename_;
|
|
/*
|
|
basename:
|
|
pbm2png.c:32: `basename' redeclared as different kind of symbol
|
|
/usr/include/string.h:245: previous declaration of `basename'
|
|
*/
|
|
unsigned char *titlestring;
|
|
unsigned char *bottom;
|
|
int pageoffset=-13;
|
|
unsigned long t1[256]; /* Conversion from 1-bit to 2-bit expansion */
|
|
unsigned long t2[256]; /* Conversion from 2-bit to 4-bit expansion */
|
|
unsigned char *l1; /* Input data, output data. lw<<1 bytes long */
|
|
unsigned long *l2; /* 2-bit expansion lw unsigned long's long*/
|
|
unsigned long long *l4; /* 4-bit expansion lw unsigned long long's long */
|
|
int ox,oy;
|
|
int lw; /* Width of the input in 16-pixel units. */
|
|
int lb; /* Width of the input in 8-pixel units */
|
|
FILE *of;
|
|
int filenumber;
|
|
int x,y;
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
unsigned long ppm;
|
|
float dpi;
|
|
unsigned char string[8192];
|
|
unsigned char *filename;
|
|
int ifd;
|
|
|
|
void
|
|
gentables(void)
|
|
{
|
|
int a,d;
|
|
unsigned long b;
|
|
unsigned char c;
|
|
|
|
for (a=0;a<256;a++){
|
|
b=0;
|
|
c=a;
|
|
for (d=7;d>=0;d--){
|
|
b<<=2;
|
|
b|=(c>>d)&1;
|
|
}
|
|
t1[a]=b;
|
|
b=0;
|
|
c=a;
|
|
for (d=4;d;d--){
|
|
b<<=4;
|
|
b|=c&3;
|
|
c>>=2;
|
|
}
|
|
t2[a]=b;
|
|
}
|
|
}
|
|
|
|
void
|
|
make_index(void)
|
|
{
|
|
FILE *f;
|
|
|
|
f=fopen("index.html","w");
|
|
|
|
fprintf(f,"<html><head><title>%s</title></head><body bgcolor=\"#000000\" text=\"#00ff00\" link=\"#ffff00\""
|
|
" vlink=\"#00ffff\" alink=\"#ffff00\">\n",titlestring);
|
|
fprintf(f,"<a href=\"../\"><img src=\"up.png\" border=0></a><br>"
|
|
"<a href=\"%s.pdf\">Download in PDF format</a><br>"
|
|
"<h1>%s</h1><h2>Page index</h2>",basename_,titlestring);
|
|
{
|
|
int n;
|
|
unsigned char *t;
|
|
FILE *g;
|
|
|
|
fprintf(f,"<table cellspacing=0 cellpadding=0 border=1>\n<tr>");
|
|
for (n=0;n<filenumber;n++){
|
|
if (!(n%40)&&n){
|
|
fprintf(f,"</tr>\n<tr>");
|
|
}
|
|
fprintf(f,"<td><a href=\"%s%d.html\">%d</a></td>\n",basename_,n,n+pageoffset);
|
|
}
|
|
|
|
fprintf(f,"</tr></table><h2>");
|
|
g=fopen("index.dir","rb");
|
|
if (g)
|
|
{
|
|
t=malloc(65536);
|
|
fprintf(f,"Contents Index</h2><table border=0 cellspacing=0 cellpadding=0>");
|
|
while(fgets(t,65536,g)){
|
|
int val=0;
|
|
int len=strlen(t);
|
|
unsigned char *u=t;
|
|
if (len&&t[len-1]=='\n') t[len-1]=0;
|
|
if (*t>'9'||*t<'0') continue;
|
|
while(*u>='0'&&*u<='9'){
|
|
val*=10;
|
|
val+=*u-'0';
|
|
u++;
|
|
}
|
|
val--;
|
|
u++; /* The char behind the number is ignored */
|
|
fprintf(f,"<tr><td>%d </td><td><a href=\"%s%d.html\">%s</a></td></tr>\n",val+pageoffset
|
|
,basename_,val,u);
|
|
}
|
|
fprintf(f,"</table>");
|
|
fclose(g);
|
|
free(t);
|
|
}
|
|
fprintf(f,"</body></html>");
|
|
fclose(f);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
make_page(int index)
|
|
{
|
|
FILE *f;
|
|
|
|
sprintf(string,"%s%d.html",basename_,index);
|
|
f=fopen(string,"w");
|
|
|
|
fprintf(f,"<html><head><title>%s</title></head><body bgcolor=\"#000000\" text=\"#00ff00\" link=\"#ffff00\" vlink=\"#00ffff\" alink=\"#ffff00\">\n",titlestring);
|
|
fprintf(f,"<h1>%s, Page %d</h1>\n",titlestring,index+pageoffset);
|
|
fprintf(f,"<p><table border=0 cellspacing=0 cellpadding=2><tr>");
|
|
fprintf(f,"<td valign=\"top\"><table border=0 cellpadding=0 cellspacing=0><tr><td>");
|
|
if (index)
|
|
{
|
|
fprintf(f,"<a href=\"%s%d.html\"><img src=\"left.png\" border=0></a>\n",basename_,index-1);
|
|
}
|
|
else
|
|
{
|
|
fprintf(f,"<img src=\"left.png\" border=0>\n");
|
|
}
|
|
fprintf(f,"</td><td>");
|
|
if (index<filenumber-1)
|
|
{
|
|
fprintf(f,"<a href=\"%s%d.html\"><img src=\"right.png\" border=0></a>\n",basename_,index+1);
|
|
}
|
|
else
|
|
{
|
|
fprintf(f,"<img src=\"right.png\" border=0>\n");
|
|
}
|
|
fprintf(f,"</td></tr><tr><td colspan=2><a href=\"index.html\"><img src=\"idx.png\" border=0></a></td>");
|
|
fprintf(f,"</tr></table>");
|
|
fprintf(f,"</td><td><img src=\"%s%d.png\" border=\"0\"></td>\n</tr></table>",basename_,index);
|
|
fprintf(f,"</body></html>");
|
|
fclose(f);
|
|
}
|
|
|
|
/* Returns 0 is OK, exit(1) on error, returns 1 on broken pipe */
|
|
int
|
|
sure_read(unsigned char *dest, size_t len)
|
|
{
|
|
ssize_t rd;
|
|
if (!len) return 0;
|
|
again:
|
|
rd=read(ifd,dest,len);
|
|
if (rd==len) return 0;
|
|
if (!rd) return 1;
|
|
if (rd<0&&(errno==EINTR||errno==EAGAIN||errno==EWOULDBLOCK)) goto again;
|
|
if (rd>0&&rd<len){
|
|
/*fprintf(stderr,"read %d/%d",rd,len);*/
|
|
len-=rd;
|
|
dest+=rd;
|
|
fflush(stderr);
|
|
goto again;
|
|
}
|
|
fprintf(stderr,"read error.\n");
|
|
perror("");
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
eat_up_whitespace_and_comments(void)
|
|
{
|
|
again:
|
|
sure_read(string,1);
|
|
if (*string==' '||*string=='\t'){
|
|
goto again;
|
|
}
|
|
if (*string=='\n'||*string=='\r'){
|
|
goto again;
|
|
}
|
|
if (*string=='#'){
|
|
/* Eat up comment */
|
|
aa:
|
|
sure_read(string,1);
|
|
if (*string!='\n'&&*string!='\r') goto aa;
|
|
goto again;
|
|
}
|
|
return *string;
|
|
}
|
|
|
|
int
|
|
read_header(void)
|
|
{
|
|
|
|
|
|
/*
|
|
if (filenumber)
|
|
{
|
|
int a;
|
|
|
|
for (a=0;a<256;a++){
|
|
sure_read(string,1);
|
|
printf("%d ", *string);
|
|
fflush(stdout);
|
|
}
|
|
exit(0);
|
|
}
|
|
*/
|
|
if (sure_read(string,2)) return 1; /* P1 */
|
|
x=eat_up_whitespace_and_comments()-'0';
|
|
again:
|
|
sure_read(string,1);
|
|
if (*string!=' '&&*string!='\t'&&*string!='\n'&&*string!='\r'){
|
|
x*=10;
|
|
x+=*string-'0';
|
|
goto again;
|
|
}
|
|
y=eat_up_whitespace_and_comments()-'0';
|
|
bgain:
|
|
sure_read(string,1);
|
|
if (*string!=' '&&*string!='\t'&&*string!='\n'&&*string!='\r'){
|
|
y*=10;
|
|
y+=*string-'0';
|
|
goto bgain;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifndef Z_BEST_COMPRESSION
|
|
#define Z_BEST_COMPRESSION 9
|
|
#endif
|
|
|
|
void
|
|
open_png(void)
|
|
{
|
|
png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
|
|
info_ptr=png_create_info_struct(png_ptr);
|
|
png_init_io(png_ptr,of);
|
|
png_set_filter(png_ptr,0,PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_UP
|
|
|PNG_FILTER_AVG|PNG_FILTER_PAETH);
|
|
png_set_compression_level(png_ptr,Z_BEST_COMPRESSION);
|
|
png_set_IHDR(png_ptr,info_ptr,ox,oy,8,PNG_COLOR_TYPE_GRAY,PNG_INTERLACE_NONE,
|
|
PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
|
|
png_set_gAMA(png_ptr,info_ptr,1.0);
|
|
png_set_pHYs(png_ptr,info_ptr,ppm,ppm,PNG_RESOLUTION_METER);
|
|
png_write_info(png_ptr,info_ptr);
|
|
}
|
|
|
|
void
|
|
close_png(void)
|
|
{
|
|
png_write_end(png_ptr,info_ptr);
|
|
png_destroy_write_struct(&png_ptr,&info_ptr);
|
|
}
|
|
|
|
/* Represents the same pixels that are in l2 in l4. */
|
|
void
|
|
move_2_to_4(void)
|
|
{
|
|
unsigned long long bit4;
|
|
unsigned long bit2;
|
|
int index;
|
|
|
|
for (index=lw-1;index>=0;index--){
|
|
bit2=l2[index];
|
|
bit4=t2[bit2&255];
|
|
bit4<<=16;
|
|
bit2>>=8;
|
|
bit4|=t2[bit2&255];
|
|
bit4<<=16;
|
|
bit2>>=8;
|
|
bit4|=t2[bit2&255];
|
|
bit4<<=16;
|
|
bit2>>=8;
|
|
bit4|=t2[bit2&255];
|
|
l4[index]=bit4;
|
|
}
|
|
}
|
|
|
|
/* Adds the same pixels that are in l2 to l4. */
|
|
void
|
|
add_2_to_4(void)
|
|
{
|
|
unsigned long long bit4;
|
|
unsigned long bit2;
|
|
int index;
|
|
|
|
for (index=lw-1;index>=0;index--){
|
|
bit2=l2[index];
|
|
bit4=t2[bit2&255];
|
|
bit4<<=16;
|
|
bit2>>=8;
|
|
bit4|=t2[bit2&255];
|
|
bit4<<=16;
|
|
bit2>>=8;
|
|
bit4|=t2[bit2&255];
|
|
bit4<<=16;
|
|
bit2>>=8;
|
|
bit4|=t2[bit2&255];
|
|
l4[index]+=bit4;
|
|
}
|
|
}
|
|
|
|
/* Moves pixels from l1 to l2 with format change */
|
|
void
|
|
move_1_to_2(void)
|
|
{
|
|
int index;
|
|
|
|
for (index=lw-1;index>=0;index--)
|
|
l2[index]=(t1[l1[index<<1]]<<16)|t1[l1[(index<<1)+1]];
|
|
}
|
|
|
|
/* Adds pixels from l1 to l2 with format change */
|
|
void
|
|
add_1_to_2(void)
|
|
{
|
|
int index;
|
|
|
|
for (index=lw-1;index>=0;index--)
|
|
l2[index]+=(t1[l1[index<<1]]<<16)|t1[l1[(index<<1)+1]];
|
|
}
|
|
|
|
/* Loads 1 line into l1 */
|
|
void
|
|
load(void)
|
|
{
|
|
sure_read(l1,lb);
|
|
}
|
|
|
|
/* Loads 3 lines into l2 */
|
|
void
|
|
load_to_2(void)
|
|
{
|
|
load();
|
|
move_1_to_2();
|
|
load();
|
|
add_1_to_2();
|
|
load();
|
|
add_1_to_2();
|
|
}
|
|
|
|
/* Loads 15 lines into l4 */
|
|
void
|
|
load_to_4(void)
|
|
{
|
|
load_to_2();
|
|
move_2_to_4();
|
|
load_to_2();
|
|
add_2_to_4();
|
|
load_to_2();
|
|
add_2_to_4();
|
|
load_to_2();
|
|
add_2_to_4();
|
|
load_to_2();
|
|
add_2_to_4();
|
|
}
|
|
|
|
/* Converts the data from l4 to l1 including suming up 17 adjacent pixels */
|
|
void
|
|
export_from_4(void)
|
|
{
|
|
int a,b,reg_hold;
|
|
unsigned char sum;
|
|
unsigned long long *loader=l4;
|
|
unsigned long long reg=0;
|
|
|
|
reg_hold=0;
|
|
for (a=0;a<ox;a++){
|
|
sum=0;
|
|
for (b=17;b;b--){
|
|
if (!reg_hold){
|
|
reg=*loader++;
|
|
reg_hold=16;
|
|
}
|
|
sum+=reg&15;
|
|
reg>>=4;
|
|
reg_hold--;
|
|
}
|
|
l1[a]=sum;
|
|
}
|
|
png_write_row(png_ptr,l1);
|
|
}
|
|
|
|
/*
|
|
* Parameters: <hundred_dpi> <basename_> <titlestring> <bottom_html_code>
|
|
* <pageoffset> [first page number]
|
|
*/
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int a,z;
|
|
|
|
if (argc<7){
|
|
fprintf(stderr,"Usage: pbm2png <hundred_dpi> <basename_> <titlestring> <bottom_html_code> <pageoffset> <ifname> [starting_filenumber]\n");
|
|
return 0;
|
|
}
|
|
dpi=atof(argv[1])/10;
|
|
ppm=dpi*1000/25.4;
|
|
basename_=argv[2];
|
|
titlestring=argv[3];
|
|
bottom=argv[4];
|
|
pageoffset=atol(argv[5]);
|
|
filename=argv[6];
|
|
again0:
|
|
ifd=open(filename,O_RDONLY);
|
|
if (ifd<0){
|
|
if (errno==EAGAIN||errno==EINTR||errno==EWOULDBLOCK) goto
|
|
again0;
|
|
else {
|
|
perror("");
|
|
exit(1);
|
|
}
|
|
}
|
|
fprintf(stderr,"filename %s, %d\n",filename,ifd);
|
|
if (argc>=8){
|
|
filenumber=atol(argv[7]);
|
|
}
|
|
gentables();
|
|
again:
|
|
fprintf(stderr,"\nFile %i\n",filenumber);
|
|
if (read_header()){
|
|
for (a=0;a<filenumber;a++)
|
|
make_page(a);
|
|
make_index();
|
|
return 0;
|
|
}
|
|
lw=(x+15)>>4;
|
|
lb=(x+7)>>3;
|
|
ox=x/17;
|
|
oy=y/15;
|
|
fprintf(stderr,"Input: %i*%i pixels, %f*%f dpi, %.1fMB.\n",x,y,dpi*17,dpi*15,(float)lb*y/1048576);
|
|
fprintf(stderr,"Ouput: %i*%i pixels, %f*%f dpi, %.1fKB raw data.\n",ox,oy,dpi,dpi,(float)ox*oy/1024);
|
|
l1=(unsigned char*)malloc(lw*2);
|
|
l2=(unsigned long*)malloc(lw*sizeof(unsigned long));
|
|
l4=(unsigned long long*)malloc(lw*sizeof(unsigned long long));
|
|
sprintf(string,"%s%d.png",basename_,filenumber);
|
|
filenumber++;
|
|
of=fopen(string,"w");
|
|
open_png();
|
|
for (z=oy;z;z--){
|
|
if (!(z&15)){
|
|
fprintf(stderr,".");
|
|
fflush(stderr);
|
|
}
|
|
load_to_4();
|
|
export_from_4();
|
|
}
|
|
close_png();
|
|
fprintf(stderr,"\nWritten %lu bytes of data, ratio %.1f%%\n",ftell(of),(float)ftell(of)*100/ox/oy);
|
|
fclose(of);
|
|
for (a=y%15;a;a--)
|
|
load();
|
|
free(l1);
|
|
free(l2);
|
|
free(l4);
|
|
goto again;
|
|
}
|