diff options
Diffstat (limited to 'kfaxview/libkfaximage')
-rw-r--r-- | kfaxview/libkfaximage/Makefile.am | 14 | ||||
-rw-r--r-- | kfaxview/libkfaximage/faxexpand.cpp | 745 | ||||
-rw-r--r-- | kfaxview/libkfaximage/faxexpand.h | 126 | ||||
-rw-r--r-- | kfaxview/libkfaximage/faxinit.cpp | 345 | ||||
-rw-r--r-- | kfaxview/libkfaximage/kfaximage.cpp | 667 | ||||
-rw-r--r-- | kfaxview/libkfaximage/kfaximage.h | 162 |
6 files changed, 2059 insertions, 0 deletions
diff --git a/kfaxview/libkfaximage/Makefile.am b/kfaxview/libkfaximage/Makefile.am new file mode 100644 index 00000000..616afb3c --- /dev/null +++ b/kfaxview/libkfaximage/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = -I$(top_srcdir) $(all_includes) + +lib_LTLIBRARIES = libkfaximage.la +libkfaximage_la_LDFLAGS = $(all_libraries) -no-undefined -avoid-version +libkfaximage_la_LIBADD = $(LIB_KDECORE) +libkfaximage_la_SOURCES = kfaximage.cpp faxexpand.cpp faxinit.cpp + +include_HEADERS = kfaximage.h +noinst_HEADERS = faxexpand.h + +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) $(libkfaximage_la_SOURCES) -o $(podir)/libkfaximgage.pot diff --git a/kfaxview/libkfaximage/faxexpand.cpp b/kfaxview/libkfaximage/faxexpand.cpp new file mode 100644 index 00000000..9c4b4082 --- /dev/null +++ b/kfaxview/libkfaximage/faxexpand.cpp @@ -0,0 +1,745 @@ +/* Expand one page of fax data + 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 <config.h> + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> + +#include <kdebug.h> + +#include "faxexpand.h" + +//Uncomment this for verbose debug output +//#define DEBUG_FAX +#define verbose false + +pagenode::pagenode() +{ +} + +/* Note that NeedBits() only works for n <= 16 */ +#define NeedBits(n) do { \ + if (BitsAvail < (n)) { \ + BitAcc |= *sp++ << BitsAvail; \ + BitsAvail += 16; \ + } \ +} while (0) +#define GetBits(n) (BitAcc & ((1<<(n))-1)) +#define ClrBits(n) do { \ + BitAcc >>= (n); \ + BitsAvail -= (n); \ +} while (0) + +#ifdef DEBUG_FAX +#define DEBUG_SHOW putchar(BitAcc & (1 << t) ? '1' : '0') +#define LOOKUP(wid,tab) do { \ + int t; \ + NeedBits(wid); \ + TabEnt = tab + GetBits(wid); \ + printf("%08lX/%d: %s%5d\t", BitAcc, BitsAvail, \ + StateNames[TabEnt->State], TabEnt->Param); \ + for (t = 0; t < TabEnt->Width; t++) \ + DEBUG_SHOW; \ + putchar('\n'); \ + fflush(stdout); \ + ClrBits(TabEnt->Width); \ +} while (0) + +#define SETVAL(x) do { \ + *pa++ = RunLength + (x); \ + printf("SETVAL: %d\t%d\n", RunLength + (x), a0); \ + a0 += x; \ + RunLength = 0; \ +} while (0) + +const char *StateNames[] = { + "Null ", + "Pass ", + "Horiz ", + "V0 ", + "VR ", + "VL ", + "Ext ", + "TermW ", + "TermB ", + "MakeUpW", + "MakeUpB", + "MakeUp ", + "EOL ", +}; + +#else +#define LOOKUP(wid,tab) do { \ + NeedBits(wid); \ + TabEnt = tab + GetBits(wid); \ + ClrBits(TabEnt->Width); \ +} while (0) + +#define SETVAL(x) do { \ + *pa++ = RunLength + (x); \ + a0 += x; \ + RunLength = 0; \ +} while (0) +#endif + +#define dumpruns(runs) do { \ + printf("-------------------- %d\n", LineNum); \ + for (pa = runs, a0 = 0; a0 < lastx; a0 += *pa++) \ + printf("%4d %d\n", a0, *pa); \ +} while (0) + +#define EndOfData(pn) (sp >= pn->data + pn->length/sizeof(*pn->data)) + +/* This macro handles coding errors in G3 data. + We redefine it below for the G4 case */ +#define SKIP_EOL do { \ + while (!EndOfData(pn)) { \ + NeedBits(11); \ + if (GetBits(11) == 0) \ + break; \ + ClrBits(1); \ + } \ + ClrBits(11); \ + goto EOL; \ +} while (0) +#define eol2lab EOL2: + +/* the line expanders are written as macros so that they can be reused + (twice each) but still have direct access to the local variables of + the "calling" function */ +#define expand1d() do { \ + while (a0 < lastx) { \ + int done = 0; \ + while (!done) { /* white first */ \ + LOOKUP(12, WhiteTable); \ + switch (TabEnt->State) { \ + case S_EOL: \ + EOLcnt = 1; \ + goto EOL; \ + case S_TermW: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + case S_Ext: \ + unexpected("Extension code", LineNum); \ + SKIP_EOL; \ + break; \ + default: \ + unexpected("WhiteTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + done = a0 >= lastx; \ + while (!done) { /* then black */ \ + LOOKUP(13, BlackTable); \ + switch (TabEnt->State) { \ + case S_EOL: \ + EOLcnt = 1; \ + goto EOL; \ + case S_TermB: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + case S_Ext: \ + unexpected("Extension code", LineNum); \ + SKIP_EOL; \ + break; \ + default: \ + unexpected("BlackTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + } \ + EOL: ; \ +} while (0) + +#define CHECK_b1 do { \ + if (pa != thisrun) while (b1 <= a0 && b1 < lastx) { \ + b1 += pb[0] + pb[1]; \ + pb += 2; \ + } \ +} while (0) + +#define expand2d(eolab) do { \ + while (a0 < lastx) { \ + LOOKUP(7, MainTable); \ + switch (TabEnt->State) { \ + case S_Pass: \ + CHECK_b1; \ + b1 += *pb++; \ + RunLength += b1 - a0; \ + a0 = b1; \ + b1 += *pb++; \ + break; \ + case S_Horiz: \ + if ((pa-run0)&1) { \ + int done = 0; \ + while (!done) { /* black first */ \ + LOOKUP(13, BlackTable); \ + switch (TabEnt->State) { \ + case S_TermB: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("BlackTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + done = 0; \ + while (!done) { /* then white */ \ + LOOKUP(12, WhiteTable); \ + switch (TabEnt->State) { \ + case S_TermW: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("WhiteTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + } \ + else { \ + int done = 0; \ + while (!done) { /* white first */ \ + LOOKUP(12, WhiteTable); \ + switch (TabEnt->State) { \ + case S_TermW: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("WhiteTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + done = 0; \ + while (!done) { /* then black */ \ + LOOKUP(13, BlackTable); \ + switch (TabEnt->State) { \ + case S_TermB: \ + SETVAL(TabEnt->Param); \ + done = 1; \ + break; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("BlackTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + } \ + CHECK_b1; \ + break; \ + case S_V0: \ + CHECK_b1; \ + SETVAL(b1 - a0); \ + b1 += *pb++; \ + break; \ + case S_VR: \ + CHECK_b1; \ + SETVAL(b1 - a0 + TabEnt->Param); \ + b1 += *pb++; \ + break; \ + case S_VL: \ + CHECK_b1; \ + SETVAL(b1 - a0 - TabEnt->Param); \ + b1 -= *--pb; \ + break; \ + case S_Ext: \ + *pa++ = lastx - a0; \ + if (verbose) \ + kdDebug() << "Line " << LineNum << ": extension code\n";\ + SKIP_EOL; \ + break; \ + case S_EOL: \ + *pa++ = lastx - a0; \ + NeedBits(4); \ + if (GetBits(4) && verbose) /* already seen 7 zeros */ \ + kdDebug() << "Line " << LineNum << ": Bad EOL\n"; \ + ClrBits(4); \ + EOLcnt = 1; \ + goto eolab; \ + break; \ + default: \ + unexpected("MainTable", LineNum); \ + SKIP_EOL; \ + break; \ + } \ + } \ + if (RunLength) { \ + if (RunLength + a0 < lastx) { \ + /* expect a final V0 */ \ + NeedBits(1); \ + if (!GetBits(1)) { \ + unexpected("MainTable", LineNum); \ + SKIP_EOL; \ + } \ + ClrBits(1); \ + } \ + SETVAL(0); \ + } \ + eol2lab ; \ +} while (0) + +static void +unexpected(const char *what, int LineNum) +{ + if (verbose) + kdError() << "Line " << LineNum << ": Unexpected state in " + << what << endl; +} + +/* Expand tiff modified huffman data (g3-1d without EOLs) */ +void +MHexpand(struct pagenode *pn, drawfunc df) +{ + int a0; /* reference element */ + int lastx; /* copy line width to register */ + t32bits BitAcc; /* bit accumulator */ + int BitsAvail; /* # valid bits in BitAcc */ + int RunLength; /* Length of current run */ + t16bits *sp; /* pointer into compressed data */ + pixnum *pa; /* pointer into new line */ + int EOLcnt; /* number of consecutive EOLs */ + int LineNum; /* line number */ + pixnum *runs; /* list of run lengths */ + struct tabent *TabEnt; + + sp = pn->data; + BitAcc = 0; + BitsAvail = 0; + lastx = pn->size.width(); + runs = (pixnum *) malloc(lastx * sizeof(pixnum)); + for (LineNum = 0; LineNum < pn->rowsperstrip; ) { +#ifdef DEBUG_FAX + printf("\nBitAcc=%08lX, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", LineNum); + fflush(stdout); +#endif + RunLength = 0; + pa = runs; + a0 = 0; + EOLcnt = 0; + if (BitsAvail & 7) /* skip to byte boundary */ + ClrBits(BitsAvail & 7); + expand1d(); + if (RunLength) + SETVAL(0); + if (a0 != lastx) { + if (verbose) + kdWarning() << "Line " << LineNum << ": length is " + << a0 << " (expected "<< lastx << ")\n"; + while (a0 > lastx) + a0 -= *--pa; + if (a0 < lastx) { + if ((pa - runs) & 1) + SETVAL(0); + SETVAL(lastx - a0); + } + } + (*df)(runs, LineNum++, pn); + } + free(runs); +} + +/* Expand group-3 1-dimensional data */ +void +g31expand(struct pagenode *pn, drawfunc df) +{ + int a0; /* reference element */ + int lastx; /* copy line width to register */ + t32bits BitAcc; /* bit accumulator */ + int BitsAvail; /* # valid bits in BitAcc */ + int RunLength; /* Length of current run */ + t16bits *sp; /* pointer into compressed data */ + pixnum *pa; /* pointer into new line */ + int EOLcnt; /* number of consecutive EOLs */ + int LineNum; /* line number */ + pixnum *runs; /* list of run lengths */ + struct tabent *TabEnt; + + sp = pn->data; + BitAcc = 0; + BitsAvail = 0; + lastx = pn->size.width(); + runs = (pixnum *) malloc(lastx * sizeof(pixnum)); + EOLcnt = 0; + for (LineNum = 0; LineNum < pn->rowsperstrip; ) { +#ifdef DEBUG_FAX + printf("\nBitAcc=%08lX, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", LineNum); + fflush(stdout); +#endif + if (EOLcnt == 0) + while (!EndOfData(pn)) { + /* skip over garbage after a coding error */ + NeedBits(11); + if (GetBits(11) == 0) + break; + ClrBits(1); + } + for (EOLcnt = 1; !EndOfData(pn); EOLcnt++) { + /* we have seen 11 zeros, which implies EOL, + skip possible fill bits too */ + while (1) { + NeedBits(8); + if (GetBits(8)) + break; + ClrBits(8); + } + while (GetBits(1) == 0) + ClrBits(1); + ClrBits(1); /* the eol flag */ + NeedBits(11); + if (GetBits(11)) + break; + ClrBits(11); + } + if (EOLcnt > 1 && EOLcnt != 6 && verbose) + kdError() << "Line " << LineNum << ": bad RTC (" << EOLcnt << " EOLs)\n"; + if (EOLcnt >= 6 || EndOfData(pn)) { + free(runs); + return; + } + RunLength = 0; + pa = runs; + a0 = 0; + EOLcnt = 0; + expand1d(); + if (RunLength) + SETVAL(0); + if (a0 != lastx) { + if (verbose) + kdWarning() << "Line " << LineNum << ": length is " + << a0 << " (expected "<< lastx << ")\n"; + while (a0 > lastx) + a0 -= *--pa; + if (a0 < lastx) { + if ((pa - runs) & 1) + SETVAL(0); + SETVAL(lastx - a0); + } + } + (*df)(runs, LineNum++, pn); + } + free(runs); +} + +/* Expand group-3 2-dimensional data */ +void +g32expand(struct pagenode *pn, drawfunc df) +{ + int RunLength; /* Length of current run */ + int a0; /* reference element */ + int b1; /* next change on previous line */ + int lastx = pn->size.width();/* copy line width to register */ + pixnum *run0, *run1; /* run length arrays */ + pixnum *thisrun, *pa, *pb; /* pointers into runs */ + t16bits *sp; /* pointer into compressed data */ + t32bits BitAcc; /* bit accumulator */ + int BitsAvail; /* # valid bits in BitAcc */ + int EOLcnt; /* number of consecutive EOLs */ + int refline = 0; /* 1D encoded reference line */ + int LineNum; /* line number */ + struct tabent *TabEnt; + + sp = pn->data; + BitAcc = 0; + BitsAvail = 0; + /* allocate space for 2 runlength arrays */ + run0 = (pixnum *) malloc(2 * ((lastx+5)&~1) * sizeof(pixnum)); + run1 = run0 + ((lastx+5)&~1); + run1[0] = lastx; + run1[1] = 0; + EOLcnt = 0; + for (LineNum = 0; LineNum < pn->rowsperstrip; ) { +#ifdef DEBUG_FAX + printf("\nBitAcc=%08lX, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", LineNum); + fflush(stdout); +#endif + if (EOLcnt == 0) + while (!EndOfData(pn)) { + /* skip over garbage after a coding error */ + NeedBits(11); + if (GetBits(11) == 0) + break; + ClrBits(1); + } + for (EOLcnt = 1; !EndOfData(pn); EOLcnt++) { + /* we have seen 11 zeros, which implies EOL, + skip possible fill bits too */ + while (1) { + NeedBits(8); + if (GetBits(8)) + break; + ClrBits(8); + } + while (GetBits(1) == 0) + ClrBits(1); + ClrBits(1); /* the eol flag */ + NeedBits(12); + refline = GetBits(1); /* 1D / 2D flag */ + ClrBits(1); + if (GetBits(11)) + break; + ClrBits(11); + } + if (EOLcnt > 1 && EOLcnt != 6 && verbose) + kdError() << "Line " << LineNum << ": bad RTC (" << EOLcnt << " EOLs)\n"; + if (EOLcnt >= 6 || EndOfData(pn)) { + free(run0); + return; + } + if (LineNum == 0 && refline == 0 && verbose) + kdDebug() << "First line is 2-D encoded\n"; + RunLength = 0; + if (LineNum & 1) { + pa = run1; + pb = run0; + } + else { + pa = run0; + pb = run1; + } + thisrun = pa; + EOLcnt = 0; + a0 = 0; + b1 = *pb++; + + if (refline) { + expand1d(); + } + else { + expand2d(EOL2); + } + if (RunLength) + SETVAL(0); + if (a0 != lastx) { + if (verbose) + kdWarning() << "Line " << LineNum << ": length is " + << a0 << " (expected "<< lastx << ")\n"; + while (a0 > lastx) + a0 -= *--pa; + if (a0 < lastx) { + if ((pa - run0) & 1) + SETVAL(0); + SETVAL(lastx - a0); + } + } + SETVAL(0); /* imaginary change at end of line for reference */ + (*df)(thisrun, LineNum++, pn); + } + free(run0); +} + +/* Redefine the "skip to eol" macro. We cannot recover from coding + errors in G4 data */ +#undef SKIP_EOL +#undef eol2lab +#define SKIP_EOL do { \ + if (verbose) \ + kdError() << "Line " << LineNum << ": G4 coding error\n"; \ + free(run0); \ + return; \ +} while (0) +#define eol2lab + +/* Expand group-4 data */ +void +g4expand(struct pagenode *pn, drawfunc df) +{ + int RunLength; /* Length of current run */ + int a0; /* reference element */ + int b1; /* next change on previous line */ + int lastx = pn->size.width();/* copy line width to register */ + pixnum *run0, *run1; /* run length arrays */ + pixnum *thisrun, *pa, *pb; /* pointers into runs */ + t16bits *sp; /* pointer into compressed data */ + t32bits BitAcc; /* bit accumulator */ + int BitsAvail; /* # valid bits in BitAcc */ + int LineNum; /* line number */ + int EOLcnt; + struct tabent *TabEnt; + + sp = pn->data; + BitAcc = 0; + BitsAvail = 0; + /* allocate space for 2 runlength arrays */ + run0 = (pixnum *) malloc(2 * ((lastx+5)&~1) * sizeof(pixnum)); + run1 = run0 + ((lastx+5)&~1); + run1[0] = lastx; /* initial reference line */ + run1[1] = 0; + + for (LineNum = 0; LineNum < pn->rowsperstrip; ) { +#ifdef DEBUG_FAX + printf("\nBitAcc=%08lX, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", LineNum); + fflush(stdout); +#endif + RunLength = 0; + if (LineNum & 1) { + pa = run1; + pb = run0; + } + else { + pa = run0; + pb = run1; + } + thisrun = pa; + a0 = 0; + b1 = *pb++; + expand2d(EOFB); + if (a0 < lastx) { + if ((pa - run0) & 1) + SETVAL(0); + SETVAL(lastx - a0); + } + SETVAL(0); /* imaginary change at end of line for reference */ + (*df)(thisrun, LineNum++, pn); + continue; + EOFB: + NeedBits(13); + if (GetBits(13) != 0x1001 && verbose) + kdError() << "Bad RTC\n"; + break; + } + free(run0); +} + +static const unsigned char zerotab[256] = { + 0x88, 0x07, 0x16, 0x06, 0x25, 0x05, 0x15, 0x05, + 0x34, 0x04, 0x14, 0x04, 0x24, 0x04, 0x14, 0x04, + 0x43, 0x03, 0x13, 0x03, 0x23, 0x03, 0x13, 0x03, + 0x33, 0x03, 0x13, 0x03, 0x23, 0x03, 0x13, 0x03, + 0x52, 0x02, 0x12, 0x02, 0x22, 0x02, 0x12, 0x02, + 0x32, 0x02, 0x12, 0x02, 0x22, 0x02, 0x12, 0x02, + 0x42, 0x02, 0x12, 0x02, 0x22, 0x02, 0x12, 0x02, + 0x32, 0x02, 0x12, 0x02, 0x22, 0x02, 0x12, 0x02, + 0x61, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x31, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x41, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x31, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x51, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x31, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x41, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x31, 0x01, 0x11, 0x01, 0x21, 0x01, 0x11, 0x01, + 0x70, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x50, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x60, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x50, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00 +}; + +#define check(v) do { \ + prezeros = zerotab[v]; \ + postzeros = prezeros & 15; \ + prezeros >>= 4; \ + if (prezeros == 8) { \ + zeros += 8; \ + continue; \ + } \ + if (zeros + prezeros < 11) { \ + empty = 0; \ + zeros = postzeros; \ + continue; \ + } \ + zeros = postzeros; \ + if (empty) \ + EOLcnt++; \ + lines++; \ + empty = 1; \ +} while (0) + +/* count fax lines */ +int +G3count(struct pagenode *pn, int twoD) +{ + t16bits *p = pn->data; + t16bits *end = p + pn->length/sizeof(*p); + int lines = 0; /* lines seen so far */ + int zeros = 0; /* number of consecutive zero bits seen */ + int EOLcnt = 0; /* number of consecutive EOLs seen */ + int empty = 1; /* empty line */ + int prezeros, postzeros; + + while (p < end && EOLcnt < 6) { + t16bits bits = *p++; + check(bits&255); + if (twoD && (prezeros + postzeros == 7)) { + if (postzeros || ((bits & 0x100) == 0)) + zeros--; + } + check(bits>>8); + if (twoD && (prezeros + postzeros == 7)) { + if (postzeros || ((p < end) && ((*p & 1) == 0))) + zeros--; + } + } + return lines - EOLcnt; /* don't count trailing EOLs */ +} diff --git a/kfaxview/libkfaximage/faxexpand.h b/kfaxview/libkfaximage/faxexpand.h new file mode 100644 index 00000000..8da4e8bc --- /dev/null +++ b/kfaxview/libkfaximage/faxexpand.h @@ -0,0 +1,126 @@ +/* Include file for fax routines + Copyright (C) 1990, 1995 Frank D. Cringle. + Copyright (C) 2005 Helge Deller <deller@kde.org> + +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. */ + +#ifndef _faxexpand_h_ +#define _faxexpand_h_ + +#include <sys/types.h> +#include <unistd.h> + +#include <qglobal.h> +#include <qimage.h> + +#define t32bits Q_UINT32 +#define t16bits Q_UINT16 + +typedef t16bits pixnum; + +class pagenode; + +/* drawfunc() points to a function which processes a line of the + expanded image described as a list of run lengths. + run is the base of an array of lengths, starting with a + (possibly empty) white run for line number linenum. + pn points to the page descriptor */ +typedef void (*drawfunc)(pixnum *run, int linenum, class pagenode *pn); + +struct strip { /* tiff strip descriptor */ + off_t offset; /* offset in file */ + off_t size; /* size of this strip */ +}; + + +/* defines for the pagenode member: type */ +#define FAX_TIFF 1 +#define FAX_RAW 2 + +class pagenode { /* compressed page descriptor */ + public: + pagenode(); + ~pagenode() { }; + int nstrips; /* number of strips */ + int rowsperstrip; /* number of rows per strip */ + int stripnum; /* current strip while expanding */ + struct strip *strips; /* array of strips containing fax data in file */ + t16bits *data; /* in-memory copy of strip */ + size_t length; /* length of data */ + QSize size; /* width & height of page in pixels */ + int inverse; /* black <=> white */ + int lsbfirst; /* bit order is lsb first */ + int type; /* Bernd: tiff vs no tiff*/ + int orient; /* orientation - upsidedown, landscape, mirrored */ + int vres; /* vertical resolution: 1 = fine */ + QPoint dpi; /* DPI horz/vert */ + void (*expander)(class pagenode *, drawfunc); + QImage image; + unsigned int bytes_per_line; +}; + +extern class pagenode *firstpage, *lastpage, *thispage; +extern class pagenode defaultpage; + +/* page orientation flags */ +#define TURN_NONE 0 +#define TURN_U 1 +#define TURN_L 2 +#define TURN_M 4 + +/* fsm state codes */ +#define S_Null 0 +#define S_Pass 1 +#define S_Horiz 2 +#define S_V0 3 +#define S_VR 4 +#define S_VL 5 +#define S_Ext 6 +#define S_TermW 7 +#define S_TermB 8 +#define S_MakeUpW 9 +#define S_MakeUpB 10 +#define S_MakeUp 11 +#define S_EOL 12 + +/* state table entry */ +struct tabent { + unsigned char State; + unsigned char Width; /* width of code in bits */ + pixnum Param; /* run length */ +}; + +extern struct tabent MainTable[]; /* 2-D state table */ +extern struct tabent WhiteTable[]; /* White run lengths */ +extern struct tabent BlackTable[]; /* Black run lengths */ + +void MHexpand(class pagenode *pn, drawfunc df); +void g31expand(class pagenode *pn, drawfunc df); +void g32expand(class pagenode *pn, drawfunc df); +void g4expand(class pagenode *pn, drawfunc df); + +unsigned char *getstrip(class pagenode *pn, int strip); +class pagenode *notefile(const char *name); +int notetiff(const char *name); + +/* initialise code tables */ +extern void fax_init_tables(void); + +/* count lines in image */ +extern int G3count(class pagenode *pn, int twoD); + +#endif diff --git a/kfaxview/libkfaximage/faxinit.cpp b/kfaxview/libkfaximage/faxinit.cpp new file mode 100644 index 00000000..aa6166aa --- /dev/null +++ b/kfaxview/libkfaximage/faxinit.cpp @@ -0,0 +1,345 @@ +/* Initialise fax decoder tables + 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 <config.h> + +#include <sys/types.h> +#include "faxexpand.h" + +struct tabent MainTable[128]; +struct tabent WhiteTable[4096]; +struct tabent BlackTable[8192]; + +struct proto { + t16bits code; /* right justified, lsb-first, zero filled */ + t16bits val; /* (pixel count)<<4 + code width */ +}; + +static const struct proto Pass[] = { +{ 0x0008, 4 }, +{ 0, 0 } +}; + +static const struct proto Horiz[] = { +{ 0x0004, 3 }, +{ 0, 0 } +}; + +static const struct proto V0[] = { +{ 0x0001, 1 }, +{ 0, 0 } +}; + +static const struct proto VR[] = { +{ 0x0006, (1<<4)+3 }, +{ 0x0030, (2<<4)+6 }, +{ 0x0060, (3<<4)+7 }, +{ 0, 0 } +}; + +static const struct proto VL[] = { +{ 0x0002, (1<<4)+3 }, +{ 0x0010, (2<<4)+6 }, +{ 0x0020, (3<<4)+7 }, +{ 0, 0 } +}; + +static const struct proto ExtV[] = { +{ 0x0040, 7 }, +{ 0, 0 } +}; + +static const struct proto EOLV[] = { +{ 0x0000, 7 }, +{ 0, 0 } +}; + +static const struct proto MakeUpW[] = { +{ 0x001b, 1029 }, +{ 0x0009, 2053 }, +{ 0x003a, 3078 }, +{ 0x0076, 4103 }, +{ 0x006c, 5128 }, +{ 0x00ec, 6152 }, +{ 0x0026, 7176 }, +{ 0x00a6, 8200 }, +{ 0x0016, 9224 }, +{ 0x00e6, 10248 }, +{ 0x0066, 11273 }, +{ 0x0166, 12297 }, +{ 0x0096, 13321 }, +{ 0x0196, 14345 }, +{ 0x0056, 15369 }, +{ 0x0156, 16393 }, +{ 0x00d6, 17417 }, +{ 0x01d6, 18441 }, +{ 0x0036, 19465 }, +{ 0x0136, 20489 }, +{ 0x00b6, 21513 }, +{ 0x01b6, 22537 }, +{ 0x0032, 23561 }, +{ 0x0132, 24585 }, +{ 0x00b2, 25609 }, +{ 0x0006, 26630 }, +{ 0x01b2, 27657 }, +{ 0, 0 } +}; + +static const struct proto MakeUpB[] = { +{ 0x03c0, 1034 }, +{ 0x0130, 2060 }, +{ 0x0930, 3084 }, +{ 0x0da0, 4108 }, +{ 0x0cc0, 5132 }, +{ 0x02c0, 6156 }, +{ 0x0ac0, 7180 }, +{ 0x06c0, 8205 }, +{ 0x16c0, 9229 }, +{ 0x0a40, 10253 }, +{ 0x1a40, 11277 }, +{ 0x0640, 12301 }, +{ 0x1640, 13325 }, +{ 0x09c0, 14349 }, +{ 0x19c0, 15373 }, +{ 0x05c0, 16397 }, +{ 0x15c0, 17421 }, +{ 0x0dc0, 18445 }, +{ 0x1dc0, 19469 }, +{ 0x0940, 20493 }, +{ 0x1940, 21517 }, +{ 0x0540, 22541 }, +{ 0x1540, 23565 }, +{ 0x0b40, 24589 }, +{ 0x1b40, 25613 }, +{ 0x04c0, 26637 }, +{ 0x14c0, 27661 }, +{ 0, 0 } +}; + +static const struct proto MakeUp[] = { +{ 0x0080, 28683 }, +{ 0x0180, 29707 }, +{ 0x0580, 30731 }, +{ 0x0480, 31756 }, +{ 0x0c80, 32780 }, +{ 0x0280, 33804 }, +{ 0x0a80, 34828 }, +{ 0x0680, 35852 }, +{ 0x0e80, 36876 }, +{ 0x0380, 37900 }, +{ 0x0b80, 38924 }, +{ 0x0780, 39948 }, +{ 0x0f80, 40972 }, +{ 0, 0 } +}; + +static const struct proto TermW[] = { +{ 0x00ac, 8 }, +{ 0x0038, 22 }, +{ 0x000e, 36 }, +{ 0x0001, 52 }, +{ 0x000d, 68 }, +{ 0x0003, 84 }, +{ 0x0007, 100 }, +{ 0x000f, 116 }, +{ 0x0019, 133 }, +{ 0x0005, 149 }, +{ 0x001c, 165 }, +{ 0x0002, 181 }, +{ 0x0004, 198 }, +{ 0x0030, 214 }, +{ 0x000b, 230 }, +{ 0x002b, 246 }, +{ 0x0015, 262 }, +{ 0x0035, 278 }, +{ 0x0072, 295 }, +{ 0x0018, 311 }, +{ 0x0008, 327 }, +{ 0x0074, 343 }, +{ 0x0060, 359 }, +{ 0x0010, 375 }, +{ 0x000a, 391 }, +{ 0x006a, 407 }, +{ 0x0064, 423 }, +{ 0x0012, 439 }, +{ 0x000c, 455 }, +{ 0x0040, 472 }, +{ 0x00c0, 488 }, +{ 0x0058, 504 }, +{ 0x00d8, 520 }, +{ 0x0048, 536 }, +{ 0x00c8, 552 }, +{ 0x0028, 568 }, +{ 0x00a8, 584 }, +{ 0x0068, 600 }, +{ 0x00e8, 616 }, +{ 0x0014, 632 }, +{ 0x0094, 648 }, +{ 0x0054, 664 }, +{ 0x00d4, 680 }, +{ 0x0034, 696 }, +{ 0x00b4, 712 }, +{ 0x0020, 728 }, +{ 0x00a0, 744 }, +{ 0x0050, 760 }, +{ 0x00d0, 776 }, +{ 0x004a, 792 }, +{ 0x00ca, 808 }, +{ 0x002a, 824 }, +{ 0x00aa, 840 }, +{ 0x0024, 856 }, +{ 0x00a4, 872 }, +{ 0x001a, 888 }, +{ 0x009a, 904 }, +{ 0x005a, 920 }, +{ 0x00da, 936 }, +{ 0x0052, 952 }, +{ 0x00d2, 968 }, +{ 0x004c, 984 }, +{ 0x00cc, 1000 }, +{ 0x002c, 1016 }, +{ 0, 0 } +}; + +static const struct proto TermB[] = { +{ 0x03b0, 10 }, +{ 0x0002, 19 }, +{ 0x0003, 34 }, +{ 0x0001, 50 }, +{ 0x0006, 67 }, +{ 0x000c, 84 }, +{ 0x0004, 100 }, +{ 0x0018, 117 }, +{ 0x0028, 134 }, +{ 0x0008, 150 }, +{ 0x0010, 167 }, +{ 0x0050, 183 }, +{ 0x0070, 199 }, +{ 0x0020, 216 }, +{ 0x00e0, 232 }, +{ 0x0030, 249 }, +{ 0x03a0, 266 }, +{ 0x0060, 282 }, +{ 0x0040, 298 }, +{ 0x0730, 315 }, +{ 0x00b0, 331 }, +{ 0x01b0, 347 }, +{ 0x0760, 363 }, +{ 0x00a0, 379 }, +{ 0x0740, 395 }, +{ 0x00c0, 411 }, +{ 0x0530, 428 }, +{ 0x0d30, 444 }, +{ 0x0330, 460 }, +{ 0x0b30, 476 }, +{ 0x0160, 492 }, +{ 0x0960, 508 }, +{ 0x0560, 524 }, +{ 0x0d60, 540 }, +{ 0x04b0, 556 }, +{ 0x0cb0, 572 }, +{ 0x02b0, 588 }, +{ 0x0ab0, 604 }, +{ 0x06b0, 620 }, +{ 0x0eb0, 636 }, +{ 0x0360, 652 }, +{ 0x0b60, 668 }, +{ 0x05b0, 684 }, +{ 0x0db0, 700 }, +{ 0x02a0, 716 }, +{ 0x0aa0, 732 }, +{ 0x06a0, 748 }, +{ 0x0ea0, 764 }, +{ 0x0260, 780 }, +{ 0x0a60, 796 }, +{ 0x04a0, 812 }, +{ 0x0ca0, 828 }, +{ 0x0240, 844 }, +{ 0x0ec0, 860 }, +{ 0x01c0, 876 }, +{ 0x0e40, 892 }, +{ 0x0140, 908 }, +{ 0x01a0, 924 }, +{ 0x09a0, 940 }, +{ 0x0d40, 956 }, +{ 0x0340, 972 }, +{ 0x05a0, 988 }, +{ 0x0660, 1004 }, +{ 0x0e60, 1020 }, +{ 0, 0 } +}; + +static const struct proto ExtH[] = { +{ 0x0100, 9 }, +{ 0, 0 } +}; + +static const struct proto EOLH[] = { +{ 0x0000, 11 }, +{ 0, 0 } +}; + +static void +FillTable(struct tabent *T, int Size, const struct proto *P, int State) +{ + int limit = 1 << Size; + + while (P->val) { + int width = P->val & 15; + int param = P->val >> 4; + int incr = 1 << width; + int code; + for (code = P->code; code < limit; code += incr) { + struct tabent *E = T+code; + E->State = State; + E->Width = width; + E->Param = param; + } + P++; + } +} + +/* initialise the huffman code tables */ +void +fax_init_tables(void) +{ + static bool already_initialized = 0; + if (already_initialized) + return; + + ++already_initialized; + + FillTable(MainTable, 7, Pass, S_Pass); + FillTable(MainTable, 7, Horiz, S_Horiz); + FillTable(MainTable, 7, V0, S_V0); + FillTable(MainTable, 7, VR, S_VR); + FillTable(MainTable, 7, VL, S_VL); + FillTable(MainTable, 7, ExtV, S_Ext); + FillTable(MainTable, 7, EOLV, S_EOL); + FillTable(WhiteTable, 12, MakeUpW, S_MakeUpW); + FillTable(WhiteTable, 12, MakeUp, S_MakeUp); + FillTable(WhiteTable, 12, TermW, S_TermW); + FillTable(WhiteTable, 12, ExtH, S_Ext); + FillTable(WhiteTable, 12, EOLH, S_EOL); + FillTable(BlackTable, 13, MakeUpB, S_MakeUpB); + FillTable(BlackTable, 13, MakeUp, S_MakeUp); + FillTable(BlackTable, 13, TermB, S_TermB); + FillTable(BlackTable, 13, ExtH, S_Ext); + FillTable(BlackTable, 13, EOLH, S_EOL); +} diff --git a/kfaxview/libkfaximage/kfaximage.cpp b/kfaxview/libkfaximage/kfaximage.cpp new file mode 100644 index 00000000..28744923 --- /dev/null +++ b/kfaxview/libkfaximage/kfaximage.cpp @@ -0,0 +1,667 @@ +/* + This file is part of KDE FAX image library + Copyright (c) 2005 Helge Deller <deller@kde.org> + + based on Frank D. Cringle's viewfax package + Copyright (C) 1990, 1995 Frank D. Cringle. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <config.h> + +#include <stdlib.h> + +#include <qimage.h> +#include <qfile.h> + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> + +#include "faxexpand.h" +#include "kfaximage.h" + +static const char FAXMAGIC[] = "\000PC Research, Inc\000\000\000\000\000\000"; +static const char littleTIFF[] = "\x49\x49\x2a\x00"; +static const char bigTIFF[] = "\x4d\x4d\x00\x2a"; + +KFaxImage::KFaxImage( const QString &filename, QObject *parent, const char *name ) + : QObject(parent,name) +{ + KGlobal::locale()->insertCatalogue( QString::fromLatin1("libkfaximage") ); + loadImage(filename); +} + +KFaxImage::~KFaxImage() +{ } + +bool KFaxImage::loadImage( const QString &filename ) +{ + reset(); + + m_filename = filename; + m_errorString = QString::null; + + if (m_filename.isEmpty()) + return false; + + int ok = notetiff(); + if (!ok) { + reset(); + } + return ok == 1; +} + +void KFaxImage::reset() +{ + fax_init_tables(); + m_pagenodes.setAutoDelete(true); + m_pagenodes.clear(); + // do not reset m_errorString and m_filename, since + // they may be needed by calling application +} + +QImage KFaxImage::page( unsigned int pageNr ) +{ + if (pageNr >= numPages()) { + kdDebug() << "KFaxImage::page() called with invalid page number\n"; + return QImage(); + } + pagenode *pn = m_pagenodes.at(pageNr); + GetImage(pn); + return pn->image; +} + +QPoint KFaxImage::page_dpi( unsigned int pageNr ) +{ + if (pageNr >= numPages()) { + kdDebug() << "KFaxImage::page_dpi() called with invalid page number\n"; + return QPoint(0,0); + } + pagenode *pn = m_pagenodes.at(pageNr); + GetImage(pn); + return pn->dpi; +} + +QSize KFaxImage::page_size( unsigned int pageNr ) +{ + if (pageNr >= numPages()) { + kdDebug() << "KFaxImage::page_size() called with invalid page number\n"; + return QSize(0,0); + } + pagenode *pn = m_pagenodes.at(pageNr); + GetImage(pn); + return pn->size; +} + + +pagenode *KFaxImage::AppendImageNode(int type) +{ + pagenode *pn = new pagenode(); + if (pn) { + pn->type = type; + pn->expander = g31expand; + pn->strips = NULL; + pn->size = QSize(1728,2339); + pn->vres = -1; + pn->dpi = KFAX_DPI_FINE; + m_pagenodes.append(pn); + } + return pn; +} + +bool KFaxImage::NewImage(pagenode *pn, int w, int h) +{ + pn->image = QImage( w, h, 1, 2, QImage::systemByteOrder() ); + pn->image.setColor(0, qRgb(255,255,255)); + pn->image.setColor(1, qRgb(0,0,0)); + pn->data = (Q_UINT16*) pn->image.bits(); + pn->bytes_per_line = pn->image.bytesPerLine(); + pn->dpi = KFAX_DPI_FINE; + + return !pn->image.isNull(); +} + +void KFaxImage::FreeImage(pagenode *pn) +{ + pn->image = QImage(); + pn->data = NULL; + pn->bytes_per_line = 0; +} + +void KFaxImage::kfaxerror(const QString& error) +{ + m_errorString = error; + kdError() << "kfaxerror: " << error << endl; +} + + +/* Enter an argument in the linked list of pages */ +pagenode * +KFaxImage::notefile(int type) +{ + pagenode *newnode = new pagenode(); + newnode->type = type; + newnode->size = QSize(1728,0); + return newnode; +} + +static t32bits +get4(unsigned char *p, QImage::Endian endian) +{ + return (endian == QImage::BigEndian) + ? (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, QImage::Endian endian) +{ + return (endian == QImage::BigEndian) ? (p[0]<<8)|p[1] : p[0]|(p[1]<<8); +} + +/* generate pagenodes for the images in a tiff file */ +int +KFaxImage::notetiff() +{ +#define SC(x) (char *)(x) + unsigned char header[8]; + QImage::Endian endian; + t32bits IFDoff; + pagenode *pn = NULL; + QString str; + + QFile file(filename()); + if (!file.open(IO_ReadOnly)) { + kfaxerror(i18n("Unable to open file for reading.")); + return 0; + } + + if (file.readBlock(SC(header), 8) != 8) { + kfaxerror(i18n("Unable to read file header (file too short).")); + return 0; + } + if (memcmp(header, &littleTIFF, 4) == 0) + endian = QImage::LittleEndian; + else if (memcmp(header, &bigTIFF, 4) == 0) + endian = QImage::BigEndian; + else { + maybe_RAW_FAX: + kfaxerror(i18n("This is not a TIFF FAX file.")); + // AppendImageNode(FAX_RAW); + return 0; + } + IFDoff = get4(header+4, endian); + if (IFDoff & 1) { + goto maybe_RAW_FAX; + } + do { /* for each page */ + unsigned char buf[8]; + unsigned char *dir = NULL , *dp = NULL; + int ndirent; + pixnum iwidth = 1728; + pixnum iheight = 2339; + int inverse = false; + int lsbfirst = 0; + int t4opt = 0, comp = 0; + int orient = TURN_NONE; + int yres = 196; /* 98.0 */ + struct strip *strips = NULL; + unsigned int rowsperstrip = 0; + t32bits nstrips = 1; + + if (!file.at(IFDoff)) { + realbad: + kfaxerror( i18n("Invalid or incomplete TIFF file.") ); + bad: + if (strips) + free(strips); + if (dir) + free(dir); + file.close(); + return 1; + } + if (file.readBlock(SC(buf), 2) != 2) + goto realbad; + ndirent = get2(buf, endian); + int len = 12*ndirent+4; + dir = (unsigned char *) malloc(len); + if (file.readBlock(SC(dir), len) != len) + 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 *) malloc(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 (!file.at(value)) + goto realbad; + for (count = 0; count < nstrips; count++) { + if (file.readBlock(SC(buf), (ftype == 3) ? 2 : 4) <= 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(filename()).arg(nstrips).arg(count); + kfaxerror(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 (!file.at(value)) + goto realbad; + for (count = 0; count < nstrips; count++) { + if (file.readBlock(SC(buf), (ftype == 3) ? 2 : 4) <= 0) + goto realbad; + strips[count].size = (ftype == 3) ? + get2(buf, endian) : get4(buf, endian); + } + break; + case 283: /* YResolution */ + if (!file.at(value) || + file.readBlock(SC(buf), 8) != 8) + 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 = (yres * 254) / 100; /* *2.54 */ + break; + } + } + IFDoff = get4(dp, endian); + free(dir); + dir = NULL; + if (comp == 5) { + // compression type 5 is LZW compression // XXX + kfaxerror(i18n("Due to patent reasons LZW (Lempel-Ziv & Welch) " + "compressed Fax files cannot be loaded yet.\n")); + goto bad; + } + if (comp < 2 || comp > 4) { + kfaxerror(i18n("This version can only handle Fax files\n")); + goto bad; + } + pn = AppendImageNode(FAX_TIFF); + pn->nstrips = nstrips; + pn->rowsperstrip = nstrips > 1 ? rowsperstrip : iheight; + pn->strips = strips; + pn->size = QSize(iwidth,iheight); + pn->inverse = inverse; + pn->lsbfirst = lsbfirst; + pn->orient = orient; + pn->dpi.setY(yres); + pn->vres = (yres > 150) ? 1:0; /* 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); + file.close(); + return 1; +#undef UC +} + +/* report error and remove bad file from the list */ +void +KFaxImage::badfile(pagenode *pn) +{ + kfaxerror(i18n("%1: Bad Fax File").arg(filename())); + FreeImage(pn); +} + +/* rearrange input bits into t16bits lsb-first chunks */ +static void +normalize(pagenode *pn, int revbits, int swapbytes, size_t length) +{ + t32bits *p = (t32bits *) pn->data; + + kdDebug() << "normalize = " << ((revbits<<1)|swapbytes) << endl; + 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 * +KFaxImage::getstrip(pagenode *pn, int strip) +{ + size_t offset, roundup; + unsigned char *Data; + + union { t16bits s; unsigned char b[2]; } so; +#define ShortOrder so.b[1] + so.s = 1; /* XXX */ + + QFile file(filename()); + if (!file.open(IO_ReadOnly)) { + badfile(pn); + return NULL; + } + + if (pn->strips == NULL) { + offset = 0; + pn->length = file.size(); + } + else if (strip < pn->nstrips) { + offset = pn->strips[strip].offset; + pn->length = pn->strips[strip].size; + } + else { + kfaxerror( i18n("Trying to expand too many strips.") ); + return NULL; + } + + /* round size to full boundary plus t32bits */ + roundup = (pn->length + 7) & ~3; + + Data = (unsigned char *) malloc(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 (!file.at(offset) || + (size_t) file.readBlock((char *)Data, pn->length) != pn->length) { + badfile(pn); + free(Data); + return NULL; + } + file.close(); + + pn->data = (t16bits *) Data; + if (pn->strips == NULL && memcmp(Data, FAXMAGIC, sizeof(FAXMAGIC)-1) == 0) { + /* handle ghostscript / PC Research fax file */ + if (Data[24] != 1 || Data[25] != 0){ + kfaxerror( i18n("Only the first page of the PC Research multipage file will be shown.") ); + } + pn->length -= 64; + pn->vres = Data[29]; + pn->data += 32; + roundup -= 64; + } + + normalize(pn, !pn->lsbfirst, ShortOrder, roundup); + if (pn->size.height() == 0) + pn->size.setHeight( G3count(pn, pn->expander == g32expand) ); + if (pn->size.height() == 0) { + + kfaxerror( i18n("No fax found in file.") ); + badfile(pn); + free(Data); + return NULL; + } + if (pn->strips == NULL) + pn->rowsperstrip = pn->size.height(); + return Data; +} + + +static void +drawline(pixnum *run, int LineNum, pagenode *pn) +{ + t32bits *p, *p1; /* p - current line, p1 - low-res duplicate */ + pixnum *r; /* pointer to run-lengths */ + t32bits pix; /* current pixel value */ + t32bits acc; /* pixel accumulator */ + int nacc; /* number of valid bits in acc */ + int tot; /* total pixels in line */ + int n; + + LineNum += pn->stripnum * pn->rowsperstrip; + if (LineNum >= pn->size.height()) { + if (LineNum == pn->size.height()) + kdError() << "Height exceeded\n"; + return; + } + + p = (t32bits *) pn->image.scanLine(LineNum*(2-pn->vres)); + p1 =(t32bits *)( pn->vres ? 0 : pn->image.scanLine(1+LineNum*(2-pn->vres))); + r = run; + acc = 0; + nacc = 0; + pix = pn->inverse ? ~0 : 0; + tot = 0; + while (tot < pn->size.width()) { + n = *r++; + tot += n; + /* Watch out for buffer overruns, e.g. when n == 65535. */ + if (tot > pn->size.width()) + break; + if (pix) + acc |= (~(t32bits)0 >> nacc); + else if (nacc) + acc &= (~(t32bits)0 << (32 - nacc)); + else + acc = 0; + if (nacc + n < 32) { + nacc += n; + pix = ~pix; + continue; + } + *p++ = acc; + if (p1) + *p1++ = acc; + n -= 32 - nacc; + while (n >= 32) { + n -= 32; + *p++ = pix; + if (p1) + *p1++ = pix; + } + acc = pix; + nacc = n; + pix = ~pix; + } + if (nacc) { + *p++ = acc; + if (p1) + *p1++ = acc; + } +} + +int +KFaxImage::GetPartImage(pagenode *pn, int n) +{ + unsigned char *Data = getstrip(pn, n); + if (Data == 0) + return 3; + pn->stripnum = n; + (*pn->expander)(pn, drawline); + free(Data); + return 1; +} + +int +KFaxImage::GetImage(pagenode *pn) +{ + if (!pn->image.isNull()) + return 1; + + int i; + if (pn->strips == 0) { + + kdDebug() << "Loading RAW fax file " << m_filename << " size=" << pn->size << endl; + + /* raw file; maybe we don't have the height yet */ + unsigned char *Data = getstrip(pn, 0); + if (Data == 0){ + return 0; + } + if (!NewImage(pn, pn->size.width(), (pn->vres ? 1:2) * pn->size.height())) + return 0; + + (*pn->expander)(pn, drawline); + } + else { + /* multi-strip tiff */ + kdDebug() << "Loading MULTI STRIP TIFF fax file " << m_filename << endl; + + if (!NewImage(pn, pn->size.width(), (pn->vres ? 1:2) * pn->size.height())) + return 0; + pn->stripnum = 0; + kdDebug() << "has " << pn->nstrips << " strips.\n"; + for (i = 0; i < pn->nstrips; i++){ + + int k = GetPartImage(pn, i); + if ( k == 3 ){ + FreeImage(pn); + kfaxerror( i18n("Fax G3 format not yet supported.") ); + return k; + } + + } + } + + // byte-swapping the image on little endian machines +#if defined(Q_BYTE_ORDER) && (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) + for (int y=pn->image.height()-1; y>=0; --y) { + Q_UINT32 *source = (Q_UINT32 *) pn->image.scanLine(y); + Q_UINT32 *dest = source; + for (int x=(pn->bytes_per_line/4)-1; x>=0; --x) { + Q_UINT32 dv = 0, sv = *source; + for (int bit=32; bit>0; --bit) { + dv <<= 1; + dv |= sv&1; + sv >>= 1; + } + *dest = dv; + ++dest; + ++source; + } + } +#endif + + kdDebug() << filename() + << "\n\tsize = " << pn->size + << "\n\tDPI = " << pn->dpi + << "\n\tresolution = " << (pn->vres ? "fine" : "normal") + << endl; + + return 1; +} + + +#include "kfaximage.moc" diff --git a/kfaxview/libkfaximage/kfaximage.h b/kfaxview/libkfaximage/kfaximage.h new file mode 100644 index 00000000..0ed2db90 --- /dev/null +++ b/kfaxview/libkfaximage/kfaximage.h @@ -0,0 +1,162 @@ +/* + This file is part of KDE FAX image loading library + Copyright (c) 2005 Helge Deller <deller@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _LIBKFAXIMAGE_H_ +#define _LIBKFAXIMAGE_H_ + +#include <qobject.h> +#include <qstring.h> +#include <qimage.h> +#include <qptrlist.h> +#include <kdelibs_export.h> + +class pagenode; + + +/** + * This is KFaxImage, a class for loading FAX files under KDE + * + * @short KFaxImage + * @author Helge Deller + * @version 0.1 + * + * + * Standard fax dpi values: + * ------------------------ + * Standard: 203 dpi x 98 lpi + * Fine: 203 dpi x 196 lpi + * Super Fine: 203 dpi y 392 lpi, or + * 406 dpi x 392 lpi + */ + +#define KFAX_DPI_STANDARD QPoint(203,98) +#define KFAX_DPI_FINE QPoint(203,196) +#define KFAX_DPI_SUPERFINE QPoint(406,392) + + +class KDE_EXPORT KFaxImage : public QObject +{ + Q_OBJECT + +public: + + /** + * KFaxImage - the KDE FAX loader class + * constructor. + * @param filename: an optional FAX file which should be loaded. + * @see: numPages + */ + + KFaxImage( const QString &filename = QString::null, QObject *parent = 0, const char *name = 0 ); + + /** + * Destructor + * + * releases internal allocated memory. + */ + + virtual ~KFaxImage(); + + /** + * loads the FAX image and returns true when sucessful. + * @param filename: the file which should be loaded. + * @return boolean value on success or failure + * @see: numPages + * @see: errorString + */ + + bool loadImage( const QString &filename ); + + /** + * returns currently loaded image file name. + * @return FAX filename + */ + + QString filename() { return m_filename; }; + + /** + * returns number of pages which are stored in the FAX file + * @return page count + */ + + unsigned int numPages() const { return m_pagenodes.count(); }; + + /** + * returns a QImage which holds the contents of page pageNr + * @param pageNr: page number (starting with 0) + * @return QImage of the page pageNr + */ + + QImage page( unsigned int pageNr ); + + /** + * returns the DPI (dots per inch) of page pageNr + * @param pageNr: page number (starting with 0) + * @return a QPoint value with the DPIs in X- and Y-direction + */ + + QPoint page_dpi( unsigned int pageNr ); + + /** + * returns the physical pixels of page pageNr + * @param pageNr: page number (starting with 0) + * @return a QSize value with the width and height of the image + */ + + QSize page_size( unsigned int pageNr ); + + /** + * @return a user-visible, translated error string + */ + + const QString errorString() const { return m_errorString; }; + + + + private: + + /** + * private member variables + */ + + QString m_filename; + QString m_errorString; + + typedef QPtrList<pagenode> t_PageNodeList; + t_PageNodeList m_pagenodes; + + /** + * private member functions + */ + + void reset(); + void kfaxerror(const QString& error); + pagenode *AppendImageNode(int type); + bool NewImage(pagenode *pn, int w, int h); + void FreeImage(pagenode *pn); + unsigned char *getstrip(pagenode *pn, int strip); + int GetPartImage(pagenode *pn, int n); + int GetImage(pagenode *pn); + pagenode *notefile(int type); + int notetiff(); + void badfile(pagenode *pn); +}; + +#endif /* _LIBKFAXIMAGE_H_ */ + |