|
|
|
/*
|
|
|
|
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 <tqimage.h>
|
|
|
|
#include <tqfile.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 TQString &filename, TQObject *parent, const char *name )
|
|
|
|
: TQObject(parent,name)
|
|
|
|
{
|
|
|
|
TDEGlobal::locale()->insertCatalogue( TQString::fromLatin1("libkfaximage") );
|
|
|
|
loadImage(filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
KFaxImage::~KFaxImage()
|
|
|
|
{ }
|
|
|
|
|
|
|
|
bool KFaxImage::loadImage( const TQString &filename )
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
|
|
|
|
m_filename = filename;
|
|
|
|
m_errorString = TQString();
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
TQImage KFaxImage::page( unsigned int pageNr )
|
|
|
|
{
|
|
|
|
if (pageNr >= numPages()) {
|
|
|
|
kdDebug() << "KFaxImage::page() called with invalid page number\n";
|
|
|
|
return TQImage();
|
|
|
|
}
|
|
|
|
pagenode *pn = m_pagenodes.at(pageNr);
|
|
|
|
GetImage(pn);
|
|
|
|
return pn->image;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPoint KFaxImage::page_dpi( unsigned int pageNr )
|
|
|
|
{
|
|
|
|
if (pageNr >= numPages()) {
|
|
|
|
kdDebug() << "KFaxImage::page_dpi() called with invalid page number\n";
|
|
|
|
return TQPoint(0,0);
|
|
|
|
}
|
|
|
|
pagenode *pn = m_pagenodes.at(pageNr);
|
|
|
|
GetImage(pn);
|
|
|
|
return pn->dpi;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQSize KFaxImage::page_size( unsigned int pageNr )
|
|
|
|
{
|
|
|
|
if (pageNr >= numPages()) {
|
|
|
|
kdDebug() << "KFaxImage::page_size() called with invalid page number\n";
|
|
|
|
return TQSize(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 = TQSize(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 = TQImage( w, h, 1, 2, TQImage::systemByteOrder() );
|
|
|
|
pn->image.setColor(0, tqRgb(255,255,255));
|
|
|
|
pn->image.setColor(1, tqRgb(0,0,0));
|
|
|
|
pn->data = (TQ_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 = TQImage();
|
|
|
|
pn->data = NULL;
|
|
|
|
pn->bytes_per_line = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KFaxImage::kfaxerror(const TQString& 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 = TQSize(1728,0);
|
|
|
|
return newnode;
|
|
|
|
}
|
|
|
|
|
|
|
|
static t32bits
|
|
|
|
get4(unsigned char *p, TQImage::Endian endian)
|
|
|
|
{
|
|
|
|
return (endian == TQImage::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, TQImage::Endian endian)
|
|
|
|
{
|
|
|
|
return (endian == TQImage::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];
|
|
|
|
TQImage::Endian endian;
|
|
|
|
t32bits IFDoff;
|
|
|
|
pagenode *pn = NULL;
|
|
|
|
TQString str;
|
|
|
|
|
|
|
|
TQFile 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 = TQImage::LittleEndian;
|
|
|
|
else if (memcmp(header, &bigTIFF, 4) == 0)
|
|
|
|
endian = TQImage::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: /* Qt::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 = TQSize(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 */
|
|
|
|
|
|
|
|
TQFile 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 == TQ_LITTLE_ENDIAN)
|
|
|
|
for (int y=pn->image.height()-1; y>=0; --y) {
|
|
|
|
TQ_UINT32 *source = (TQ_UINT32 *) pn->image.scanLine(y);
|
|
|
|
TQ_UINT32 *dest = source;
|
|
|
|
for (int x=(pn->bytes_per_line/4)-1; x>=0; --x) {
|
|
|
|
TQ_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"
|