bool raiseToNextBR(Class2Params&);
bool sendTraining(Class2Params&, int, fxStr& emsg);
bool sendTCF(const Class2Params&, u_int ms);
- bool sendPage(TIFF* tif, const Class2Params&, u_int, u_int, fxStr& emsg);
+ 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 sendPPM(u_int ppm, HDLCFrame& mcf, fxStr& emsg);
params = next;
}
- /*
- * According to T.30 5.3.2.4 we must pause at least 75 ms "after
- * receipt of a signal using the T.30 binary coded modulation" and
- * "before sending any signals using V.27 ter/V.29/V.33/V.17
- * modulation system"
- */
- pause(conf.class1SendMsgDelay);
+ if (params.ec != EC_ENABLE) { // ECM does it later
+ /*
+ * According to T.30 5.3.2.4 we must pause at least 75 ms "after
+ * receipt of a signal using the T.30 binary coded modulation" and
+ * "before sending any signals using V.27 ter/V.29/V.33/V.17
+ * modulation system"
+ */
+ pause(conf.class1SendMsgDelay);
+ }
/*
* The ECM protocol needs to know PPM, so this must be done beforehand...
// add one more flag to ensure one full flag gets transmitted before DLE+ETX
blockData(0x7e, true);
+ // start up the high-speed carrier...
+ if (flowControl == FLOW_XONXOFF)
+ setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
+ pause(conf.class1SendMsgDelay); // T.30 5.3.2.4
+ fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
+ if (!atCmd(tmCmd, AT_CONNECT))
+ return (false);
+ pause(conf.class1TMConnectDelay);
+
// The block is assembled. Transmit it, adding transparent DLEs. End with DLE+ETX.
if (!putModemDLEData(ecmStuffedBlock, ecmStuffedBlockPos, bitrev, getDataTimeout())) {
return (false);
protoTrace(emsg);
return (false);
}
- if (!blockgood || !lastblock) {
- // start up the high-speed carrier...
- if (flowControl == FLOW_XONXOFF)
- setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
- pause(conf.class1SendMsgDelay); // T.30 5.3.2.4
- fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
- if (!atCmd(tmCmd, AT_CONNECT))
- return (false);
- pause(conf.class1TMConnectDelay);
- }
} while (!blockgood);
frameNumber = 0;
if (lastblock) blockNumber = 0;
* Send a page of data.
*/
bool
-Class1Modem::sendPage(TIFF* tif, const Class2Params& params, u_int pageChop, u_int ppmcmd, fxStr& emsg)
+Class1Modem::sendPage(TIFF* tif, Class2Params& params, u_int pageChop, u_int ppmcmd, fxStr& emsg)
{
int lastbyte = 0;
- /*
- * Set high speed carrier & start transfer. If the
- * negotiated modulation technique includes short
- * training, then we use it here (it's used for all
- * high speed carrier traffic other than the TCF).
- */
- fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
- if (!atCmd(tmCmd, AT_CONNECT)) {
- emsg = "Unable to establish message carrier";
- return (false);
+ if (params.ec != EC_ENABLE) { // ECM does it later
+ /*
+ * Set high speed carrier & start transfer. If the
+ * negotiated modulation technique includes short
+ * training, then we use it here (it's used for all
+ * high speed carrier traffic other than the TCF).
+ */
+ fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
+ if (!atCmd(tmCmd, AT_CONNECT)) {
+ emsg = "Unable to establish message carrier";
+ return (false);
+ }
+ // As with TCF, T.31 8.3.3 requires the DCE to report CONNECT at the beginning
+ // of transmission of the training pattern rather than at the end. We pause here
+ // to allow the remote's +FRM to result in CONNECT.
+ pause(conf.class1TMConnectDelay);
+ if (flowControl == FLOW_XONXOFF)
+ setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
}
- // As with TCF, T.31 8.3.3 requires the DCE to report CONNECT at the beginning
- // of transmission of the training pattern rather than at the end. We pause here
- // to allow the remote's +FRM to result in CONNECT.
- pause(conf.class1TMConnectDelay);
bool rc = true;
ecmBlockPos = ecmFramePos = ecmBitPos = ecmOnes = ecmByte = 0;
protoTrace("SEND begin page");
- if (flowControl == FLOW_XONXOFF)
- setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
tstrip_t nstrips = TIFFNumberOfStrips(tif);
if (nstrips > 0) {
+
+ /*
+ * RTFCC may mislead us here, so we temporarily
+ * adjust params.
+ */
+ Class2Params newparams = params;
+ uint16 compression;
+ TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
+ if (compression != COMPRESSION_CCITTFAX4) {
+ uint32 g3opts = 0;
+ TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &g3opts);
+ if ((g3opts & GROUP3OPT_2DENCODING) == DF_2DMR)
+ params.df = DF_2DMR;
+ else
+ params.df = DF_1DMH;
+ } else
+ params.df = DF_2DMMR;
+
/*
* Correct bit order of data if not what modem expects.
*/
/*
* Setup tag line processing.
*/
- bool doTagLine = (params.df != DF_2DMMR) && setupTagLineSlop(params);
+ bool doTagLine = setupTagLineSlop(params);
u_int ts = getTagLineSlop();
/*
* Calculate total amount of space needed to read
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);
+
/*
* Image the tag line, if intended.
*/
u_char* dp;
if (doTagLine) {
- dp = imageTagLine(data+ts, fillorder, params);
- totdata = totdata+ts - (dp-data);
+ u_long totbytes = totdata;
+ dp = imageTagLine(data+ts, fillorder, params, totbytes);
+ // Because the whole image is processed with MMR,
+ // totdata is then determined during encoding.
+ totdata = (params.df == DF_2DMMR) ? totbytes : totdata+ts - (dp-data);
} else
dp = data;
+ if (conf.softRTFCC && params.df != newparams.df) {
+ switch (params.df) {
+ case DF_1DMH:
+ protoTrace("Reading MH-compressed image file");
+ break;
+ case DF_2DMR:
+ protoTrace("Reading MR-compressed image file");
+ break;
+ case DF_2DMMR:
+ protoTrace("Reading MMR-compressed image file");
+ break;
+ }
+ dp = convertPhaseCData(dp, totdata, fillorder, params, newparams);
+ }
+ params = newparams; // revert back
+
/*
* correct broken Phase C (T.4/T.6) data if neccessary
*/
* bytes of data to send. To minimize underrun we
* do this padding in a strip-sized buffer.
*/
- uint32 rowsperstrip;
- TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
- if (rowsperstrip == (uint32) -1)
- TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &rowsperstrip);
u_char* fill = new u_char[minLen*rowsperstrip];
u_char* eoFill = fill + minLen*rowsperstrip;
u_char* fp = fill;
* RTFCC may mislead us here, so we temporarily
* adjust params.
*/
- uint16 senddf = params.df;
+ Class2Params newparams = params;
uint16 compression;
TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
if (compression != COMPRESSION_CCITTFAX4) {
/*
* Setup tag line processing.
*/
- u_int ts = 0;
- bool doTagLine = 0;
- if (compression != COMPRESSION_CCITTFAX4) { // broken in G4
- doTagLine = setupTagLineSlop(params);
- ts = getTagLineSlop();
- }
+ u_int ts = getTagLineSlop();
+ bool doTagLine = setupTagLineSlop(params);
/*
* Calculate total amount of space needed to read
* the image into memory (in its encoded format).
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);
/*
* Image the tag line, if intended, and then
* pass the data to the modem, filtering DLE's
*/
u_char* dp;
if (doTagLine) {
- dp = imageTagLine(data+ts, fillorder, params);
- totdata = totdata+ts - (dp-data);
+ u_long totbytes = totdata;
+ dp = imageTagLine(data+ts, fillorder, params, totbytes);
+ totdata = (params.df == DF_2DMMR) ? totbytes : totdata+ts - (dp-data);
} else
dp = data;
+ if (conf.softRTFCC && !conf.class2RTFCC && params.df != newparams.df) {
+ switch (params.df) {
+ case DF_1DMH:
+ protoTrace("Reading MH-compressed image file");
+ break;
+ case DF_2DMR:
+ protoTrace("Reading MR-compressed image file");
+ break;
+ case DF_2DMMR:
+ protoTrace("Reading MMR-compressed image file");
+ break;
+ }
+ dp = convertPhaseCData(dp, totdata, fillorder, params, newparams);
+ }
+ params = newparams; // revert back
+
/*
* correct broken Phase C (T.4/T.6) data if necessary
*/
rc = putModemDLEData(dp, (u_int) totdata, bitrev, getDataTimeout());
endTimedTransfer();
protoTrace("SENT %u bytes of data", totdata);
-
- params.df = senddf; // revert back
- delete data;
}
return (rc);
}
*pBufSize = endOfData - buf;
return lastbyte;
}
+
+u_char*
+FaxModem::convertPhaseCData(u_char* buf, u_long& totdata, u_int fillorder,
+ const Class2Params& params, const Class2Params& newparams)
+{
+ MemoryDecoder dec(buf, params.pageWidth(), totdata, fillorder, params.is2D(), (params.df == DF_2DMMR));
+ u_char* data = dec.convertDataFormat(newparams);
+ totdata = dec.getCC();
+ return (data);
+}
// tag line support
bool setupTagLineSlop(const Class2Params&);
u_int getTagLineSlop() const;
- u_char* imageTagLine(u_char* buf, u_int fillorder, const Class2Params&);
+ u_char* imageTagLine(u_char* buf, u_int fillorder, const Class2Params&, u_long& totdata);
/*
* 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);
+/*
+ * Convert Phase C data...
+ */
+ u_char* convertPhaseCData(u_char* buf, u_long& totdata, u_int fillorder,
+ const Class2Params& params, const Class2Params& newparams);
public:
enum { // FaxModem::RTNHandling
RTN_RETRANSMIT = 0, // retransmit page after RTN until MCF/MPS
* re-reading the q file, we won't know if the data format was
* requested. So, RTFCC defeats requested data formatting. :-(
*/
- if (class2RTFCC) {
+ if (class2RTFCC || softRTFCC) {
params.df = clientCapabilities.df;
// 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_ENABLE))
+ params.df = DF_2DMR;
+ if (params.df == DF_2DMR && !modem->supports2D())
+ params.df = DF_1DMH;
} else {
if (compression == COMPRESSION_CCITTFAX4) {
if (!clientInfo.getSupportsMMR()) {
/*
* Group 3 Facsimile Writer Support.
+ *
+ * The 2-D encoding functionality was taken from the libtiff 3.5.7 distribution.
*/
#include "G3Encoder.h"
#include "StackBuffer.h"
bitmap = TIFFGetBitRevTable(fillOrder != FILLORDER_MSB2LSB);
data = 0;
bit = 8;
+ firstEOL = true;
}
/*
bit = 8;
}
+static const tableentry horizcode =
+ { 3, 0x1 }; /* 001 */
+static const tableentry passcode =
+ { 4, 0x1 }; /* 0001 */
+static const tableentry vcodes[7] = {
+ { 7, 0x03 }, /* 0000 011 */
+ { 6, 0x03 }, /* 0000 11 */
+ { 3, 0x03 }, /* 011 */
+ { 1, 0x1 }, /* 1 */
+ { 3, 0x2 }, /* 010 */
+ { 6, 0x02 }, /* 0000 10 */
+ { 7, 0x02 } /* 0000 010 */
+};
+
+#define isAligned(p,t) ((((u_long)(p)) & (sizeof (t)-1)) == 0)
+
/*
- * Encode a multi-line raster. We do everything with
- * 1D-data, inserting the appropriate tag bits when
- * 2D-encoding is required.
+ * Find a span of ones or zeros using the supplied
+ * table. The byte-aligned start of the bit string
+ * is supplied along with the start+end bit indices.
+ * The table gives the number of consecutive ones or
+ * zeros starting from the msb and is indexed by byte
+ * value.
+ */
+int
+G3Encoder::findspan(const u_char** bpp, int bs, int be, const u_char* tab)
+{
+ const u_char *bp = *bpp;
+ int bits = be - bs;
+ int n, span;
+
+ /*
+ * Check partial byte on lhs.
+ */
+ if (bits > 0 && (n = (bs & 7))) {
+ span = tab[(*bp << n) & 0xff];
+ if (span > 8-n) /* table value too generous */
+ span = 8-n;
+ if (span > bits) /* constrain span to bit range */
+ span = bits;
+ if (n+span < 8) /* doesn't extend to edge of byte */
+ goto done;
+ bits -= span;
+ bp++;
+ } else
+ span = 0;
+ /*
+ * Scan full bytes for all 1's or all 0's.
+ */
+ while (bits >= 8) {
+ n = tab[*bp];
+ span += n;
+ bits -= n;
+ if (n < 8) /* end of run */
+ goto done;
+ bp++;
+ }
+ /*
+ * Check partial byte on rhs.
+ */
+ if (bits > 0) {
+ n = tab[*bp];
+ span += (n > bits ? bits : n);
+ }
+done:
+ *bpp = bp;
+ return (span);
+}
+
+/*
+ * Find a span of ones or zeros using the supplied
+ * table. The ``base'' of the bit string is supplied
+ * along with the start+end bit indices.
+ */
+int
+G3Encoder::find0span(const u_char* bp, int bs, int be)
+{
+ int32 bits = be - bs;
+ int32 n, span;
+
+ bp += bs>>3;
+ /*
+ * Check partial byte on lhs.
+ */
+ if (bits > 0 && (n = (bs & 7))) {
+ span = zeroruns[(*bp << n) & 0xff];
+ if (span > 8-n) /* table value too generous */
+ span = 8-n;
+ if (span > bits) /* constrain span to bit range */
+ span = bits;
+ if (n+span < 8) /* doesn't extend to edge of byte */
+ return (span);
+ bits -= span;
+ bp++;
+ } else
+ span = 0;
+ if (bits >= 2*8*sizeof (long)) {
+ long* lp;
+ /*
+ * Align to longword boundary and check longwords.
+ */
+ while (!isAligned(bp, long)) {
+ if (*bp != 0x00)
+ return (span + zeroruns[*bp]);
+ span += 8, bits -= 8;
+ bp++;
+ }
+ lp = (long*) bp;
+ while (bits >= 8*sizeof (long) && *lp == 0) {
+ span += 8*sizeof (long), bits -= 8*sizeof (long);
+ lp++;
+ }
+ bp = (u_char*) lp;
+ }
+ /*
+ * Scan full bytes for all 0's.
+ */
+ while (bits >= 8) {
+ if (*bp != 0x00) /* end of run */
+ return (span + zeroruns[*bp]);
+ span += 8, bits -= 8;
+ bp++;
+ }
+ /*
+ * Check partial byte on rhs.
+ */
+ if (bits > 0) {
+ n = zeroruns[*bp];
+ span += (n > bits ? bits : n);
+ }
+ return (span);
+}
+
+int
+G3Encoder::find1span(const u_char* bp, int bs, int be)
+{
+ int32 bits = be - bs;
+ int32 n, span;
+
+ bp += bs>>3;
+ /*
+ * Check partial byte on lhs.
+ */
+ if (bits > 0 && (n = (bs & 7))) {
+ span = oneruns[(*bp << n) & 0xff];
+ if (span > 8-n) /* table value too generous */
+ span = 8-n;
+ if (span > bits) /* constrain span to bit range */
+ span = bits;
+ if (n+span < 8) /* doesn't extend to edge of byte */
+ return (span);
+ bits -= span;
+ bp++;
+ } else
+ span = 0;
+ if (bits >= 2*8*sizeof (long)) {
+ long* lp;
+ /*
+ * Align to longword boundary and check longwords.
+ */
+ while (!isAligned(bp, long)) {
+ if (*bp != 0xff)
+ return (span + oneruns[*bp]);
+ span += 8, bits -= 8;
+ bp++;
+ }
+ lp = (long*) bp;
+ while (bits >= 8*sizeof (long) && *lp == ~0) {
+ span += 8*sizeof (long), bits -= 8*sizeof (long);
+ lp++;
+ }
+ bp = (u_char*) lp;
+ }
+ /*
+ * Scan full bytes for all 1's.
+ */
+ while (bits >= 8) {
+ if (*bp != 0xff) /* end of run */
+ return (span + oneruns[*bp]);
+ span += 8, bits -= 8;
+ bp++;
+ }
+ /*
+ * Check partial byte on rhs.
+ */
+ if (bits > 0) {
+ n = oneruns[*bp];
+ span += (n > bits ? bits : n);
+ }
+ return (span);
+}
+
+/*
+ * Return the offset of the next bit in the range
+ * [bs..be] that is different from the specified
+ * color. The end, be, is returned if no such bit
+ * exists.
+ */
+#define finddiff(_cp, _bs, _be, _color) \
+ (_bs + (_color ? find1span(_cp,_bs,_be) : find0span(_cp,_bs,_be)))
+
+/*
+ * Like finddiff, but also check the starting bit
+ * against the end in case start > end.
+ */
+#define finddiff2(_cp, _bs, _be, _color) \
+ (_bs < _be ? finddiff(_cp,_bs,_be,_color) : _be)
+
+/*
+ * Encode a multi-line raster. For MH and MR we can do everything with
+ * 1D-data, if desired, inserting the appropriate tag bits in MR. For
+ * MMR we must do everything with 2D-data, thus when coding 2D-data a
+ * reference line, rp, is required.
*/
void
-G3Encoder::encode(const void* vp, u_int w, u_int h)
+G3Encoder::encode(const void* vp, u_int w, u_int h, u_char* rp)
{
+#define PIXEL(buf,ix) ((((buf)[(ix)>>3]) >> (7-((ix)&7))) & 1)
u_int rowbytes = howmany(w, 8);
- bool firstEOL = true;
+ const u_char* bp = (const unsigned char*) vp;
while (h-- > 0) {
- if( firstEOL ) // according to T.4 first EOL
- firstEOL = false; // should not be aligned
- else if (bit != 4)
- putBits(0, (bit < 4) ? bit+4 : bit-4); // byte-align other EOLs
- if (is2D)
- putBits((EOL<<1)|1, 12+1);
- else
- putBits(EOL, 12);
- int bs = 0, span;
- const u_char* bp = (const u_char*) vp;
- for (;;) {
- span = findspan(&bp, bs, w, zeroruns); // white span
- putspan(span, TIFFFaxWhiteCodes);
- bs += span;
- if (bs >= w)
- break;
- span = findspan(&bp, bs, w, oneruns); // black span
- putspan(span, TIFFFaxBlackCodes);
- bs += span;
- if (bs >= w)
- break;
+ if (!isG4) { // put the EOL
+ if( firstEOL ) // according to T.4 first EOL
+ firstEOL = false; // should not be aligned
+ else if (bit != 4)
+ putBits(0, (bit < 4) ? bit+4 : bit-4); // byte-align other EOLs
+ if (is2D)
+ if (rp)
+ putBits((EOL<<1)|0, 12+1); // T.4 4.2.2
+ else
+ putBits((EOL<<1)|1, 12+1);
+ else
+ putBits(EOL, 12);
+ }
+ if (rp) { // 2-D line
+ uint32 a0 = 0;
+ uint32 a1 = (PIXEL(bp, 0) != 0 ? 0 : finddiff(bp, 0, w, 0));
+ uint32 b1 = (PIXEL(rp, 0) != 0 ? 0 : finddiff(rp, 0, w, 0));
+ uint32 a2, b2;
+ for (;;) {
+ b2 = finddiff2(rp, b1, w, PIXEL(rp,b1));
+ if (b2 >= a1) {
+ int32 d = b1 - a1;
+ if (!(-3 <= d && d <= 3)) { /* horizontal mode */
+ a2 = finddiff2(bp, a1, w, PIXEL(bp,a1));
+ putcode(horizcode);
+ if (a0+a1 == 0 || PIXEL(bp, a0) == 0) {
+ putspan(a1-a0, TIFFFaxWhiteCodes);
+ putspan(a2-a1, TIFFFaxBlackCodes);
+ } else {
+ putspan(a1-a0, TIFFFaxBlackCodes);
+ putspan(a2-a1, TIFFFaxWhiteCodes);
+ }
+ a0 = a2;
+ } else { /* vertical mode */
+ putcode(vcodes[d+3]);
+ a0 = a1;
+ }
+ } else { /* pass mode */
+ putcode(passcode);
+ a0 = b2;
+ }
+ if (a0 >= w)
+ break;
+ a1 = finddiff(bp, a0, w, PIXEL(bp,a0));
+ b1 = finddiff(rp, a0, w, !PIXEL(bp,a0));
+ b1 = finddiff(rp, b1, w, PIXEL(bp,a0));
+ }
+ memcpy(rp, bp, rowbytes);
+ bp += rowbytes; // advance raster row
+ } else { // 1-D line
+ int bs = 0, span;
+ for (;;) {
+ span = findspan(&bp, bs, w, zeroruns); // white span
+ putspan(span, TIFFFaxWhiteCodes);
+ bs += span;
+ if (bs >= w)
+ break;
+ span = findspan(&bp, bs, w, oneruns); // black span
+ putspan(span, TIFFFaxBlackCodes);
+ bs += span;
+ if (bs >= w)
+ break;
+ }
}
- vp = (const u_char*)vp + rowbytes;
+ }
+#undef PIXEL
+}
+
+void
+G3Encoder::encoderCleanup()
+{
+ if (isG4) {
+ putBits(EOL, 12);
+ putBits(EOL, 12);
}
if (bit != 8) // flush partial byte
flushBits();
flushBits();
}
-/*
- * Find a span of ones or zeros using the supplied
- * table. The byte-aligned start of the bit string
- * is supplied along with the start+end bit indices.
- * The table gives the number of consecutive ones or
- * zeros starting from the msb and is indexed by byte
- * value.
- */
-int
-G3Encoder::findspan(const u_char** bpp, int bs, int be, const u_char* tab)
-{
- const u_char *bp = *bpp;
- int bits = be - bs;
- int n, span;
-
- /*
- * Check partial byte on lhs.
- */
- if (bits > 0 && (n = (bs & 7))) {
- span = tab[(*bp << n) & 0xff];
- if (span > 8-n) /* table value too generous */
- span = 8-n;
- if (span > bits) /* constrain span to bit range */
- span = bits;
- if (n+span < 8) /* doesn't extend to edge of byte */
- goto done;
- bits -= span;
- bp++;
- } else
- span = 0;
- /*
- * Scan full bytes for all 1's or all 0's.
- */
- while (bits >= 8) {
- n = tab[*bp];
- span += n;
- bits -= n;
- if (n < 8) /* end of run */
- goto done;
- bp++;
- }
- /*
- * Check partial byte on rhs.
- */
- if (bits > 0) {
- n = tab[*bp];
- span += (n > bits ? bits : n);
- }
-done:
- *bpp = bp;
- return (span);
-}
-
const u_char G3Encoder::zeroruns[256] = {
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
};
-
-/*
- * Return the offset of the next bit in the range
- * [bs..be] that is different from bs. The end,
- * be, is returned if no such bit exists.
- */
-int
-G3Encoder::finddiff(const u_char* cp, int bs, int be)
-{
- cp += bs >> 3; /* adjust byte offset */
- return (bs + findspan(&cp, bs, be,
- (*cp & (0x80 >> (bs&7))) ? oneruns : zeroruns));
-}
private:
bool is2D; // data is to be 1d/2d-encoded
bool isG4; // data is to be G4-encoded
+ bool firstEOL; // first EOL is not byte-aligned
const u_char* bitmap; // bit reversal table
short data; // current input/output byte
short bit; // current bit in input/output byte
static const u_char oneruns[256];
static int findspan(const u_char**, int, int, const u_char*);
- static int finddiff(const u_char*, int, int);
+ static int find0span(const u_char*, int, int);
+ static int find1span(const u_char*, int, int);
+ static int finddiff(const u_char*, int, int, int);
void putBits(u_int bits, u_int length);
void putcode(const tableentry& te);
virtual ~G3Encoder();
void setupEncoder(u_int fillOrder, bool, bool);
- void encode(const void* raster, u_int w, u_int h);
+ void encode(const void* raster, u_int w, u_int h, u_char* rp = NULL);
+ void encoderCleanup();
};
#endif /* _G3Encoder_ */
return (*(endOfData - 1));
}
+void
+MemoryDecoder::invalidCode(const char* type, int x)
+{
+ printf("Invalid %s code word, x %d\n", type, x);
+}
+void
+MemoryDecoder::badPixelCount(const char* type, int got, int expected)
+{
+ if (!seenRTC())
+ printf("Bad %s pixel count, got %d, expected %d\n",
+ type, got, expected);
+}
+void
+MemoryDecoder::badDecodingState(const char* type, int x)
+{
+ printf("Panic, bad %s decoding state, x %d\n", type, x);
+}
+
static bool
isBlank(tiff_runlen_t* runs, u_int rowpixels)
{
(void)decodeRow(rowBuf, width);
/*
* syncronize to the next EOL and calculate pointer to it
- * (see detailed explanation of look_ahead in TagLine.c++)
+ * (see detailed explanation of look_ahead in encodeTagLine())
*/
(void)isNextRow1D();
u_int look_ahead = roundup(getPendingBits(),8) / 8;
u_int decoded = current() - look_ahead - start;
enc.encode(rowBuf, width, 1);
+ enc.encoderCleanup();
u_int encoded = result.getLength();
while( encoded < decoded ){
if(!RTCraised()) {
/*
* syncronize to the next EOL and calculate pointer to it
- * (see detailed explanation of look_ahead in TagLine.c++)
+ * (see detailed explanation of look_ahead in encodeTagLine())
*/
(void)isNextRow1D();
u_int look_ahead = roundup(getPendingBits(),8) / 8;
endOfData--; // step back over the first byte of EOFB
return endOfData;
}
+
+u_char* MemoryDecoder::encodeTagLine(u_long* raster, u_int th, u_int slop)
+{
+ /*
+ * Decode (and discard) the top part of the page where
+ * the tag line is to be imaged. Note that we assume
+ * the strip of raw data has enough scanlines in it
+ * to satisfy our needs (caller is responsible).
+ *
+ * ... and then...
+ *
+ * Encode the result according to the parameters of
+ * the outgoing page. Note that the encoded data is
+ * written in the bit order of the page data since
+ * it must be merged back with it below.
+ */
+ fxStackBuffer result;
+ G3Encoder enc(result);
+ enc.setupEncoder(fillorder, is2D, isG4);
+
+ u_char* start = current();
+ decode(NULL, width, th); // discard decoded data
+ if (!isG4) {
+ /*
+ * If the source is 2D-encoded and the decoding done
+ * above leaves us at a row that is 2D-encoded, then
+ * our re-encoding below will generate a decoding
+ * error if we don't fix things up. Thus we discard
+ * up to the next 1D-encoded scanline. (We could
+ * instead decode the rows and re-encoded them below
+ * but to do that would require decoding above instead
+ * of skipping so that the reference line for the
+ * 2D-encoded rows is available.)
+ */
+ u_int n;
+ for (n = 0; n < 4 && !isNextRow1D(); n++)
+ decodeRow(NULL, width);
+ th += n; // compensate for discarded rows
+ /*
+ * Things get tricky trying to identify the last byte in
+ * the decoded data that we want to replace. The decoder
+ * must potentially look ahead to see the zeros that
+ * makeup the EOL that marks the end of the data we want
+ * to skip over. Consequently current() must be
+ * adjusted by the look ahead, a factor of the number of
+ * bits pending in the G3 decoder's bit accumulator.
+ */
+ u_int look_ahead = roundup(getPendingBits(),8) / 8;
+ u_int decoded = current() - look_ahead - bp;
+ enc.encode(raster, width, th);
+ enc.encoderCleanup();
+ delete raster;
+ /*
+ * To properly join the newly encoded data and the previous
+ * data we need to insert two bytes of zero-fill prior to
+ * the start of the old data to ensure 11 bits of zero exist
+ * prior to the EOL code in the first line of data that
+ * follows what we skipped over above. Note that this
+ * assumes the G3 decoder always stops decoding prior to
+ * an EOL code and that we've adjusted the byte count to the
+ * start of the old data so that the leading bitstring is
+ * some number of zeros followed by a 1.
+ */
+ result.put((char) 0);
+ result.put((char) 0);
+ /*
+ * Copy the encoded raster with the tag line back to
+ * the front of the buffer that was passed in. The
+ * caller has preallocated a hunk of space for us to
+ * do this and we also reuse space occupied by the
+ * original encoded raster image. If insufficient space
+ * exists for the newly encoded tag line, then we jam
+ * as much as will fit w/o concern for EOL markers;
+ * this will cause at most one bad row to be received
+ * at the receiver (got a better idea?).
+ */
+ u_int encoded = result.getLength();
+ if (encoded > slop + decoded)
+ encoded = slop + decoded;
+ u_char* dst = bp + (int)(decoded-encoded);
+ memcpy(dst, (const unsigned char*) result, encoded);
+ return (dst);
+ } else {
+ u_char* refrow = new u_char[byteWidth*sizeof(u_char)]; // reference row
+ memset(refrow, 0, byteWidth*sizeof(u_char)); // clear to white
+ enc.encode(raster, width, th, (unsigned char*) refrow);
+ /*
+ * refrow does not need to be altered now to match the
+ * last line of raster because the raster contains MARGIN_BOT
+ * blank lines.
+ */
+ delete raster;
+ if (!RTCraised()) {
+ for (;;) {
+ (void) decodeRow(rowBuf, width);
+ if(seenRTC())
+ break;
+ enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
+ 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);
+ }
+}
+
+u_char* MemoryDecoder::convertDataFormat(const Class2Params& params)
+{
+ /*
+ * Convert data to the format specified in params. The decoder has already
+ * 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));
+
+ 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;
+
+ 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;
+ }
+ k--;
+ } else { // DF_1DMH
+ enc.encode(rowBuf, width, 1);
+ }
+ 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);
+}
u_char* rowBuf;
int decodeNextByte();
+ void invalidCode(const char* type, int x);
+ void badPixelCount(const char* type, int got, int expected);
+ void badDecodingState(const char* type, int x);
public:
MemoryDecoder(u_char* data, u_long cc);
MemoryDecoder(u_char* data, u_int wid, u_long n,
void fixFirstEOL();
u_char* cutExtraRTC();
u_char* cutExtraEOFB();
+ u_char* encodeTagLine (u_long* raster, u_int th, u_int slop);
+ u_char* convertDataFormat(const Class2Params& params);
int getLastByte();
void scanPageForBlanks(u_int fillorder, const Class2Params& params);
const u_char* getEndOfPage() { return endOfData; }
u_int getLastBlanks() { return nblanks; }
+ u_long getCC() { return cc; }
};
#endif
recvDataFormat = DF_ALL; // default to no transcoding
rtnHandling = FaxModem::RTN_RETRANSMIT; // retransmit until MCF/MPS
saveUnconfirmedPages = true; // keep unconfirmed pages
+ softRTFCC = false; // real-time fax comp. conv. (software)
}
void
class2UseHex = getBoolean(value);
else if (streq(tag, "class2rtfcc"))
class2RTFCC = getBoolean(value);
+ else if (streq(tag, "modemsoftrtfcc"))
+ softRTFCC = getBoolean(value);
else if (streq(tag, "saveunconfirmedpages"))
saveUnconfirmedPages = getBoolean(value);
else
u_int percentGoodLines; // required % of good lines in page
u_int maxConsecutiveBadLines; // max consecutive bad lines in page
u_int minSpeed; // minimum speed for fax transmits
+ bool softRTFCC; // real-time fax compression conversion (software)
bool waitForConnect; // modem sends multiple answer responses
fxStr tagLineFmt; // format string for tag lines
fxStr tagLineFontFile; // font file for imaging tag lines
*/
#include "FaxServer.h"
#include "PCFFont.h"
-#include "G3Encoder.h"
#include "StackBuffer.h"
#include "FaxFont.h"
#include "FaxRequest.h"
}
}
-/*
- * A memory-based G3 decoder--does no checking for end-of-data; it
- * assumes there'll be enough to satisfy the decoding requests.
- */
-class TagLineMemoryDecoder : public G3Decoder {
-private:
- const u_char* bp;
- int decodeNextByte();
-public:
- TagLineMemoryDecoder(const u_char* data);
- ~TagLineMemoryDecoder();
- const u_char* current() { return bp; }
-};
-TagLineMemoryDecoder::TagLineMemoryDecoder(const u_char* data) : bp(data) {}
-TagLineMemoryDecoder::~TagLineMemoryDecoder() {}
-int TagLineMemoryDecoder::decodeNextByte() { return *bp++; }
-
-#ifdef roundup
-#undef roundup
-#endif
-#define roundup(a,b) ((((a)+((b)-1))/(b))*(b))
+#include "MemoryDecoder.h"
/*
* Image the tag line in place of the top few lines of the page
* setup the current page number.
*/
u_char*
-FaxModem::imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
+FaxModem::imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params, u_long& totdata)
{
u_int l;
/*
* because the number of consecutive 2D-encoded rows is bounded
* by the K parameter in the CCITT spec.
*/
- u_int lpr = howmany(w,32); // longs/raster row
+ u_int lpr = howmany(w, sizeof(u_long)*8); // longs/raster row
u_long* raster = new u_long[(h+SLOP_LINES)*lpr]; // decoded raster
memset(raster,0,(h+SLOP_LINES)*lpr*sizeof (u_long));// clear raster to white
/*
(void) tagLineFont->imageText(tagField, (u_short*) raster, w, h,
xoff, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOT);
}
- /*
- * Decode (and discard) the top part of the page where
- * the tag line is to be imaged. Note that we assume
- * the strip of raw data has enough scanlines in it
- * to satisfy our needs (caller is responsible).
- */
- TagLineMemoryDecoder dec(buf);
- dec.setupDecoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
- tiff_runlen_t runs[2*4864]; // run arrays for cur+ref rows
- dec.setRuns(runs, runs+4864, w);
-
- dec.decode(NULL, w, th); // discard decoded data
- /*
- * If the source is 2D-encoded and the decoding done
- * above leaves us at a row that is 2D-encoded, then
- * our re-encoding below will generate a decoding
- * error if we don't fix things up. Thus we discard
- * up to the next 1D-encoded scanline. (We could
- * instead decode the rows and re-encoded them below
- * but to do that would require decoding above instead
- * of skipping so that the reference line for the
- * 2D-encoded rows is available.)
- */
- u_int n;
- for (n = 0; n < 4 && !dec.isNextRow1D(); n++)
- dec.decodeRow(NULL, w);
- th += n; // compensate for discarded rows
- /*
- * Things get tricky trying to identify the last byte in
- * the decoded data that we want to replace. The decoder
- * must potentially look ahead to see the zeros that
- * makeup the EOL that marks the end of the data we want
- * to skip over. Consequently dec.current() must be
- * adjusted by the look ahead, a factor of the number of
- * bits pending in the G3 decoder's bit accumulator.
- */
- u_int look_ahead = roundup(dec.getPendingBits(),8) / 8;
- u_int decoded = dec.current() - look_ahead - buf;
/*
* Scale image data as needed (see notes above).
}
memset(l2, 0, MARGIN_BOT*lpr*sizeof (u_long));
}
-
- /*
- * Encode the result according to the parameters of
- * the outgoing page. Note that the encoded data is
- * written in the bit order of the page data since
- * it must be merged back with it below.
- */
- fxStackBuffer result;
- G3Encoder enc(result);
- enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
- enc.encode(raster, w, th);
- delete raster;
- /*
- * To properly join the newly encoded data and the previous
- * data we need to insert two bytes of zero-fill prior to
- * the start of the old data to ensure 11 bits of zero exist
- * prior to the EOL code in the first line of data that
- * follows what we skipped over above. Note that this
- * assumes the G3 decoder always stops decoding prior to
- * an EOL code and that we've adjusted the byte count to the
- * start of the old data so that the leading bitstring is
- * some number of zeros followed by a 1.
- */
- result.put((char) 0);
- result.put((char) 0);
- /*
- * Copy the encoded raster with the tag line back to
- * the front of the buffer that was passed in. The
- * caller has preallocated a hunk of space for us to
- * do this and we also reuse space occupied by the
- * original encoded raster image. If insufficient space
- * exists for the newly encoded tag line, then we jam
- * as much as will fit w/o concern for EOL markers;
- * this will cause at most one bad row to be received
- * at the receiver (got a better idea?).
- */
- u_int encoded = result.getLength();
- if (encoded > tagLineSlop + decoded)
- encoded = tagLineSlop + decoded;
- u_char* dst = buf + (int)(decoded-encoded);
- memcpy(dst, (const unsigned char*)result, encoded);
- return (dst);
+ MemoryDecoder dec(buf, w, totdata, fillorder, params.is2D(), (params.df == DF_2DMMR));
+ u_char* encbuf = dec.encodeTagLine(raster, th, tagLineSlop);
+ totdata = dec.getCC();
+ return (encbuf);
}
#include "Sys.h"
#include "PCFFont.h"
-#include "G3Decoder.h"
-#include "G3Encoder.h"
#include "StackBuffer.h"
#include "FaxFont.h"
#include "tiffio.h"
return (false);
}
-class MemoryDecoder : public G3Decoder {
-private:
- const u_char* bp;
- int row;
-
- int decodeNextByte();
-
- void invalidCode(const char* type, int x);
- void badPixelCount(const char* type, int got, int expected);
- void badDecodingState(const char* type, int x);
-public:
- MemoryDecoder(const u_char* data);
- ~MemoryDecoder();
- const u_char* current() { return bp; }
-
- void setRowNum(int r) { row = r; }
-};
-MemoryDecoder::MemoryDecoder(const u_char* data) { bp = data; }
-MemoryDecoder::~MemoryDecoder() {}
-int MemoryDecoder::decodeNextByte() { return *bp++; }
-void
-MemoryDecoder::invalidCode(const char* type, int x)
-{
- printf("Invalid %s code word, row %lu, x %d\n", type, row, x);
-}
-void
-MemoryDecoder::badPixelCount(const char* type, int got, int expected)
-{
- printf("Bad %s pixel count, row %lu, got %d, expected %d\n",
- type, row, got, expected);
-}
-void
-MemoryDecoder::badDecodingState(const char* type, int x)
-{
- printf("Panic, bad %s decoding state, row %lu, x %d\n", type, row, x);
-}
-
-#ifdef roundup
-#undef roundup
-#endif
-#define roundup(a,b) ((((a)+((b)-1))/(b))*(b))
+#include "MemoryDecoder.h"
/*
* Image the tag line in place of the top few lines of the page
* setup the current page number.
*/
u_char*
-imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
+imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params, u_long& totdata)
{
u_int l;
/*
* longs here to optimize the scaling done below for the
* low res case. This should satisfy the word-alignment.
*/
- u_int lpr = howmany(w,32); // longs/raster row
+ u_int lpr = howmany(w,sizeof(u_long)*8); // longs/raster row
u_long* raster = new u_long[(h+SLOP_LINES)*lpr]; // decoded raster
memset(raster,0,(h+SLOP_LINES)*lpr*sizeof (u_long));// clear raster to white
/*
(void) tagLineFont->imageText(tagField, (u_short*) raster, w, h,
xoff, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOT);
}
- /*
- * Decode (and discard) the top part of the page where
- * the tag line is to be imaged. Note that we assume
- * the strip of raw data has enough scanlines in it
- * to satisfy our needs (caller is responsible).
- */
- MemoryDecoder dec(buf);
- dec.setupDecoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
- tiff_runlen_t runs[2*4864]; // run arrays for cur+ref rows
- dec.setRuns(runs, runs+4864, w);
-
- u_int row;
- for (row = 0; row < th; row++) {
- dec.setRowNum(row);
- dec.decodeRow(NULL, w);
- }
- /*
- * If the source is 2D-encoded and the decoding done
- * above leaves us at a row that is 2D-encoded, then
- * our re-encoding below will generate a decoding
- * error if we don't fix things up. Thus we discard
- * up to the next 1D-encoded scanline. (We could
- * instead decode the rows and re-encoded them below
- * but to do that would require decoding above instead
- * of skipping so that the reference line for the
- * 2D-encoded rows is available.)
- */
- for (; row < th+4 && !dec.isNextRow1D(); row++) {
- dec.setRowNum(row);
- dec.decodeRow(NULL, w);
- }
- th = row; // add in discarded rows
- /*
- * Things get tricky trying to identify the last byte in
- * the decoded data that we want to replace. The decoder
- * must potentially look ahead to see the zeros that
- * makeup the EOL that marks the end of the data we want
- * to skip over. Consequently dec.current() must be
- * adjusted by the look ahead, a factor of the number of
- * bits pending in the G3 decoder's bit accumulator.
- */
- u_int look_ahead = roundup(dec.getPendingBits(),8) / 8;
- u_int decoded = dec.current() - look_ahead - buf;
/*
* Scale image data as needed (see notes above).
}
memset(l2, 0, MARGIN_BOT*lpr*sizeof (u_long));
}
-
- /*
- * Encode the result according to the parameters of
- * the outgoing page. Note that the encoded data is
- * written in the bit order of the page data since
- * it must be merged back with it below.
- */
- fxStackBuffer result;
- G3Encoder enc(result);
- enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
- enc.encode(raster, w, th);
- delete raster;
- /*
- * To properly join the newly encoded data and the previous
- * data we need to insert two bytes of zero-fill prior to
- * the start of the old data to ensure 11 bits of zero exist
- * prior to the EOL code in the first line of data that
- * follows what we skipped over above. Note that this
- * assumes the G3 decoder always stops decoding prior to
- * an EOL code and that we've adjusted the byte count to the
- * start of the old data so that the leading bitstring is
- * some number of zeros followed by a 1.
- */
- result.put((char) 0);
- result.put((char) 0);
- /*
- * Copy the encoded raster with the tag line back to
- * the front of the buffer that was passed in. The
- * caller has preallocated a hunk of space for us to
- * do this and we also reuse space occupied by the
- * original encoded raster image. If insufficient space
- * exists for the newly encoded tag line, then we jam
- * as much as will fit w/o concern for EOL markers;
- * this will cause at most one bad row to be received
- * at the receiver (got a better idea?).
- */
- u_int encoded = result.getLength();
- if (encoded > tagLineSlop + decoded)
- encoded = tagLineSlop + decoded;
- u_char* dst = buf + (int)(decoded-encoded);
- memcpy(dst, (const unsigned char*)result, encoded);
- return (dst);
+ MemoryDecoder dec(buf, w, totdata, fillorder, params.is2D(), (params.df == DF_2DMMR));
+ u_char* encbuf = dec.encodeTagLine(raster, th, tagLineSlop);
+ totdata = dec.getCC();
+ return (encbuf);
}
void
TIFFSetField(otif, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(otif, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(otif, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
- TIFFSetField(otif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
TIFFSetField(otif, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
uint32 r;
TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &r);
TIFFSetField(otif, TIFFTAG_ROWSPERSTRIP, r);
TIFFSetField(otif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(otif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+
+ TIFFSetField(otif, TIFFTAG_COMPRESSION, comp);
uint32 opts = 0;
- TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &opts);
- params.df = (opts & GROUP3OPT_2DENCODING) ? DF_2DMR : DF_1DMH;
- TIFFSetField(otif, TIFFTAG_GROUP3OPTIONS, opts);
+ if (comp != COMPRESSION_CCITTFAX4) {
+ TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &opts);
+ params.df = (opts & GROUP3OPT_2DENCODING) ? DF_2DMR : DF_1DMH;
+ TIFFSetField(otif, TIFFTAG_GROUP3OPTIONS, opts);
+ } else {
+ TIFFGetField(tif, TIFFTAG_GROUP4OPTIONS, &opts);
+ params.df = DF_2DMMR;
+ TIFFSetField(tif, TIFFTAG_GROUP4OPTIONS, opts);
+ }
uint16 o;
if (TIFFGetField(otif, TIFFTAG_ORIENTATION, &o))
TIFFSetField(tif, TIFFTAG_ORIENTATION, o);
bool firstStrip = setupTagLineSlop(params);
u_int ts = tagLineSlop;
for (u_int strip = 0; strip < TIFFNumberOfStrips(tif); strip++) {
- u_int totbytes = (u_int) stripbytecount[strip];
+ u_long totbytes = (u_long) stripbytecount[strip];
if (totbytes > 0) {
u_char* data = new u_char[totbytes+ts];
if (TIFFReadRawStrip(tif, strip, data+ts, totbytes) >= 0) {
u_char* dp;
if (firstStrip) {
+ uint32 rowsperstrip;
+ TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+ if (rowsperstrip == (uint32) -1)
+ TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &rowsperstrip);
/*
* Generate tag line at the top of the page.
*/
- dp = imageTagLine(data+ts, fillorder, params);
- totbytes = totbytes+ts - (dp-data);
+ u_long totdata = totbytes;
+ dp = imageTagLine(data+ts, fillorder, params, totdata);
+ totbytes = (params.df == DF_2DMMR) ? totdata : totbytes+ts - (dp-data);
firstStrip = false;
} else
dp = data;
ModemSoftFlowCmd string \- command for setting software flow control between \s-1DTE\s+1 and \s-1DCE\s+1
ModemSoftResetCmd string \s-1ATZ\s+1 command for doing a soft reset
ModemSoftResetCmdDelay integer \s-13000\s+1 time, in ms, to pause after a soft reset
+ModemSoftRTFCC boolean \s-1No\s+1 enable software-driven real-time fax compression conversion
ModemType string \s-1\fIsee below\fP\s+1 modem type
ModemVerboseResultsCmd string \s-1ATV1\s+1 command for enabling verbose result codes
ModemWaitForConnect boolean \s-1No\s+1 force server to wait for ``\s-1CONNECT\s+1'' response on answer
.B ModemSoftResetCmd
before any further commands are sent to the modem.
.TP
+.B ModemSoftRTFCC
+Whether or not to enable software-driven Real-Time Fax Compression Conversion.
+RTFCC allows HylaFAX to convert the image compression between MH MR and MMR
+formats regardless of how faxq formatted the image file.
+Note that when using RTFCC, the
+compression format of the file will be ignored, thus the ``-1'', ``-2'',
+and ``-3'' options for sendfax, ps2fax, and others will only influence how the
+document is prepared by faxq and will not influence the
+actual negotiated session parameters.
+.B Class2RTFCC
+takes precedence over
+.B ModemSoftRTFCC
+and if both are enabled, then software-driven RTFCC will not be performed
+in favor of the firmware-driven RTFCC.
+.TP
.B ModemType
This parameter must be set to one of: ``Class2'', ``Class2.0'',
or ``Class1'';