Changelog for HylaFAX
+* improve dataformat reporting and handling (27 Sep 2005)
+* add JBIG send support (27 Sep 2005)
* add hasV17Trouble detection on sending (27 Sep 2005)
* improve NSF station ID detection (27 Sep 2005)
* session logging improvements (27 Sep 2005)
*/
@HAVE_PAM@
+/*
+ * JBIG library support
+ */
+@HAVE_JBIG@
+
#endif
LN
LN_S
HAVE_PAM
-PAMLIBS"
+PAMLIBS
+HAVE_JBIG
+LIBJBIG"
VAR2="MACHDEPLIBS
MAKECXXOVERRIDE
else
Note "Disabling PAM support"
fi
+HAVE_JBIG="/*#define HAVE_JBIG 1*/"
+LIBJBIG=""
+if [ "$DISABLE_JBIG" != "yes" ]; then
+ Note "Checking for JBIG library support"
+ CheckForLibrary jbg_enc_init -ljbig &&
+ CheckForIncludeFile jbig.h && {
+ HAVE_JBIG="#define HAVE_JBIG 1"
+ LIBJBIG="-ljbig"
+ }
+ if [ "x$LIBJBIG" = "x" ]; then
+ Note "... not found. Disabling JBIG support"
+ else
+ Note "... found. Enabling JBIG support"
+ fi
+else
+ Note "Disabling JBIG support"
+fi
CheckForLibrary crypt -lc || {
#
# FreeBSD-2.1 in particular needs -lcrypt.
C++FILE = @CXXFILE@
# default definitions for programs--overide them as desired
-LIBS = ${LIBUTIL} @PAMLIBS@
+LIBS = ${LIBUTIL} @PAMLIBS@ @LIBJBIG@
LLDLIBS = ${LIBS} ${LIBTIFF} ${LIBZ} ${LIBREGEX} ${LIBPORT} ${MACHDEPLIBS}
#
# Override this definition to eliminate shared library use.
if (conf.class1ECMSupport) {
modemParams.ec = BIT(EC_DISABLE) | BIT(EC_ENABLE64) | BIT(EC_ENABLE256);
modemParams.df |= BIT(DF_2DMMR);
+ if (conf.class1JBIGSupport) modemParams.df |= BIT(DF_JBIG);
} else
modemParams.ec = BIT(EC_DISABLE);
}
if (conf.class1ECMSupport) {
// JBIG
- if (conf.class1JBIGBasicSupport)
+ if (conf.class1JBIGSupport) {
dis_caps.setBit(FaxParams::BITNUM_JBIG_BASIC, true);
+ dis_caps.setBit(FaxParams::BITNUM_JBIG_L0, true); // JBIG library can handle L0 = 1-Yd
+ }
/* - disabled for now
// JBIG grey/color requires JPEG grey/color
if (conf.class1GreyJBIGSupport || conf.class1ColorJBIGSupport) {
if (conf.class1ColorJBIGSupport)
dis_caps.setBit(FaxParams::BITNUM_FULLCOLOR, true);
*/
-
// JPEG
if (conf.class1GreyJPEGSupport || conf.class1ColorJPEGSupport)
dis_caps.setBit(FaxParams::BITNUM_JPEG, true);
bool sendTCF(const Class2Params&, u_int ms);
bool sendPage(TIFF* tif, Class2Params&, u_int, u_int, fxStr& emsg);
bool sendPageData(u_char* data, u_int cc, const u_char* bitrev, bool ecm, fxStr& emsg);
- bool sendRTC(Class2Params params, u_int ppmcmd, int lastbyte, fxStr& emsg);
+ bool sendRTC(Class2Params params, u_int ppmcmd, int lastbyte, uint32 rowsperstrip, fxStr& emsg);
bool sendPPM(u_int ppm, HDLCFrame& mcf, fxStr& emsg);
bool decodePPM(const fxStr& pph, u_int& ppm, fxStr& emsg);
// reception support
* send all the data they are presented.
*/
bool
-Class1Modem::sendRTC(Class2Params params, u_int ppmcmd, int lastbyte, fxStr& emsg)
+Class1Modem::sendRTC(Class2Params params, u_int ppmcmd, int lastbyte, uint32 rows, fxStr& emsg)
{
+ if (params.df == DF_JBIG) {
+ /*
+ * If we ever needed to send NEWLEN or other JBIG-terminating
+ * markers this is where we would do it.
+ */
+ //u_char newlen[8] = { 0xFF, 0x05,
+ // (rows>>24)&0xFF, (rows>>16)&0xFF, (rows>>8)&0xFF, rows&0xFF,
+ // 0xFF, 0x02 }; // SDNORM is added per spec
+ //return sendClass1ECMData(newlen, 8, rtcRev, true, ppmcmd, emsg);
+ return sendClass1ECMData(NULL, 0, rtcRev, true, ppmcmd, emsg);
+ }
+
// determine the number of trailing zeros on the last byte of data
u_short zeros = 0;
for (short i = 7; i >= 0; i--) {
protoTrace("SEND begin page");
tstrip_t nstrips = TIFFNumberOfStrips(tif);
+ uint32 rowsperstrip = 0;
if (nstrips > 0) {
/*
off += (u_int) sbc;
}
totdata -= pageChop; // deduct trailing white space not sent
- uint32 rowsperstrip;
TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
if (rowsperstrip == (uint32) -1)
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &rowsperstrip);
} else
dp = data;
+ /*
+ * After a page chop rowsperstrip is no longer valid, as the strip will
+ * be shorter. Therefore, convertPhaseCData (for the benefit of JBIG) and
+ * correctPhaseCData deliberately update rowsperstrip.
+ */
+
if (conf.softRTFCC && params.df != newparams.df) {
switch (params.df) {
case DF_1DMH:
protoTrace("Reading MMR-compressed image file");
break;
}
- dp = convertPhaseCData(dp, totdata, fillorder, params, newparams);
+ dp = convertPhaseCData(dp, totdata, fillorder, params, newparams, rowsperstrip);
}
params = newparams; // revert back
/*
* correct broken Phase C (T.4/T.6) data if neccessary
*/
- lastbyte = correctPhaseCData(dp, &totdata, fillorder, params);
+ if (params.df <= DF_2DMMR)
+ lastbyte = correctPhaseCData(dp, &totdata, fillorder, params, rowsperstrip);
/*
* Send the page of data. This is slightly complicated
* modem wants the data in MSB2LSB order, but for now we'll
* avoid the temptation to optimize.
*/
- if (fillorder != FILLORDER_LSB2MSB) {
+ if (params.df <= DF_2DMMR && fillorder != FILLORDER_LSB2MSB) {
TIFFReverseBits(dp, totdata);
lastbyte = frameRev[lastbyte];
}
delete data;
}
if (rc || abortRequested())
- rc = sendRTC(params, ppmcmd, lastbyte, emsg);
+ rc = sendRTC(params, ppmcmd, lastbyte, rowsperstrip, emsg);
protoTrace("SEND end page");
if (params.ec == EC_DISABLE) {
// these were already done by ECM protocol
} else
dp = data;
+ uint32 rows = 0;
if (conf.softRTFCC && !conf.class2RTFCC && params.df != newparams.df) {
switch (params.df) {
case DF_1DMH:
protoTrace("Reading MMR-compressed image file");
break;
}
- dp = convertPhaseCData(dp, totdata, fillorder, params, newparams);
+ dp = convertPhaseCData(dp, totdata, fillorder, params, newparams, rows);
}
/*
* correct broken Phase C (T.4/T.6) data if necessary
*/
- lastByte = correctPhaseCData(dp, &totdata, fillorder, (conf.class2RTFCC ? params : newparams));
+ lastByte = correctPhaseCData(dp, &totdata, fillorder, (conf.class2RTFCC ? params : newparams), rows);
params = newparams; // revert back
case DF_JPEG_COLOR:
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
break;
- case DF_JBIG_BASIC:
+ case DF_JBIG:
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JBIG);
break;
case DF_2DMMR:
}
break;
- case DF_JBIG_BASIC:
+ case DF_JBIG:
{
setupStartPage(tif, params);
+ copyQualityTrace("BIH: Dl %d, D %d, P %d, fill %d", buf[0], buf[1], buf[2], buf[3]);
/*
* Parse JBIG BIH to get the image length.
*
* times the sixth byte, 256 times the seventh byte, and the eighth byte.
*/
u_long framelength = 0;
- //Yd is byte 9, 10, 11, and 12
- framelength = 256*256*256*buf[9-1];
- framelength += 256*256*buf[10-1];
- framelength += 256*buf[11-1];
- framelength += buf[12-1];
-
- protoTrace("RECV: Yd field in BIH is %d", framelength);
+ framelength = 256*256*256*buf[8];
+ framelength += 256*256*buf[9];
+ framelength += 256*buf[10];
+ framelength += buf[11];
/*
- * Senders commonly use 0xFFFF and 0xFFFFFFFF as empty fill. We ignore such large
+ * Senders commonly use 0xFFFF and 0xFFFFFFFF as "maximum Yd". We ignore such large
* values because if we use them and no NEWLEN marker is received, then the page
* length causes problems for viewers (specifically libtiff).
*/
if (framelength < 65535 && framelength > recvEOLCount) recvEOLCount = framelength;
+ copyQualityTrace("BIH: Xd %d, Yd %d, L0 %d, Mx %d, My %d",
+ 256*256*256*buf[4]+256*256*buf[5]+256*buf[6]+buf[7],
+ framelength,
+ 256*256*256*buf[12]+256*256*buf[13]+256*buf[14]+buf[15],
+ buf[16], buf[17]);
+ copyQualityTrace("BIH: fill %d, HITOLO %d, SEQ %d, ILEAVE %d, SMID %d",
+ (buf[18]&0xF0)>>4, (buf[18]&0x8)>>3, (buf[18]&0x4)>>2, (buf[18]&0x2)>>1, buf[18]&0x1);
+ copyQualityTrace("BIH: fill %d, LRLTWO %d, VLENGTH %d, TPDON %d, TPBON %d, DPON %d, DPPRIV %u, DPLAST %u",
+ (buf[19]&0x80)>>7, (buf[19]&0x40)>>6, (buf[19]&0x20)>>5, (buf[19]&0x10)>>4, (buf[19]&0x8)>>3,
+ (buf[19]&0x4)>>2, (buf[19]&0x2)>>1, buf[19]&0x1);
}
break;
}
break;
- case DF_JBIG_BASIC:
- //search for NEWLEN Marker Segment in JBIG Bi-Level Image Data
+ case DF_JBIG:
+ // search for Marker Segments in JBIG Bi-Level Image Data
{
u_long framelength = 0;
- for (u_int i = 0; i < cc-2; i++) {
- if (buf[i] == 0xFF && buf[i+1] == 0x05) {
+ u_int sdnormcount = 0, i = 0;
+ if (seq & 1) i += 20; // skip BIH
+ for (; i < cc; i++) {
+ if (i+1 < cc && buf[i] == 0xFF && buf[i+1] == 0x04) {
+ if (sdnormcount) {
+ copyQualityTrace("Found %d SDNORM Marker Segments in BID", sdnormcount);
+ sdnormcount = 0;
+ }
+ copyQualityTrace("Found ABORT Marker Segment in BID");
+ i += 1;
+ continue;
+ }
+ if (i+7 < cc && buf[i] == 0xFF && buf[i+1] == 0x06) {
+ if (sdnormcount) {
+ copyQualityTrace("Found %d SDNORM Marker Segments in BID", sdnormcount);
+ sdnormcount = 0;
+ }
+ copyQualityTrace("Found ATMOVE Marker Segment in BID, Yat %d, tx %d, ty %d",
+ 256*256*256*buf[i+2]+256*256*buf[i+3]+256*buf[i+4]+buf[i+5], buf[i+6], buf[i+7]);
+ i += 7;
+ continue;
+ }
+ if (i+6 < cc && buf[i] == 0xFF && buf[i+1] == 0x07) {
+ if (sdnormcount) {
+ copyQualityTrace("Found %d SDNORM Marker Segments in BID", sdnormcount);
+ sdnormcount = 0;
+ }
+ u_long clength = 256*256*256*buf[i+2]+256*256*buf[i+3]+256*buf[i+4]+buf[i+5];
+ fxStr comment = "";
+ for (u_long cpos = 0; i+6+cpos < cc && cpos < clength; cpos++) comment.append(buf[i+6+cpos]);
+ copyQualityTrace("Found COMMENT Marker Segment in BID, \"%s\"", (const char*) comment);
+ i = i + clength + 5;
+ continue;
+ }
+ if (i+5 < cc && buf[i] == 0xFF && buf[i+1] == 0x05) {
+ if (sdnormcount) {
+ copyQualityTrace("Found %d SDNORM Marker Segments in BID", sdnormcount);
+ sdnormcount = 0;
+ }
framelength = 256*256*256*buf[i+2];
framelength += 256*256*buf[i+3];
framelength += 256*buf[i+4];
framelength += buf[i+5];
-
- protoTrace("RECV: Found NEWLEN Marker Segment in BID, Yd = %d", framelength);
+ copyQualityTrace("Found NEWLEN Marker Segment in BID, Yd = %d", framelength);
if (framelength < 65535) recvEOLCount = framelength;
+ i += 5;
+ continue;
+ }
+ if (i+1 < cc && buf[i] == 0xFF && buf[i+1] == 0x01) {
+ if (sdnormcount) {
+ copyQualityTrace("Found %d SDNORM Marker Segments in BID", sdnormcount);
+ sdnormcount = 0;
+ }
+ copyQualityTrace("Found RESERVE Marker Segment in BID");
+ i += 1;
+ continue;
}
+ if (i+1 < cc && buf[i] == 0xFF && buf[i+1] == 0x02) {
+ sdnormcount++;
+ i += 1;
+ continue;
+ }
+ if (i+1 < cc && buf[i] == 0xFF && buf[i+1] == 0x03) {
+ if (sdnormcount) {
+ copyQualityTrace("Found %d SDNORM Marker Segments in BID", sdnormcount);
+ sdnormcount = 0;
+ }
+ copyQualityTrace("Found SDRST Marker Segment in BID");
+ i += 1;
+ continue;
+ }
+ if (i+1 < cc && buf[i] == 0xFF && buf[i+1] != 0x00) { // not STUFF
+ if (sdnormcount) {
+ copyQualityTrace("Found %d SDNORM Marker Segments in BID", sdnormcount);
+ sdnormcount = 0;
+ }
+ copyQualityTrace("Found Unknown Marker %#x Segment in BID", buf[i+1]);
+ i +=1;
+ continue;
+ }
+ }
+ if (sdnormcount) {
+ copyQualityTrace("Found %d SDNORM Marker Segments in BID", sdnormcount);
}
}
break;
framelength += buf[i+6];
framewidth = 256*buf[i+7];
framewidth += buf[i+8];
- protoTrace("RECV: Found Start of Frame (SOF) Marker, size: %lu x %lu", framewidth, framelength);
+ copyQualityTrace("Found Start of Frame (SOF) Marker, size: %lu x %lu", framewidth, framelength);
if (framelength < 65535 && framelength > recvEOLCount) recvEOLCount = framelength;
}
if (buf[i] == 0xFF && buf[i+1] == 0xDC) {
framelength = 256*buf[i+4];
framelength += buf[i+5];
- protoTrace("RECV: Found Define Number of Lines (DNL) Marker, lines: %lu", framelength);
+ copyQualityTrace("Found Define Number of Lines (DNL) Marker, lines: %lu", framelength);
if (framelength < 65535) recvEOLCount = framelength;
}
}
return (modemParams.df & BIT(DF_2DMMR)) != 0;
}
+/*
+ * Return whether or not the modem supports JBIG.
+ */
+bool
+FaxModem::supportsJBIG() const
+{
+ return (modemParams.df & BIT(DF_JBIG)) != 0;
+}
+
/*
* Return whether or not received EOLs are byte aligned.
*/
int
FaxModem::correctPhaseCData(u_char* buf, u_long* pBufSize,
- u_int fillorder, const Class2Params& params)
+ u_int fillorder, const Class2Params& params, uint32& rows)
{
u_char* endOfData;
int lastbyte = 0;
MemoryDecoder dec1(buf, params.pageWidth(), *pBufSize, fillorder, params.is2D(), true);
endOfData = dec1.cutExtraEOFB();
lastbyte = dec1.getLastByte();
+ rows = dec1.getRows();
} else {
MemoryDecoder dec1(buf, params.pageWidth(), *pBufSize, fillorder, params.is2D(), false);
dec1.fixFirstEOL();
*/
MemoryDecoder dec2(buf, params.pageWidth(), *pBufSize, fillorder, params.is2D(), false);
endOfData = dec2.cutExtraRTC();
+ rows = dec2.getRows();
}
if( endOfData )
*pBufSize = endOfData - buf;
u_char*
FaxModem::convertPhaseCData(u_char* buf, u_long& totdata, u_int fillorder,
- const Class2Params& params, const Class2Params& newparams)
+ const Class2Params& params, const Class2Params& newparams, uint32& rows)
{
MemoryDecoder dec(buf, params.pageWidth(), totdata, fillorder, params.is2D(), (params.df == DF_2DMMR));
u_char* data = dec.convertDataFormat(newparams);
totdata = dec.getCC();
+ rows = dec.getRows();
return (data);
}
* Correct if neccessary Phase C (T.4/T.6) data (remove extra RTC/EOFB etc.)
*/
int correctPhaseCData(u_char* buf, u_long* pBufSize,
- u_int fillorder, const Class2Params& params);
+ u_int fillorder, const Class2Params& params, uint32& rows);
/*
* Convert Phase C data...
*/
u_char* convertPhaseCData(u_char* buf, u_long& totdata, u_int fillorder,
- const Class2Params& params, const Class2Params& newparams);
+ const Class2Params& params, const Class2Params& newparams, uint32& rows);
public:
enum { // FaxModem::RTNHandling
RTN_RETRANSMIT = 0, // retransmit page after RTN until MCF/MPS
// methods for querying modem capabilities
virtual bool supports2D() const;
virtual bool supportsMMR() const;
+ virtual bool supportsJBIG() const;
virtual bool supportsEOLPadding() const;
virtual bool supportsVRes(float res) const;
virtual bool supportsPageWidth(u_int w, u_int r) const;
* use in pre-formatting documents sent in future conversations.
*/
clientInfo.setSupportsVRes(clientCapabilities.vr);
- clientInfo.setSupports2DEncoding(clientCapabilities.df >= DF_2DMR);
- clientInfo.setSupportsMMR(clientCapabilities.df >= DF_2DMMR);
+ clientInfo.setSupports2DEncoding(clientCapabilities.df & BIT(DF_2DMR));
+ clientInfo.setSupportsMMR(clientCapabilities.df & BIT(DF_2DMMR));
clientInfo.setMaxPageWidthInPixels(clientCapabilities.pageWidth());
clientInfo.setMaxPageLengthInMM(clientCapabilities.pageLength());
traceProtocol("REMOTE best rate %s", clientCapabilities.bitRateName());
traceProtocol("REMOTE max %s", clientCapabilities.pageWidthName());
traceProtocol("REMOTE max %s", clientCapabilities.pageLengthName());
traceProtocol("REMOTE best vres %s", clientCapabilities.bestVerticalResName());
- traceProtocol("REMOTE best format %s", clientCapabilities.dataFormatName());
+ traceProtocol("REMOTE format support: %s", (const char*) clientCapabilities.dataFormatsName());
if (clientCapabilities.ec != EC_DISABLE)
traceProtocol("REMOTE supports %s", clientCapabilities.ecmName());
traceProtocol("REMOTE best %s", clientCapabilities.scanlineTimeName());
* requested. So, RTFCC defeats requested data formatting. :-(
*/
if (class2RTFCC || softRTFCC) {
- params.df = fxmin(params.df, clientCapabilities.df);
+ /*
+ * Determine the "maximum" compression.
+ *
+ * params.df prior to here represents how faxq formatted the image.
+ * clientCapabilities.df represents what formats the remote supports.
+ *
+ * Ignore what faxq did and send with the "highest" (monochrome)
+ * compression that both the modem and the remote supports.
+ */
+ params.df = 0;
+ u_int bits = clientCapabilities.df;
+ bits &= BIT(DF_JBIG+1)-1; // cap at JBIG, only deal with monochrome
+ while (bits) {
+ bits >>= 1;
+ if (bits) params.df++;
+ }
+ if (params.df == DF_JBIG && (!modem->supportsJBIG() || (params.ec == EC_DISABLE)))
+ params.df = DF_2DMMR;
// even if RTFCC supported uncompressed mode (and it doesn't)
// it's likely that the remote was incorrect in telling us it does
if (params.df == DF_2DMRUNCOMP) params.df = DF_2DMR;
// don't let RTFCC cause problems with restricted modems...
if (params.df == DF_2DMMR && (!modem->supportsMMR() || (params.ec == EC_DISABLE)))
params.df = DF_2DMR;
- if (params.df == DF_2DMR && !modem->supports2D())
+ if (params.df == DF_2DMR && (!modem->supports2D() || !(clientCapabilities.df & BIT(DF_2DMR))))
params.df = DF_1DMH;
} else {
if (compression == COMPRESSION_CCITTFAX4) {
#include "MemoryDecoder.h"
#include "G3Encoder.h"
#include "StackBuffer.h"
+#include "config.h"
MemoryDecoder::MemoryDecoder(u_char* data, u_long n)
{
nblanks = 0;
runs = NULL;
rowBuf = NULL;
+ rows = 0;
}
MemoryDecoder::MemoryDecoder(u_char* data, u_int wid, u_long n,
width = wid;
byteWidth = howmany(width, 8);
cc = n;
+ rows = 0;
fillorder = order;
is2D = twoDim;
}
endOfData = NULL;
+ rows = 0;
if(!RTCraised()) {
/*
* syncronize to the next EOL and calculate pointer to it
}
if( seenRTC() )
break;
+ rows++;
}
}
return endOfData;
* MMR requires us to decode the entire image...
*/
endOfData = NULL;
+ rows = 0;
if(!RTCraised()) {
endOfData = current();
for (;;) {
}
if( seenRTC() )
break;
+ rows++;
}
}
if (seenRTC() && *(endOfData - 1) == 0x00)
}
}
+#ifdef HAVE_JBIG
+extern "C" {
+#include "jbig.h"
+}
+fxStackBuffer resultBuffer;
+
+void bufferJBIGData(unsigned char *start, size_t len, void *file)
+{
+ resultBuffer.put((const char*) start, len);
+}
+#endif /* HAVE_JBIG */
+
u_char* MemoryDecoder::convertDataFormat(const Class2Params& params)
{
/*
* been set up, and we don't need to worry about decoder operation here.
* These params are for the encoder to use.
*/
- fxStackBuffer result;
- G3Encoder enc(result);
- enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
+ rows = 0;
+ if (params.df <= DF_2DMMR) {
+ fxStackBuffer result;
+ G3Encoder enc(result);
+ enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
- u_char* refrow = new u_char[byteWidth*sizeof(u_char)]; // reference row
- memset(refrow, 0, byteWidth*sizeof(u_char)); // clear to white
+ u_char* refrow = new u_char[byteWidth*sizeof(u_char)]; // reference row
+ memset(refrow, 0, byteWidth*sizeof(u_char)); // clear to white
- /*
- * For MR we encode a 1-D or 2-D line according to T.4 4.2.1.
- * We understand that "standard" resolution means VR_NORMAL.
- */
- u_short k = 0;
+ /*
+ * For MR we encode a 1-D or 2-D line according to T.4 4.2.1.
+ * We understand that "standard" resolution means VR_NORMAL.
+ */
+ u_short k = 0;
- if (!RTCraised()) {
- for (;;) {
- (void) decodeRow(rowBuf, width);
- if(seenRTC())
- break;
- // encode the line specific to the desired format
- if (params.df == DF_2DMMR) {
- enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
- } else if (params.df == DF_2DMR) {
- if (k) {
- enc.encode(rowBuf, width, 1, (unsigned char*) refrow); // 2-D
- } else {
- enc.encode(rowBuf, width, 1); // 1-D
- k = (params.vr == VR_NORMAL || params.vr == VR_200X100) ? 2 : 4;
+ if (!RTCraised()) {
+ for (;;) {
+ (void) decodeRow(rowBuf, width);
+ if(seenRTC())
+ break;
+ rows++;
+ // encode the line specific to the desired format
+ if (params.df == DF_2DMMR) {
+ enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
+ } else if (params.df == DF_2DMR) {
+ if (k) {
+ enc.encode(rowBuf, width, 1, (unsigned char*) refrow); // 2-D
+ } else {
+ enc.encode(rowBuf, width, 1); // 1-D
+ k = (params.vr == VR_NORMAL || params.vr == VR_200X100) ? 2 : 4;
+ }
+ k--;
+ } else { // DF_1DMH
+ enc.encode(rowBuf, width, 1);
}
- k--;
- } else { // DF_1DMH
- enc.encode(rowBuf, width, 1);
+ memcpy(refrow, rowBuf, byteWidth*sizeof(u_char));
}
- memcpy(refrow, rowBuf, byteWidth*sizeof(u_char));
}
+ enc.encoderCleanup();
+ cc = result.getLength();
+ u_char* dst = new u_char[cc];
+ memcpy(dst, (const unsigned char*) result, cc);
+ return (dst);
+ } else if (params.df == DF_JBIG) {
+#ifdef HAVE_JBIG
+ char* decodedrow = new char[byteWidth];
+ fxStackBuffer raster;
+ resultBuffer = raster; // initialize resultBuffer
+ if (!RTCraised()) {
+ for (;;) {
+ (void) decodeRow(decodedrow, width);
+ if(seenRTC())
+ break;
+ raster.put(decodedrow, byteWidth*sizeof(u_char));
+ rows++;
+ }
+ }
+ delete decodedrow;
+ // bitmap raster is prepared, pass through JBIG encoding...
+ cc = raster.getLength();
+ u_char* rasterdst = new u_char[cc];
+ memcpy(rasterdst, (const unsigned char*) raster, cc);
+ unsigned char *pmap[1] = { rasterdst };
+ struct jbg_enc_state jbigstate;
+ jbg_enc_init(&jbigstate, width, rows, 1, pmap, bufferJBIGData, NULL);
+ /*
+ * T.85 requires "single-progressive sequential coding" and thus:
+ *
+ * Dl = 0, D = 0, P = 1 are required
+ * L0 = 128 is suggested (and appears to be standard among senders)
+ * Mx = 0 to 127 (and 0 appears to be standard)
+ * My = 0, HITOLO = 0, SEQ = 0, ILEAVE = 0, SMID = 0
+ * TPDON = 0, DPON = 0, DPPRIV = 0, DPLAST = 0
+ *
+ * As these settings vary from the library's defaults, we carefully
+ * specify all of them.
+ */
+ jbg_enc_options(&jbigstate, 0, 0, 128, 0, 0);
+ jbg_enc_out(&jbigstate);
+ jbg_enc_free(&jbigstate);
+ delete rasterdst;
+ // image is now encoded into JBIG
+ //resultBuffer[19] |= 0x20; // set VLENGTH = 1, if someday we want to transmit NEWLEN
+ cc = resultBuffer.getLength();
+ u_char* dst = new u_char[cc];
+ memcpy(dst, (const unsigned char*) resultBuffer, cc);
+ return (dst);
+#else
+ printf("Attempt to convert Phase C data to JBIG without JBIG support. This should not happen.\n");
+ return (NULL);
+#endif /* HAVE_JBIG */
}
- enc.encoderCleanup();
- cc = result.getLength();
- u_char* dst = new u_char[cc];
- memcpy(dst, (const unsigned char*) result, cc);
- return (dst);
}
u_int width;
u_int byteWidth;
u_long cc;
+ uint32 rows;
u_int fillorder;
bool is2D, isG4;
u_char* endOfData; // used by cutExtraRTC
const u_char* getEndOfPage() { return endOfData; }
u_int getLastBlanks() { return nblanks; }
u_long getCC() { return cc; }
+ uint32 getRows() { return rows; }
};
#endif
return caps.df & BIT(DF_2DMMR);
}
+/*
+ * Return whether or not the modem supports JBIG.
+ */
+bool
+Modem::supportsJBIG() const
+{
+ return caps.df & BIT(DF_JBIG);
+}
+
/*
* Return whether or not the modem supports the
* specified page length. As above for vertical
bool isCapable(const Job& job) const;
bool supports2D() const; // modem supports 2D-encoded fax
bool supportsMMR() const; // modem supports 2D-MMR encoding
+ bool supportsJBIG() const; // modem supports JBIG encoding
bool supportsVRes(float) const; // modem supports vertical resolution
bool supportsVR(u_int) const; // modem supports VR setting
// modem support fax page width
class1ECMSupport = true; // support for ECM
class1GreyJPEGSupport = false; // support for greyscale JPEG
class1ColorJPEGSupport = false; // support for full color JPEG
- class1JBIGBasicSupport = false; // support for monochrome JBIG
+#ifdef HAVE_JBIG
+ class1JBIGSupport = true; // support for monochrome JBIG
+#else
+ class1JBIGSupport = false; // support for monochrome JBIG
+#endif
class1Resolutions = VR_ALL; // resolutions support
class1PersistentECM = true; // continue to correct
class1TCFRecvHack = false; // historical behavior
class1GreyJPEGSupport = getBoolean(value);
else if (streq(tag, "class1colorjpegsupport"))
class1ColorJPEGSupport = getBoolean(value);
- else if (streq(tag, "class1jbigbasicsupport"))
- class1JBIGBasicSupport = getBoolean(value);
+#ifdef HAVE_JBIG
+ else if (streq(tag, "class1jbigsupport"))
+ class1JBIGSupport = getBoolean(value);
+#endif
else if (streq(tag, "class1persistentecm"))
class1PersistentECM = getBoolean(value);
else if (streq(tag, "class1extendedres"))
u_int class1ECMFrameSize; // ECM frame size for transmission
bool class1GreyJPEGSupport; // Greyscale JPEG support
bool class1ColorJPEGSupport; // Full-color JPEG support
- bool class1JBIGBasicSupport; // Basic (monochrome) JBIG support
+ bool class1JBIGSupport; // monochrome JBIG support
bool class1ECMSupport; // support T.30-A ECM
bool class1PersistentECM; // continue to correct
bool class1TCFRecvHack; // deliberately look for V.21 disconnect
{{"\x80\x00\x80\x48\x00", "Faxphone B640"},
{"\x80\x00\x80\x49\x10", "Fax B100"},
{"\x80\x00\x8A\x49\x10", "Laser Class 9000 Series"},
+ {"\x80\x00\x8A\x48\x00", "Laser Class 2060"},
{NULL}};
{"\x00\xD0\x00", "USC", false },
{"\x00\xE0\x00", "Hiboshi", false },
{"\x00\xF0\x00", "Sumitomo Electric", false },
- {"\x20\x41\x59", "Siemens", false },
{"\x59\x59\x01", NULL, false },
{"\xB4\x00\xB0", "DCE", false },
{"\xB4\x00\xB1", "Hasler", false },
*
* Thus, country code x61 (Korea) turns into x86 (Papua New Guinea),
* code xB5 (USA) turns into xAD (Tunisia), code x26 (China) turns
- * into x64 (Lebanon), and code x3D (France) turns into xBC (Vietnam).
+ * into x64 (Lebanon), code x04 (Germany) turns into x20 (Canada),
+ * and code x3D (France) turns into xBC (Vietnam).
* Therefore, we need to convert these to produce a legible station ID.
*/
+ {"\x20\x41\x59", "Siemens", false },
+ {"\x20\xD1\xC0", "Ferrari Electronic", false },
{"\x64\x00\x00", "unknown - China 00 00", false },
{"\x64\x01\x00", "unknown - China 01 00", false },
{"\x64\x01\x01", "unknown - China 01 01", false },
Class1HFLOCmd string \- Class 1/1.0: command to set hardware flow control
Class1FrameOverhead integer \s-14\s+1 Class 1/1.0: extra bytes in a received \s-1HDLC\s+1 frame
Class1GreyJPEGSupport boolean \s-1No\s+1 Class 1/1.0: to enable grey JPEG fax support
+Class1JBIGSupport boolean \s-1\fIsee below\fP\s+1 Class 1/1.0: to enable monochrome JBIG fax support
Class1NFLOCmd string \- Class 1/1.0: command to set no flow control
Class1RecvAbortOK integer \s-1200\s+1 Class 1/1.0: max wait (ms) for ``\s-1OK\s+1'' after recv abort
Class1RecvIdentTimer integer \s-140000\s+1 Class 1/1.0: max wait (ms) for initial ident frame
.B Class1GreyJPEGSupport
Whether or not to enable support for T.30-E greyscale facsimile
with JPEG compression. This is always enabled if
-.B Class1GreyJPEGSupport
+.B Class1ColorJPEGSupport
is enabled.
.TP
+.B Class1JBIGSupport
+Whether or not to enable support for T.85 monochrome facsimile
+with JBIG compression. This defaults to true if, during the build process
+a compatible JBIG library was found; otherwise
+.B Class1JBIGSupport
+defaults to false.
+.TP
.B Class1HFLOCmd
The command to setup hardware (\s-1RTS/CTS\s+1)
flow control between
/*
* Convert a T.30 DIS to a Class 2 parameter block.
+ *
+ * Note that DIS is a list of *capabilities* and not necessarily maximums.
+ * Thus the values here can be set to bitmaps rather than settings.
*/
void
Class2Params::setFromDIS(FaxParams& dis_caps)
xinfo |= getByte(6) << 0;
setFromDIS(dis, xinfo);
+
+ if (ec != EC_DISABLE) {
+ if (dis_caps.isBitEnabled(FaxParams::BITNUM_JBIG_BASIC)) df |= BIT(DF_JBIG);
+ if (dis_caps.isBitEnabled(FaxParams::BITNUM_JPEG)) df |= BIT(DF_JPEG_GREY);
+ //if (dis_caps.isBitEnabled(FaxParams::BITNUM_JBIG)) df |= BIT(DF_JBIG_GREY);
+ if (dis_caps.isBitEnabled(FaxParams::BITNUM_FULLCOLOR)) {
+ if (df & BIT(DF_JPEG_GREY)) df |= BIT(DF_JPEG_COLOR);
+ //if (df & BIT(DF_JBIG_GREY)) df |= BIT(DF_JBIG_COLOR);
+ }
+ }
}
/*
br = DISbrTab[(dis & DIS_SIGRATE) >> 10];
wd = DISwdTab[(dis & DIS_PAGEWIDTH) >> 6];
ln = DISlnTab[(dis & DIS_PAGELENGTH) >> 4];
+
+ // DF here is a bitmap
+ df = BIT(DF_1DMH); // required support for all G3 facsimile
if ((xinfo & DIS_G4COMP) && (xinfo & DIS_ECMODE)) // MMR requires ECM
- df = DF_2DMMR;
- else if (xinfo & DIS_2DUNCOMP)
- df = DF_2DMRUNCOMP;
- else
- df = DISdfTab[(dis & DIS_2DENCODE) >> 8];
+ df |= BIT(DF_2DMMR);
+ if (xinfo & DIS_2DUNCOMP)
+ df |= BIT(DF_2DMRUNCOMP);
+ if (dis & DIS_2DENCODE)
+ df |= BIT(DF_2DMR);
+
if (xinfo & DIS_ECMODE)
ec = (dis & DIS_FRAMESIZE) ? EC_ENABLE64 : EC_ENABLE256;
else
setFromDCS(dcs, xinfo);
- if (dcs_caps.isBitEnabled(FaxParams::BITNUM_JBIG_BASIC)) df = DF_JBIG_BASIC;
+ if (dcs_caps.isBitEnabled(FaxParams::BITNUM_JBIG_BASIC)) df = DF_JBIG;
+ if (dcs_caps.isBitEnabled(FaxParams::BITNUM_JBIG_L0)) df = DF_JBIG;
if (dcs_caps.isBitEnabled(FaxParams::BITNUM_JPEG)) df = DF_JPEG_GREY;
//if (dcs_caps.isBitEnabled(FaxParams::BITNUM_JBIG)) df = DF_JBIG_GREY;
if (dcs_caps.isBitEnabled(FaxParams::BITNUM_FULLCOLOR)) {
else if (xinfo & DCS_200X400) vr = VR_R8;
else vr = DISvrTab[(dcs & DCS_7MMVRES) >> 9];
}
+
+ // DF here is a setting, not a bitmap, max of DF_2DMMR (JPEG, JBIG set later)
+ if (df & BIT(DF_2DMMR)) df = DF_2DMMR;
+ else if (df & BIT(DF_2DMR)) df = DF_2DMR;
+ else df = DF_1DMH;
+
if (xinfo & DCS_ECMODE)
ec = (xinfo & DCSFRAME_64) ? EC_ENABLE64 : EC_ENABLE256;
else
/*
* DATA FORMAT
*
- * Options: MH (required), MR, and MMR.
+ * Options: MH (required), MR, MMR, JBIG, JPEG.
*
- * ECM support is required for MMR.
+ * ECM support is required for MMR, JBIG, and JPEG.
*
* There are other data format options in T.30
- * such as JPEG, JBIG, T.81, and T.43, but they're
- * not discernable from Class2Params.
+ * such as T.81, and T.43.
*/
if (CHECKPARAM(df, DF_2DMR, isDIS)) setBit(BITNUM_2DMR, true);
if (CHECKPARAM(df, DF_2DMMR, isDIS) && (CHECKPARAM(ec, EC_ENABLE64, isDIS) || CHECKPARAM(ec, EC_ENABLE256, isDIS)))
setBit(BITNUM_2DMMR, true);
+ if (CHECKPARAM(df, DF_JBIG, isDIS) && (CHECKPARAM(ec, EC_ENABLE64, isDIS) || CHECKPARAM(ec, EC_ENABLE256, isDIS)))
+ setBit(BITNUM_JBIG_BASIC, true);
/*
* MINIMUM SCANLINE TIME
"2-D MR", // DF_2DMR
"2-D Uncompressed Mode", // DF_2DMRUNCOMP
"2-D MMR", // DF_2DMMR
- "JBIG Basic", // DF_JBIG_BASIC
+ "JBIG", // DF_JBIG
"JPEG Greyscale", // DF_JPEG_GREY
"JPEG Full-Color" // DF_JPEG_COLOR
};
const char* Class2Params::dataFormatName() const
{ return (dataFormatNames[df]); }
+fxStr
+Class2Params::dataFormatsName()
+{
+ fxStr formats = "MH";
+ if (df & BIT(DF_2DMR)) formats.append(", MR");
+ if (df & BIT(DF_2DMMR)) formats.append(", MMR");
+ if (df & BIT(DF_JBIG)) formats.append(", JBIG");
+ // since color requires greyscale, just say one or the other
+ if (df & BIT(DF_JPEG_COLOR)) formats.append(", JPEG Full-Color");
+ else if (df & BIT(DF_JPEG_GREY)) formats.append(", JPEG Greyscale");
+ return (formats);
+}
+
const char* Class2Params::pageWidthNames[8] = {
"A4 page width (215 mm)",
"B4 page width (255 mm)",
const char* bestVerticalResName() const;
const char* scanlineTimeName() const;
const char* dataFormatName() const;
+ fxStr dataFormatsName();
const char* ecmName() const;
u_int encode() const; // generate encoded params
const u_short DF_2DMR = 1; // 2-D Modified Read
const u_short DF_2DMRUNCOMP = 2; // 2-D Uncompressed Mode
const u_short DF_2DMMR = 3; // 2-D Modified Modified Read
-const u_short DF_JBIG_BASIC = 4; // Single-progression sequential coding (Rec. T.85)
+const u_short DF_JBIG = 4; // Single-progression sequential coding (Rec. T.85)
const u_short DF_JPEG_GREY = 5; // Greyscale JPEG (T.4 Annex E and T.81)
const u_short DF_JPEG_COLOR = 6; // Full-color JPEG (T.4 Annex E and T.81)
const u_short DF_ALL = BIT(DF_2DMMR+1)-1;
return dfNames[6];
return dfNames[5];
}
+ if (isBitSet(78, dcs) || isBitSet(79, dcs))
+ return dfNames[4];
if (isBitSet(31, dcs))
return dfNames[3];
if (isBitSet(26, dcs))
BEGIN { FS="\t";
rates = "2400:4800:7200:9600:12000:14400:16800:19200:21600:24000:26400:28800:31200:33600";
setupMaps(rates, rateMap, brNames);
- datas = "1-D MH:2-D MR:2-D Uncompressed Mode:2-D MMR:JBIG Basic:JPEG Greyscale:JPEG Full-color";
+ datas = "1-D MH:2-D MR:2-D Uncompressed Mode:2-D MMR:JBIG:JPEG Greyscale:JPEG Full-color";
setupMaps(datas, dataMap, dfNames);
setupDateTimeStuff();
if (SINCEDT == "")
return dfNames[6];
return dfNames[5];
}
+ if (isBitSet(78, dcs) || isBitSet(79, dcs))
+ return dfNames[4];
if (isBitSet(31, dcs))
return dfNames[3];
if (isBitSet(26, dcs))
BEGIN { FS="\t";
rates = "2400:4800:7200:9600:12000:14400:16800:19200:21600:24000:26400:28800:31200:33600";
setupMaps(rates, rateMap, brNames);
- datas = "1-D MH:2-D MR:2-D Uncompressed Mode:2-D MMR:JBIG Basic:JPEG Greyscale:JPEG Full-color";
+ datas = "1-D MH:2-D MR:2-D Uncompressed Mode:2-D MMR:JBIG:JPEG Greyscale:JPEG Full-color";
setupMaps(datas, dataMap, dfNames);
callFailed["busy signal"] = 1;
callFailed["unknown pro"] = 1;