diff options
Diffstat (limited to 'kfax/faxinput.cpp')
-rw-r--r-- | kfax/faxinput.cpp | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/kfax/faxinput.cpp b/kfax/faxinput.cpp new file mode 100644 index 00000000..8ca7fe85 --- /dev/null +++ b/kfax/faxinput.cpp @@ -0,0 +1,479 @@ +/* Fax file input processing + Copyright (C) 1990, 1995 Frank D. Cringle. + +This file is part of viewfax - g3/g4 fax processing software. + +viewfax is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <fcntl.h> +#include "faxexpand.h" +#include <qstring.h> +#include <qfile.h> +#include <kapplication.h> +#include <klocale.h> +#include <kdebug.h> + +void statusbarupdate(char* name,int width,int height,char* res); +extern void kfaxerror(const QString& title, const QString& error); + +#define FAXMAGIC "\000PC Research, Inc\000\000\000\000\000\000" + +/* Enter an argument in the linked list of pages */ +struct pagenode * +notefile(const char *name, int type) +{ + struct pagenode *newnode = (struct pagenode *) xmalloc(sizeof *newnode); + + *newnode = defaultpage; + if (firstpage == NULL){ + firstpage = newnode; + auxpage = firstpage; + } + newnode->prev = lastpage; + newnode->next = NULL; + if (lastpage != NULL) + lastpage->next = newnode; + lastpage = newnode; + + // kdDebug() << "Adding new node " << newnode << endl; + + newnode->pathname = (char*) malloc (strlen(name) +1); + if(!newnode->pathname){ + kfaxerror(i18n("Sorry"),i18n("Out of memory\n")); + exit(1); + } + + strcpy(newnode->pathname,name); + + newnode->type = type; + + + if ((newnode->name = strrchr(newnode->pathname, '/')) != NULL) + newnode->name++; + else + newnode->name = newnode->pathname; + + if (newnode->width == 0) + newnode->width = 1728; + if (newnode->vres < 0) + newnode->vres = !(newnode->name[0] == 'f' && newnode->name[1] == 'n'); + newnode->extra = NULL; + + return newnode; +} + +static t32bits +get4(unsigned char *p, int endian) +{ + return endian ? (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3] : + p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); +} + +static int +get2(unsigned char *p, int endian) +{ + return endian ? (p[0]<<8)|p[1] : p[0]|(p[1]<<8); +} + +/* generate pagenodes for the images in a tiff file */ +int +notetiff(const char *name) +{ + + FILE *tf; + unsigned char header[8]; + static const char littleTIFF[5] = "\x49\x49\x2a\x00"; + static const char bigTIFF[5] = "\x4d\x4d\x00\x2a"; + int endian; + t32bits IFDoff; + struct pagenode *pn = NULL; + QString str; + + + if ((tf = fopen(name, "r")) == NULL) { + QString mesg = i18n("Unable to open:\n%1\n").arg(QFile::decodeName(name)); + kfaxerror(i18n("Sorry"), mesg); + return 0; + } + + if (fread(header, 8, 1, tf) == 0) { + nottiff: + fclose(tf); + (void) notefile(name,FAX_RAW); + return 0; + } + if (memcmp(header, &littleTIFF, 4) == 0) + endian = 0; + else if (memcmp(header, &bigTIFF, 4) == 0) + endian = 1; + else + goto nottiff; + IFDoff = get4(header+4, endian); + if (IFDoff & 1) + goto nottiff; + do { /* for each page */ + unsigned char buf[8]; + unsigned char *dir = NULL , *dp = NULL; + int ndirent; + pixnum iwidth = defaultpage.width ? defaultpage.width : 1728; + pixnum iheight = defaultpage.height ? defaultpage.height : 2339; + int inverse = defaultpage.inverse; + int lsbfirst = 0; + int t4opt = 0, comp = 0; + int orient = defaultpage.orient; + double yres = defaultpage.vres ? 196.0 : 98.0; + struct strip *strips = NULL; + unsigned long rowsperstrip = 0; + t32bits nstrips = 1; + + if (fseek(tf, IFDoff, SEEK_SET) < 0) { + realbad: + str = i18n("Invalid tiff file:\n%1\n").arg(QFile::decodeName(name)); + kfaxerror(i18n("Sorry"),str); + bad: + if (strips) + free(strips); + if (dir) + free(dir); + fclose(tf); + return 1; + } + if (fread(buf, 2, 1, tf) == 0) + goto realbad; + ndirent = get2(buf, endian); + dir = (unsigned char *) xmalloc(12*ndirent+4); + if (fread(dir, 12*ndirent+4, 1, tf) == 0) + goto realbad; + for (dp = dir; ndirent; ndirent--, dp += 12) { + /* for each directory entry */ + int tag, ftype; + t32bits count, value = 0; + tag = get2(dp, endian); + ftype = get2(dp+2, endian); + count = get4(dp+4, endian); + switch(ftype) { /* value is offset to list if count*size > 4 */ + case 3: /* short */ + value = get2(dp+8, endian); + break; + case 4: /* long */ + value = get4(dp+8, endian); + break; + case 5: /* offset to rational */ + value = get4(dp+8, endian); + break; + } + switch(tag) { + case 256: /* ImageWidth */ + iwidth = value; + break; + case 257: /* ImageLength */ + iheight = value; + break; + case 259: /* Compression */ + comp = value; + break; + case 262: /* PhotometricInterpretation */ + inverse ^= (value == 1); + break; + case 266: /* FillOrder */ + lsbfirst = (value == 2); + break; + case 273: /* StripOffsets */ + nstrips = count; + strips = (struct strip *) xmalloc(count * sizeof *strips); + if (count == 1 || (count == 2 && ftype == 3)) { + strips[0].offset = value; + if (count == 2) + strips[1].offset = get2(dp+10, endian); + break; + } + if (fseek(tf, value, SEEK_SET) < 0) + goto realbad; + for (count = 0; count < nstrips; count++) { + if (fread(buf, (ftype == 3) ? 2 : 4, 1, tf) == 0) + goto realbad; + strips[count].offset = (ftype == 3) ? + get2(buf, endian) : get4(buf, endian); + } + break; + case 274: /* Orientation */ + switch(value) { + default: /* row0 at top, col0 at left */ + orient = 0; + break; + case 2: /* row0 at top, col0 at right */ + orient = TURN_M; + break; + case 3: /* row0 at bottom, col0 at right */ + orient = TURN_U; + break; + case 4: /* row0 at bottom, col0 at left */ + orient = TURN_U|TURN_M; + break; + case 5: /* row0 at left, col0 at top */ + orient = TURN_M|TURN_L; + break; + case 6: /* row0 at right, col0 at top */ + orient = TURN_U|TURN_L; + break; + case 7: /* row0 at right, col0 at bottom */ + orient = TURN_U|TURN_M|TURN_L; + break; + case 8: /* row0 at left, col0 at bottom */ + orient = TURN_L; + break; + } + break; + case 278: /* RowsPerStrip */ + rowsperstrip = value; + break; + case 279: /* StripByteCounts */ + if (count != nstrips) { + str = i18n("In file %1\nStripsPerImage tag 273=%2,tag279=%3\n") + .arg(QFile::decodeName(name)).arg(nstrips).arg(count); + kfaxerror(i18n("Message"),str); + goto realbad; + } + if (count == 1 || (count == 2 && ftype == 3)) { + strips[0].size = value; + if (count == 2) + strips[1].size = get2(dp+10, endian); + break; + } + if (fseek(tf, value, SEEK_SET) < 0) + goto realbad; + for (count = 0; count < nstrips; count++) { + if (fread(buf, (ftype == 3) ? 2 : 4, 1, tf) == 0) + goto realbad; + strips[count].size = (ftype == 3) ? + get2(buf, endian) : get4(buf, endian); + } + break; + case 283: /* YResolution */ + if (fseek(tf, value, SEEK_SET) < 0 || + fread(buf, 8, 1, tf) == 0) + goto realbad; + yres = get4(buf, endian) / get4(buf+4, endian); + break; + case 292: /* T4Options */ + t4opt = value; + break; + case 293: /* T6Options */ + /* later */ + break; + case 296: /* ResolutionUnit */ + if (value == 3) + yres *= 2.54; + break; + } + } + IFDoff = get4(dp, endian); + free(dir); + dir = NULL; + if (comp == 5) { + // compression type 5 is LZW compression + kfaxerror(i18n("Sorry"),i18n("Due to patent reasons KFax can not handle LZW (Lempel-Ziv & Welch) " + "compressed Fax files.\n")); + goto bad; + } + if (comp < 2 || comp > 4) { + kfaxerror(i18n("Sorry"),i18n("This version can only handle Fax files\n")); + goto bad; + } + pn = notefile(name,FAX_TIFF); + pn->nstrips = nstrips; + pn->rowsperstrip = nstrips > 1 ? rowsperstrip : iheight; + pn->strips = strips; + pn->width = iwidth; + pn->height = iheight; + pn->inverse = inverse; + pn->lsbfirst = lsbfirst; + pn->orient = orient; + pn->vres = (yres > 150); /* arbitrary threshold for fine resolution */ + if (comp == 2) + pn->expander = MHexpand; + else if (comp == 3) + pn->expander = (t4opt & 1) ? g32expand : g31expand; + else + pn->expander = g4expand; + } while (IFDoff); + fclose(tf); + return 1; +} + +/* report error and remove bad file from the list */ +static void +badfile(struct pagenode *pn) +{ + struct pagenode *p; + + if (errno) + perror(pn->pathname); + if (pn == firstpage) { + if (pn->next == NULL){ + kfaxerror(i18n("Sorry"),i18n("Bad Fax File")); + return; + } + else{ + firstpage = thispage = firstpage->next; + firstpage->prev = NULL; + } + } + else + for (p = firstpage; p; p = p->next) + if (p->next == pn) { + thispage = p; + p->next = pn->next; + if (pn->next) + pn->next->prev = p; + break; + } + if (pn) free(pn); + pn = NULL; +} + +/* rearrange input bits into t16bits lsb-first chunks */ +static void +normalize(struct pagenode *pn, int revbits, int swapbytes, size_t length) +{ + t32bits *p = (t32bits *) pn->data; + + switch ((revbits<<1)|swapbytes) { + case 0: + break; + case 1: + for ( ; length; length -= 4) { + t32bits t = *p; + *p++ = ((t & 0xff00ff00) >> 8) | ((t & 0x00ff00ff) << 8); + } + break; + case 2: + for ( ; length; length -= 4) { + t32bits t = *p; + t = ((t & 0xf0f0f0f0) >> 4) | ((t & 0x0f0f0f0f) << 4); + t = ((t & 0xcccccccc) >> 2) | ((t & 0x33333333) << 2); + *p++ = ((t & 0xaaaaaaaa) >> 1) | ((t & 0x55555555) << 1); + } + break; + case 3: + for ( ; length; length -= 4) { + t32bits t = *p; + t = ((t & 0xff00ff00) >> 8) | ((t & 0x00ff00ff) << 8); + t = ((t & 0xf0f0f0f0) >> 4) | ((t & 0x0f0f0f0f) << 4); + t = ((t & 0xcccccccc) >> 2) | ((t & 0x33333333) << 2); + *p++ = ((t & 0xaaaaaaaa) >> 1) | ((t & 0x55555555) << 1); + } + } +} + + +/* get compressed data into memory */ +unsigned char * +getstrip(struct pagenode *pn, int strip) +{ + int fd; + size_t offset, roundup; + struct stat sbuf; + unsigned char *Data; + union { t16bits s; unsigned char b[2]; } so; + QString str; + +#define ShortOrder so.b[1] + + so.s = 1; + if ((fd = open(pn->pathname, O_RDONLY, 0)) < 0) { + badfile(pn); + return NULL; + } + + if (pn->strips == NULL) { + if (fstat(fd, &sbuf) != 0) { + close(fd); + badfile(pn); + return NULL; + } + offset = 0; + pn->length = sbuf.st_size; + } + else if (strip < pn->nstrips) { + offset = pn->strips[strip].offset; + pn->length = pn->strips[strip].size; + } + else { + str = i18n("Trying to expand too many strips\n%1%n").arg(QFile::decodeName(pn->pathname)); + kfaxerror(i18n("Warning"),str); + return NULL; + } + + /* round size to full boundary plus t32bits */ + roundup = (pn->length + 7) & ~3; + + Data = (unsigned char *) xmalloc(roundup); + /* clear the last 2 t32bits, to force the expander to terminate + even if the file ends in the middle of a fax line */ + *((t32bits *) Data + roundup/4 - 2) = 0; + *((t32bits *) Data + roundup/4 - 1) = 0; + + /* we expect to get it in one gulp... */ + if (lseek(fd, offset, SEEK_SET) < 0 || + (uint) read(fd, Data, pn->length) != pn->length) { + badfile(pn); + free(Data); + close(fd); + return NULL; + } + close(fd); + + pn->data = (t16bits *) Data; + if (pn->strips == NULL && memcmp(Data, FAXMAGIC, sizeof(FAXMAGIC)) == 0) { + /* handle ghostscript / PC Research fax file */ + if (Data[24] != 1 || Data[25] != 0){ + str = i18n("Only the first page of the PC Research multipage file\n%1\nwill be shown\n") + .arg(QFile::decodeName(pn->pathname)); + kfaxerror(i18n("Message"),str); + } + pn->length -= 64; + pn->vres = Data[29]; + pn->data += 32; + roundup -= 64; + } + + normalize(pn, !pn->lsbfirst, ShortOrder, roundup); + if (pn->height == 0) + pn->height = G3count(pn, pn->expander == g32expand); + if (pn->height == 0) { + + str = i18n("No fax found in file:\n%1\n").arg(QFile::decodeName(pn->pathname)); + kfaxerror(i18n("Sorry"),str); + errno = 0; + badfile(pn); + free(Data); + return NULL; + } + if (pn->strips == NULL) + pn->rowsperstrip = pn->height; + if (verbose && strip == 0) + kdWarning() << pn->name << "\n\twidth = " << pn->width << "\n\theight = " + << pn->height << "\n\tresolution = " << (pn->vres ? "fine" : "normal") << endl; +// statusbarupdate(pn->name,pn->width,pn->height,pn->vres ? "fine" : "normal"); + return Data; +} |