okular/generators/fax/faxdocument.cpp
2018-08-31 12:23:45 +03:00

310 lines
8.4 KiB
C++

/***************************************************************************
* Copyright (C) 2008 by Tobias Koenig <tokoe@kde.org> *
* *
* This program 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. *
***************************************************************************/
#include "faxdocument.h"
#include <stdlib.h>
#include <QFile>
#include "faxexpand.h"
static const char FAXMAGIC[] = "\000PC Research, Inc\000\000\000\000\000\000";
#define FAX_DPI_FINE QPoint(203,196)
/* 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;
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);
}
}
}
static bool new_image( pagenode *pn, int width, int height )
{
pn->image = QImage( width, height, QImage::Format_MonoLSB );
pn->image.setColor( 0, qRgb( 255, 255, 255 ) );
pn->image.setColor( 1, qRgb( 0, 0, 0 ) );
pn->bytes_per_line = pn->image.bytesPerLine();
pn->dpi = FAX_DPI_FINE;
pn->imageData = new uchar[ width * height ];
return !pn->image.isNull();
}
/* get compressed data into memory */
static unsigned char* 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( pn->filename );
if ( !file.open( QIODevice::ReadOnly ) )
return nullptr;
if ( pn->strips == nullptr )
{
offset = 0;
pn->length = file.size();
}
else if ( strip < pn->nstrips )
{
offset = pn->strips[ strip ].offset;
pn->length = pn->strips[ strip ].size;
}
else
return nullptr;
/* round size to full boundary plus t32bits */
roundup = (pn->length + 7) & ~3;
data = new uchar[ 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.seek(offset) || (size_t) file.read( (char *)data, pn->length ) != pn->length )
{
delete [] data;
return nullptr;
}
file.close();
pn->data = (t16bits *)data;
if ( pn->strips == nullptr && memcmp( data, FAXMAGIC, sizeof( FAXMAGIC ) - 1 ) == 0 )
{
/* handle ghostscript / PC Research fax file */
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 )
{
delete [] data;
pn->data = nullptr;
return nullptr;
}
if ( pn->strips == nullptr )
pn->rowsperstrip = pn->size.height();
pn->dataOrig = (t16bits *)data;
return data;
}
static void draw_line( 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() )
return;
p = (t32bits *)(pn->imageData + lineNum*(2-pn->vres)*pn->bytes_per_line);
p1 =(t32bits *)(pn->vres ? nullptr : p + pn->bytes_per_line/sizeof(*p));
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;
}
}
static bool get_image( pagenode *pn )
{
unsigned char *data = getstrip( pn, 0 );
if ( !data )
return false;
if ( !new_image( pn, pn->size.width(), (pn->vres ? 1 : 2) * pn->size.height() ) )
return false;
(*pn->expander)( pn, draw_line );
return true;
}
class FaxDocument::Private
{
public:
Private( FaxDocument *parent )
: mParent( parent )
{
mPageNode.size = QSize( 1728, 0 );
}
FaxDocument *mParent;
pagenode mPageNode;
FaxDocument::DocumentType mType;
};
FaxDocument::FaxDocument( const QString &fileName, DocumentType type )
: d( new Private( this ) )
{
d->mPageNode.filename = fileName;
d->mPageNode.strips = nullptr;
d->mPageNode.stripnum = 0;
d->mPageNode.lsbfirst = 0;
d->mPageNode.vres = 1;
d->mPageNode.inverse = 0;
d->mPageNode.data = nullptr;
d->mPageNode.dataOrig = nullptr;
d->mPageNode.imageData = nullptr;
d->mType = type;
if ( d->mType == G3 )
d->mPageNode.expander = g31expand; // or g32expand?!?
else if ( d->mType == G4 )
d->mPageNode.expander = g4expand;
}
FaxDocument::~FaxDocument()
{
delete [] d->mPageNode.dataOrig;
delete [] d->mPageNode.imageData;
delete d;
}
bool FaxDocument::load()
{
fax_init_tables();
bool ok = get_image( &(d->mPageNode) );
if ( !ok )
return false;
// byte-swapping the image
int height = d->mPageNode.size.height();
int bytes_per_line = d->mPageNode.size.width()/8;
QByteArray bytes( height * bytes_per_line, 0 );
for ( int y= height - 1; y >= 0; --y )
{
quint32 offset = y * bytes_per_line;
quint32 *source = (quint32 *) (d->mPageNode.imageData + offset);
quint32 *dest = (quint32 *) (bytes.data() + offset);
for ( int x= (bytes_per_line/4) - 1; x >= 0; --x )
{
quint32 dv = 0, sv = *source;
for ( int bit = 32; bit > 0; --bit )
{
dv <<= 1;
dv |= sv&1;
sv >>= 1;
}
*dest = dv;
++dest;
++source;
}
}
// convert it into a QImage
QImage img( (uchar*)bytes.data(), d->mPageNode.size.width(), d->mPageNode.size.height(), QImage::Format_MonoLSB );
img.setColor( 0, qRgb( 255, 255, 255 ) );
img.setColor( 1, qRgb( 0, 0, 0 ) );
d->mPageNode.image = img.copy().scaled( img.width(), img.height() * 1.5 );
return true;
}
QImage FaxDocument::image() const
{
return d->mPageNode.image;
}