]> git.ipfire.org Git - thirdparty/HylaFAX.git/commitdiff
Class2: Add JPEG (colour fax) support
authorAidan Van Dyk <aidan@ifax.com>
Fri, 11 May 2007 15:48:39 +0000 (15:48 +0000)
committerAidan Van Dyk <aidan@ifax.com>
Fri, 11 May 2007 15:48:39 +0000 (15:48 +0000)
From Lee:
| commit d52f97c68ac9e30e21c5bed68589fe384a7ba7db
| Author: Lee Howard <faxguy@howardsilvan.com>
| Date:   Wed Feb 21 19:23:06 2007 +0000
|
|   Adds JPEG (color fax) support to Class 2 driver.
|
|   A JP value needed to be added to Class2Params and the JPEG DF values migrated
|   to the new JPs in order to properly support T.32 as well as T.30... which makes
|   it a rather intrusive work.

and:
| commit 7fd4331e446076fea55da01d735feb82f05f6489
| Author: Lee Howard <faxguy@howardsilvan.com>
| Date:   Tue Feb 27 21:14:44 2007 +0000
|
|   Initialize params.jp in case FAXDCS is not present.

and:
| commit 920ba9f3ed2875c9639cc6ac33788a9f0b50e17b
| Author: Lee Howard <faxguy@howardsilvan.com>
| Date:   Tue Feb 27 21:35:17 2007 +0000
|
|   this would have prevented the faxinfo issue - so we'll do this as a
|       safety net

18 files changed:
CHANGES
config/lucent-mt-20
config/lucent-mt-21
config/mainpine-21
faxd/Class1.c++
faxd/Class2.c++
faxd/Class2.h
faxd/CopyQuality.c++
faxd/FaxModem.c++
faxd/FaxModem.h
faxd/FaxSend.c++
faxd/ModemConfig.c++
faxd/ModemConfig.h
man/hylafax-config.4f
util/Class2Params.c++
util/Class2Params.h
util/class2.h
util/faxinfo.c++

diff --git a/CHANGES b/CHANGES
index adb7636a3aa54230cb6bf459cc173d98593d5937..e191cda8618997d0a618b88eb3bbe23d2eefcc4c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,7 @@
 
 Changelog since HylaFAX 4.3.3
 
+* Add JPEC (colour fax) to Class2 (12 May 2007)
 * Add preliminary Class2 JBIG Support (11 May 2007)
 * Change socklen_t configure/detection for HP-UX 11 (11 May 2007)
 * Faxmail overhaul - make handle documents independantly, just like
index acc1c3a027835007d3a8ca6729e4baeedcee8bec..6412958b8b8719473bdeb9c171f408809f64688d 100644 (file)
@@ -32,10 +32,6 @@ Class2CQCmd:         AT+FCQ=1,0
 # versions that respond to AT+FFC=? with non-zero data support RTFCC
 # Class2RTFCC:         yes
 
-# unfortunately, HylaFAX can't currently send or receive in color
-ModemAnswerCmd:                AT+FCC=,,,,,,,,0;A
-ModemDialCmd:          AT+FCC=,,,,,,,,0;DT%s
-
 # Some firmware revisions (i.e. 1.25 in ZBAs) report MMR support but
 # corrupt the data.  Enabling the following lines should work around this.
 # Compare this against the modem's AT+FCC=? response.
index b3d1f589faea7b0836a6dc238a6dde98a4089a00..0b9479be19db1d599a533c2f9792e57b7e4c4f2e 100644 (file)
@@ -32,11 +32,6 @@ Class2CQCmd:         AT+FCQ=1,0
 # versions that respond to AT+FFC=? with non-zero data support RTFCC
 # Class2RTFCC:         yes
 
-# unfortunately, HylaFAX can't currently send or receive in color
-# Also, the modem's extended resolution support is buggy
-ModemAnswerCmd:                AT+FCC=1,,,,,,,,0;A
-ModemDialCmd:          AT+FCC=1,,,,,,,,0;DT%s
-
 # Some firmware revisions (i.e. 1.25) report MMR support but corrupt the data.
 # Some firmware revisions (i.e. 1.25 and 1.28) report extended resolution support but
 # have trouble with 300x300 and 400x400 resolutions and corrupt DIS for inch resolutions.
index 89e282d12ac01ebedd8a9c98db793b1c8e644cd4..89fd6c89d9d97a7269a7f6d86a6cd5d87fb1a4a3 100644 (file)
@@ -22,10 +22,6 @@ Class2BUGCmd:                AT+FBU=0        # early firmware is buggy
 Class2SendRTC:         yes
 Class2UseHex:          yes
 
-# unfortunately, HylaFAX can't currently send or receive in color
-ModemAnswerCmd:                AT+FCC=,,,,,,,,0;A
-ModemDialCmd:          AT+FCC=,,,,,,,,0;DT%s
-
 # You may want to do something like this to enable CTC in ECM
 # Class2Cmd:           AT+FCLASS=2.1;+FRY=4
 
index f4ee847b868c2c7f50d690699accbed1cafffb44..305141a855f0658b87abe877b5690ee9fe9522de 100644 (file)
@@ -185,6 +185,7 @@ Class1Modem::setupModem(bool isSend)
     modemParams.df = BIT(DF_1DMH) | (conf.class1MRSupport ? BIT(DF_2DMR) : 0);
     modemParams.bf = BF_DISABLE;
     modemParams.st = ST_ALL;
+    modemParams.jp = 0;
     pokeConfig(isSend);
     traceModemParams();
     /*
@@ -262,6 +263,10 @@ Class1Modem::pokeConfig(bool isSend)
                break;
        }
        if (jbigSupported) modemParams.df |= BIT(DF_JBIG);
+       if (conf.class1GreyJPEGSupport || conf.class1ColorJPEGSupport)
+           modemParams.jp |= BIT(JP_GREY);
+       if (conf.class1ColorJPEGSupport)
+           modemParams.jp |= BIT(JP_COLOR);
     } else
        modemParams.ec = BIT(EC_DISABLE);
 }
index f396068cf820d1c662751c13bfaa729b62208e70..6dba38154479cf7c0ca8faf3708c334ca76dcacc 100644 (file)
@@ -33,6 +33,7 @@ Class2Modem::Class2Modem(FaxServer& s, const ModemConfig& c) : FaxModem(s,c)
     hangupCode[0] = '\0';
     serviceType = 0;                   // must be set in derived class
     useExtendedDF = false;             // T.32 Amendment 1 extension for data format is detectable
+    useJP = false;                     // JP +FCC option support is detectable
 }
 
 Class2Modem::~Class2Modem()
@@ -85,7 +86,7 @@ Class2Modem::setupModem(bool isSend)
        return (false);
     }
     /*
-     * Syntax: (vr),(br),(wd),(ln),(df),(ec),(bf),(st)
+     * Syntax: (vr),(br),(wd),(ln),(df),(ec),(bf),(st)[,(jp)]
      * where,
      * vr      vertical resolution
      * br      bit rate
@@ -95,6 +96,7 @@ Class2Modem::setupModem(bool isSend)
      * ec      error correction
      * bf      binary file transfer
      * st      scan time/line
+     * jp      JPEG support (optional)
      */
     if (!parseRange(t30parms, modemParams)) {
        serverTrace("Error parsing " | dccQueryCmd | " response: "
@@ -379,6 +381,7 @@ Class2Modem::setupDCC(bool enableV34, bool enableV17)
     params.ec = getBestECM();
     params.bf = BF_DISABLE;
     params.st = getBestScanlineTime();
+    params.jp = modemParams.jp;
     return class2Cmd(dccCmd, params, true);
 }
 
@@ -393,14 +396,25 @@ Class2Modem::parseClass2Capabilities(const char* cap, Class2Params& params, bool
      * Some modems report capabilities in hex values, others decimal.
      */
     fxStr notation;
-    if (conf.class2UseHex)
-       notation = "%X,%X,%X,%X,%X,%X,%X,%X";
-    else
-       notation = "%d,%d,%d,%d,%d,%d,%d,%d";
-    int n = sscanf(cap, notation,
-       &params.vr, &params.br, &params.wd, &params.ln,
-       &params.df, &params.ec, &params.bf, &params.st);
-    if (n == 8) {
+    if (conf.class2UseHex) {
+       if (useJP) notation = "%X,%X,%X,%X,%X,%X,%X,%X,%X";
+       else notation = "%X,%X,%X,%X,%X,%X,%X,%X";
+    } else {
+       if (useJP) notation = "%d,%d,%d,%d,%d,%d,%d,%d,%d";
+       else notation = "%d,%d,%d,%d,%d,%d,%d,%d";
+    }
+    int n = 0;
+    if (useJP) {
+       n = sscanf(cap, notation,
+           &params.vr, &params.br, &params.wd, &params.ln,
+           &params.df, &params.ec, &params.bf, &params.st, &params.jp);
+    } else {
+       n = sscanf(cap, notation,
+           &params.vr, &params.br, &params.wd, &params.ln,
+           &params.df, &params.ec, &params.bf, &params.st);
+       params.jp = 0;
+    }
+    if ((useJP && n == 9) || (!useJP && n == 8)) {
        if (params.ec != EC_DISABLE && (conf.class2ECMType == ClassModem::ECMTYPE_CLASS20 ||
           (conf.class2ECMType == ClassModem::ECMTYPE_UNSET && serviceType != SERVICE_CLASS2)))
            params.ec += 1;             // simple adjustment, drops EC_ENABLE64
@@ -453,6 +467,15 @@ Class2Modem::parseClass2Capabilities(const char* cap, Class2Params& params, bool
        if (params.bf > BF_ENABLE)
            params.bf = BF_DISABLE;
        params.st = fxmin(params.st, (u_int) ST_40MS);
+       int jpscan = params.jp;
+       params.jp = 0;
+       if (isDIS) {
+           if (jpscan & 0x1) params.jp |= BIT(JP_GREY);
+           if (jpscan & 0x2) params.jp |= BIT(JP_COLOR);
+       } else {
+           if (jpscan == 0x1) params.jp = JP_GREY;
+           else if (jpscan & 0x2) params.jp = JP_COLOR;
+       }
        return (true);
     } else {
        protoTrace("MODEM protocol botch, can not parse \"%s\"", cap);
@@ -623,7 +646,7 @@ Class2Modem::class2Cmd(const fxStr& cmd, const Class2Params& p, bool isDCC, ATRe
     if (conf.class2ECMType == ClassModem::ECMTYPE_CLASS20 ||
        (conf.class2ECMType == ClassModem::ECMTYPE_UNSET && serviceType != SERVICE_CLASS2))
        ecm20 = true;
-    return atCmd(cmd | "=" | p.cmd(conf.class2UseHex, ecm20, (isDCC && useExtendedDF)), r, ms);
+    return atCmd(cmd | "=" | p.cmd(conf.class2UseHex, ecm20, (isDCC && useExtendedDF), useJP), r, ms);
 }
 
 /*
@@ -645,7 +668,7 @@ Class2Modem::parseRange(const char* cp, Class2Params& p)
     /*
      * VR, BF, and JP are already reported as bitmap
      * values accoring to T.32 Table 21.
-     * In vparseRange(), VR:nargs=7, BF:nargs=1.  JP not handled.
+     * In vparseRange(), VR:nargs=7, BF:nargs=1.
      */
     int masked = (1 << 7) + (1 << 1);  // reversed, count-down style
     if (!vparseRange(cp, masked, 8, &p.vr,&p.br,&p.wd,&p.ln,&p.df,&p.ec,&p.bf,&p.st))
@@ -667,12 +690,26 @@ Class2Modem::parseRange(const char* cp, Class2Params& p)
         * are not.
         */
        useExtendedDF = true;
-       p.df = BIT(DF_1DMH) | BIT(DF_2DMR) | BIT(DF_2DMMR) | BIT(DF_JBIG);
+       p.df &= DF_ALL;
+       p.df |= BIT(DF_JBIG);
     } else
        p.df &= DF_ALL;
     p.ec &= EC_ALL;
     p.bf &= BF_ALL;
     p.st &= ST_ALL;
+
+    /*
+     * As JP is optional we do a second, non-fatal parse for it.
+     */
+    int n;
+    masked = 1;
+    if (vparseRange(cp, masked, 9, &n,&n,&n,&n,&n,&n,&n,&n,&p.jp)) {
+       useJP = true;
+       if (conf.class2JPEGSupport) p.jp &= JP_ALL;
+       else p.jp = 0;
+    } else
+       p.jp = 0;
+
     return true;
 }
 
index 9d087e6b233990bd9ac6ea1468e8b58f1f422b00..4fae70422b760da34ca4b3b3ff5abcfe71cc53e0 100644 (file)
@@ -66,7 +66,8 @@ protected:
     bool       xmitWaitForXON;         // if true, wait for XON when sending
     bool       hostDidCQ;              // if true, copy quality done on host
     bool       hasPolling;             // if true, modem does polled recv
-    bool       useExtendedDF;          // if true, modem has Agere data format extension
+    bool       useExtendedDF;          // if true, modem has T.32-A data format extension
+    bool       useJP;                  // if true, modem has JP +FCC parameter option
     char       recvDataTrigger;        // char to send to start recv'ing data
     char       hangupCode[4];          // hangup reason (from modem)
     bool       hadHangup;              // true if +FHNG:/+FHS: received
index 6e96c5595ed038855b5773a03e7ff8e29aba8441..c1f54f87d74c99c4a20b5d6aae6ca436aef4056f 100644 (file)
@@ -39,7 +39,7 @@
 
 #define        RCVBUFSIZ       (32*1024)               // XXX
 
-static void setupCompression(TIFF*, u_int, uint32);
+static void setupCompression(TIFF*, u_int, u_int, uint32);
 
 void
 FaxModem::setupStartPage(TIFF* tif, const Class2Params& params)
@@ -49,7 +49,7 @@ FaxModem::setupStartPage(TIFF* tif, const Class2Params& params)
      * Group3Options must reflect the negotiated session
      * parameters for the forthcoming page data.
      */
-    setupCompression(tif, params.df, group3opts);
+    setupCompression(tif, params.df, params.jp, group3opts);
     /*
      * Do magic at page start to collect the file offset
      * to the start of the page data--this is used in case
@@ -180,7 +180,7 @@ FaxModem::recvPageDLEData(TIFF* tif, bool checkQuality,
         */
        u_int df = (conf.recvDataFormat == DF_ALL ?
            params.df : conf.recvDataFormat);
-       setupCompression(tif, df, 0);           // XXX maybe zero-fill EOLs?
+       setupCompression(tif, df, 0, 0);                // XXX maybe zero-fill EOLs?
        /*
         * Do magic at page start to collect the file offset
         * to the start of the page data--this is used in case
@@ -344,26 +344,44 @@ FaxModem::recvPageDLEData(TIFF* tif, bool checkQuality,
         * NB: the image is written as a single strip of data.
         */
        setupStartPage(tif, params);
-       if (params.df == DF_JBIG) {
+       if (params.df == DF_JBIG || params.jp == JP_GREY || params.jp == JP_COLOR) {
+           if (params.df != DF_JBIG) {
+               /*
+                * In the case of JPEG we have to buffer it all, alter SOF
+                * after seeing DNL, and then write it to disk... because
+                * most JPEG parsers won't know how to handle DNL as it's
+                * rather fax-specific.
+                */
+               recvEOLCount = 0;
+               recvRow = (u_char*) malloc(1024*1000);    // 1M should do it?
+               fxAssert(recvRow != NULL, "page buffering error (JPEG page).");
+               recvPageStart = recvRow;
+           }
+           parserCount[0] = 0;
+           parserCount[1] = 0;
+           parserCount[2] = 0;
+           memset(parserBuf, 0, 16);
            int cc = 0, c;
            bool fin = false;
-           for (; cc < 20; cc++) {
-               c = getModemChar(30000);
-               if (c == EOF || wasTimeout()) {
-                   fin = true;
-                   break;
-               }
-               if (c == DLE) {
+           if (params.df == DF_JBIG) {
+               for (; cc < 20; cc++) {
                    c = getModemChar(30000);
-                   if (c == EOF || c == ETX || wasTimeout()) {
+                   if (c == EOF || wasTimeout()) {
                        fin = true;
                        break;
                    }
+                   if (c == DLE) {
+                       c = getModemChar(30000);
+                       if (c == EOF || c == ETX || wasTimeout()) {
+                           fin = true;
+                           break;
+                       }
+                   }
+                   buf[cc] = c;
                }
-               buf[cc] = c;
+               parseJBIGBIH(buf);
+               flushRawData(tif, 0, (const u_char*) buf, cc);
            }
-           parseJBIGBIH(buf);
-           flushRawData(tif, 0, (const u_char*) buf, cc);
            if (!fin) {
                do {
                    cc = 0;
@@ -380,12 +398,19 @@ FaxModem::recvPageDLEData(TIFF* tif, bool checkQuality,
                                break;
                            }
                        }
-                       parseJBIGStream(c);
+                       if (params.df == DF_JBIG) parseJBIGStream(c);
+                       else parseJPEGStream(c);
                        buf[cc++] = c;
                    } while (cc < RCVBUFSIZ && !fin);
-                   flushRawData(tif, 0, (const u_char*) buf, cc);
+                   if (params.df == DF_JBIG) {
+                       flushRawData(tif, 0, (const u_char*) buf, cc);
+                   } else {
+                       memcpy(recvRow, (const char*) buf, cc);
+                       recvRow += cc;
+                   }
                } while (!fin);
-               clearSDNORMCount();
+               if (params.df == DF_JBIG) clearSDNORMCount();
+               else fixupJPEG(tif);
            }
            recvEndPage(tif, params);
            return (true);
@@ -447,7 +472,8 @@ FaxModem::recvSetupTIFF(TIFF* tif, long, int fillOrder, const fxStr& id)
 {
     TIFFSetField(tif, TIFFTAG_SUBFILETYPE,     FILETYPE_PAGE);
     TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,      (uint32) params.pageWidth());
-    if (params.df == DF_JPEG_COLOR || params.df == DF_JPEG_GREY) {
+    if (params.jp == JP_COLOR || params.jp == JP_GREY) {
+       TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,        8);
 #ifdef PHOTOMETRIC_ITULAB
        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,          PHOTOMETRIC_ITULAB);
 #else
@@ -458,7 +484,7 @@ FaxModem::recvSetupTIFF(TIFF* tif, long, int fillOrder, const fxStr& id)
        // libtiff requires IMAGELENGTH to be set before SAMPLESPERPIXEL, 
        // or StripOffsets and StripByteCounts will have SAMPLESPERPIXEL values
        TIFFSetField(tif, TIFFTAG_IMAGELENGTH,          2000);  // we adjust this later
-       TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL,      params.df == DF_JPEG_GREY ? 1 : 3);
+       TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL,      params.jp == JP_GREY ? 1 : 3);
     } else {
        TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,        1);
        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,          PHOTOMETRIC_MINISWHITE);
@@ -486,33 +512,34 @@ FaxModem::recvSetupTIFF(TIFF* tif, long, int fillOrder, const fxStr& id)
  * Setup the compression scheme and related tags.
  */
 static void
-setupCompression(TIFF* tif, u_int df, uint32 opts)
+setupCompression(TIFF* tif, u_int df, u_int jp, uint32 opts)
 {
-    switch (df) {
-    case DF_JPEG_GREY:
-    case DF_JPEG_COLOR:
-       TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
-       break;
-    case DF_JBIG:
-       TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JBIG);
-       break;
-    case DF_2DMMR:
-       TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4);
-       TIFFSetField(tif, TIFFTAG_GROUP4OPTIONS, opts);
-       break;
-    case DF_2DMRUNCOMP:
-       TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
-       TIFFSetField(tif, TIFFTAG_GROUP3OPTIONS,
-           opts | GROUP3OPT_2DENCODING|GROUP3OPT_UNCOMPRESSED);
-       break;
-    case DF_2DMR:
-       TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
-       TIFFSetField(tif, TIFFTAG_GROUP3OPTIONS, opts | GROUP3OPT_2DENCODING);
-       break;
-    case DF_1DMH:
-       TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
-       TIFFSetField(tif, TIFFTAG_GROUP3OPTIONS, opts &~ GROUP3OPT_2DENCODING);
-       break;
+    u_int dataform = df + (jp ? jp + 4 : 0);
+    switch (dataform) {
+       case JP_GREY+4:
+       case JP_COLOR+4:
+           TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
+           break;
+       case DF_JBIG:
+           TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JBIG);
+           break;
+       case DF_2DMMR:
+           TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4);
+           TIFFSetField(tif, TIFFTAG_GROUP4OPTIONS, opts);
+           break;
+       case DF_2DMRUNCOMP:
+           TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
+           TIFFSetField(tif, TIFFTAG_GROUP3OPTIONS,
+               opts | GROUP3OPT_2DENCODING|GROUP3OPT_UNCOMPRESSED);
+           break;
+       case DF_2DMR:
+           TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
+           TIFFSetField(tif, TIFFTAG_GROUP3OPTIONS, opts | GROUP3OPT_2DENCODING);
+           break;
+       case DF_1DMH:
+           TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
+           TIFFSetField(tif, TIFFTAG_GROUP3OPTIONS, opts &~ GROUP3OPT_2DENCODING);
+           break;
     }
 }
 
@@ -563,10 +590,9 @@ FaxModem::parseJBIGStream(u_char c)
        parserCount[2]--;
        return;
     }
-    u_long framelength = 0;
-
-    memcpy(parserBuf+1, parserBuf, 15);
+    for (u_short i = 15; i > 0; i--) parserBuf[i] = parserBuf[i-1];
     parserBuf[0] = c;
+    u_long framelength = 0;
 
     if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] == 0x04) {
        clearSDNORMCount();
@@ -617,12 +643,6 @@ FaxModem::parseJBIGStream(u_char c)
        parserCount[0] = 0;
        return;
     }
-    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] != 0x00) { // not STUFF
-       clearSDNORMCount();
-       copyQualityTrace("Found Unknown Marker %#x Segment in BID", parserBuf[0]);
-       parserCount[0] == 0;
-       return;
-    }
 }
 
 void
@@ -660,6 +680,208 @@ FaxModem::parseJBIGBIH(u_char* buf)
        (buf[19]&0x4)>>2, (buf[19]&0x2)>>1, buf[19]&0x1);
 }
 
+void
+FaxModem::parseJPEGStream(u_char c)
+{
+    /*
+     * parserCount[n]
+     *   n = 0, bytes since last marker
+     *   n = 1, framelength bypass countdown
+     */
+    parserCount[0]++;
+    if (parserCount[1]) {
+       // just skipping bytes
+       parserCount[1]--;
+       return;
+    }
+    for (u_short i = 15; i > 0; i--) parserBuf[i] = parserBuf[i-1];
+    parserBuf[0] = c;
+    u_long framelength = 0, framewidth = 0, fsize = 0;
+
+    if (parserCount[0] >= 9 && parserBuf[8] == 0xFF && parserBuf[7] >= 0xC0 && parserBuf[7] <= 0xCF
+       && parserBuf[7] != 0xC4 && parserBuf[7] != 0xC8 && parserBuf[7] != 0xCC) {
+       u_short type = parserBuf[7] - 0xC0;
+       fsize = 256*parserBuf[6];
+       fsize += parserBuf[5];
+       framelength = 256*parserBuf[3];
+       framelength += parserBuf[2];
+       framewidth = 256*parserBuf[1];
+       framewidth += parserBuf[0];
+       copyQualityTrace("Found Start of Frame (SOF%u) Marker, size: %lu x %lu", type, framewidth, framelength);
+       if (framelength < 65535 && framelength > recvEOLCount) recvEOLCount = framelength;
+       parserCount[1] = fsize - 9;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] == 0xC8) {
+       copyQualityTrace("Found JPEG Extensions (JPG) Marker");
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 4 && parserBuf[3] == 0xFF && parserBuf[2] == 0xC4) {
+       fsize = 256*parserBuf[1];
+       fsize += parserBuf[0];
+       copyQualityTrace("Found Define Huffman Tables (DHT) Marker");
+       parserCount[1] = fsize - 4;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 4 && parserBuf[3] == 0xFF && parserBuf[2] == 0xCC) {
+       fsize = 256*parserBuf[1];
+       fsize += parserBuf[0];
+       copyQualityTrace("Found Define Arithmatic Coding Conditionings (DAC) Marker");
+       parserCount[1] = fsize - 4;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] >= 0xD0 && parserBuf[0] <= 0xD7) {
+       copyQualityTrace("Found Restart (RST) Marker");
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] == 0xD8) {
+       copyQualityTrace("Found Start of Image (SOI) Marker");
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] == 0xD9) {
+       copyQualityTrace("Found End of Image (EOI) Marker");
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 4 && parserBuf[3] == 0xFF && parserBuf[2] == 0xDA) {
+       fsize = 256*parserBuf[1];
+       fsize += parserBuf[0];
+       copyQualityTrace("Found Start of Scan (SOS) Marker");
+       parserCount[1] = fsize - 4;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 4 && parserBuf[3] == 0xFF && parserBuf[2] == 0xDB) {
+       fsize = 256*parserBuf[1];
+       fsize += parserBuf[0];
+       copyQualityTrace("Found Define Quantization Tables (DQT) Marker");
+       parserCount[1] = fsize - 4;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 6 && parserBuf[5] == 0xFF && parserBuf[4] == 0xDC) {
+       fsize = 256*parserBuf[3];
+       fsize += parserBuf[2];
+       framelength = 256*parserBuf[1];
+       framelength += parserBuf[0];
+       copyQualityTrace("Found Define Number of Lines (DNL) Marker, lines: %lu", framelength);
+       if (framelength < 65535) recvEOLCount = framelength;
+       parserCount[1] = fsize - 6;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 4 && parserBuf[3] == 0xFF && parserBuf[2] == 0xDD) {
+       fsize = 256*parserBuf[1];
+       fsize += parserBuf[0];
+       copyQualityTrace("Found Define Restart Interval (DRI) Marker");
+       parserCount[1] = fsize - 4;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] == 0xDE) {
+       copyQualityTrace("Found Define Hierarchial Progression (DHP) Marker");
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 4 && parserBuf[3] == 0xFF && parserBuf[2] == 0xDF) {
+       fsize = 256*parserBuf[1];
+       fsize += parserBuf[0];
+       copyQualityTrace("Found Expand Reference Components (EXP) Marker");
+       parserCount[1] = fsize - 4;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 4 && parserBuf[3] == 0xFF && parserBuf[2] >= 0xE0 && parserBuf[2] <= 0xEF) {
+       u_short type = parserBuf[2] - 0xE0;
+       fsize = 256*parserBuf[1];
+       fsize += parserBuf[0];
+       copyQualityTrace("Found Application Segment (APP%u) Marker", type);
+       parserCount[1] = fsize - 4;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] >= 0xF0 && parserBuf[0] <= 0xFD) {
+       u_short type = parserBuf[0] - 0xF0;
+       copyQualityTrace("Found JPEG Extension (JPG%u) Marker", type);
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 4 && parserBuf[3] == 0xFF && parserBuf[2] == 0xFE) {
+       fsize = 256*parserBuf[1];
+       fsize += parserBuf[0];
+       copyQualityTrace("Found Comment (COM) Marker");
+       parserCount[1] = fsize - 4;
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] == 0x01) {
+       copyQualityTrace("Found Temporary Private Use (TEM) Marker");
+       parserCount[0] = 0;
+       return;
+    }
+    if (parserCount[0] >= 2 && parserBuf[1] == 0xFF && parserBuf[0] >= 0x02 && parserBuf[0] <= 0xBF) {
+       copyQualityTrace("Found Reserved (RES) Marker 0x%X", parserBuf[0]);
+       parserCount[0] = 0;
+       return;
+    }
+}
+
+void
+FaxModem::fixupJPEG(TIFF* tif)
+{
+    if (!recvEOLCount) {
+       /*
+        * We didn't detect an image length marker (DNL/NEWLEN).  So
+        * we use the session parameters to guess at one, and we hope that
+        * the eventual viewing decoder can cope with things if the data
+        * is short.
+        *
+        * This approach doesn't seem to work with JBIG, so for now we only do it with JPEG.
+        */
+       u_int len, res;
+       switch (params.ln) {
+           case LN_A4: len = 297; break;
+           default:    len = 364; break;       // LN_INF, LN_B4
+       }
+       switch (params.vr) {    // values in mm/100 to avoid floats
+           case VR_NORMAL:     res = 385; break;
+           case VR_FINE:       res = 770; break;
+           case VR_200X100:    res = 394; break;
+           case VR_200X200:    res = 787; break;
+           case VR_200X400:    res = 1575; break;
+           case VR_300X300:    res = 1181; break;
+           default:            res = 1540; break;      // VR_R16, VR_R8
+       }
+       recvEOLCount = (u_long) ((len * res) / 100);
+       protoTrace("RECV: assumed image length of %lu lines", recvEOLCount);
+    }
+    /*
+     * DNL markers generally are not usable in TIFF files.  Furthermore,
+     * many TIFF viewers cannot use them, either.  So, we go back
+     * through the strip and replace any "zero" image length attributes
+     * of any SOF markers with recvEOLCount.  Perhaps we should strip
+     * out the DNL marker entirely, but leaving it in seems harmless.
+     */
+    u_long pagesize = recvRow - recvPageStart;
+    recvRow = recvPageStart;
+    for (uint32 i = 0; i < (pagesize - 6); i++) {
+       if (recvRow[i] == 0xFF && recvRow[i+1] == 0xC0 && recvRow[i+5] == 0x00 && recvRow[i+6] == 0x00) {
+           recvRow[i+5] = recvEOLCount >> 8;
+           recvRow[i+6] = recvEOLCount & 0xFF;
+           protoTrace("RECV: fixing zero image frame length in SOF marker at byte %lu to %lu", i, recvEOLCount);
+       }
+    }
+    if (TIFFWriteRawStrip(tif, 0, (tdata_t) recvRow, pagesize) == -1)
+       serverTrace("RECV: %s: write error", TIFFFileName(tif));
+    free(recvRow);
+}
+
 /*
  * In ECM mode the ECM module provides the line-counter.
  */
@@ -674,9 +896,11 @@ FaxModem::writeECMData(TIFF* tif, u_char* buf, u_int cc, const Class2Params& par
      * block.  In-between blocks merely dump data into the pipe.
      */
 
+    u_int dataform = params.df + (params.jp ? params.jp + 4 : 0);
+
     char cbuf[4];              // size of the page count signal
     if (seq & 1) {             // first block
-       switch (params.df) {
+       switch (dataform) {
            case DF_1DMH:
            case DF_2DMR:
            case DF_2DMMR:
@@ -742,20 +966,28 @@ FaxModem::writeECMData(TIFF* tif, u_char* buf, u_int cc, const Class2Params& par
                {
                    setupStartPage(tif, params);
                    parseJBIGBIH(buf);
+                   parserCount[0] = 0;
+                   parserCount[1] = 0;
+                   parserCount[2] = 0;
+                   memset(parserBuf, 0, 16);
                }
                break;
 
-           case DF_JPEG_GREY:
-           case DF_JPEG_COLOR:
+           case JP_GREY+4:
+           case JP_COLOR+4:
                recvEOLCount = 0;
                recvRow = (u_char*) malloc(1024*1000);    // 1M should do it?
                fxAssert(recvRow != NULL, "page buffering error (JPEG page).");
                recvPageStart = recvRow;
                setupStartPage(tif, params);
+               parserCount[0] = 0;
+               parserCount[1] = 0;
+               parserCount[2] = 0;
+               memset(parserBuf, 0, 16);
                break;
        }
     }
-    switch (params.df) {
+    switch (dataform) {
        case DF_1DMH:
        case DF_2DMR:
        case DF_2DMMR:
@@ -782,184 +1014,28 @@ FaxModem::writeECMData(TIFF* tif, u_char* buf, u_int cc, const Class2Params& par
            break;
 
        case DF_JBIG:
-           // search for Marker Segments in JBIG Bi-Level Image Data
+       case JP_GREY+4:
+       case JP_COLOR+4:
+           // search for Marker Segments in Image Data
            {
-               parserCount[0] = 0;
-               parserCount[1] = 0;
-               parserCount[2] = 0;
-               memset(parserBuf, 0, 16);
                u_int i = 0;
-               if (seq & 1) i += 20;           // skip BIH
+               if ((dataform == DF_JBIG) && (seq & 1)) i += 20;                // skip BIH
                for (; i < cc; i++) {
-                   parseJBIGStream(buf[i]);
-               }
-               clearSDNORMCount();
-           }
-           break;
-
-       case DF_JPEG_GREY:
-       case DF_JPEG_COLOR:
-           {
-               u_long framelength = 0;
-               u_long framewidth = 0;
-               u_long fsize = 0;
-               for (u_int i = 0; i < cc-2; i++) {
-                   if (buf[i] == 0xFF && buf[i+1] >= 0xC0 && buf[i+1] <= 0xCF &&
-                       buf[i+1] != 0xC4 && buf[i+1] != 0xC8 && buf[i+1] != 0xCC) {
-                       u_short type = buf[i+1] - 0xC0;
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       framelength = 256*buf[i+5];
-                       framelength += buf[i+6];
-                       framewidth = 256*buf[i+7];
-                       framewidth += buf[i+8];
-                       copyQualityTrace("Found Start of Frame (SOF%u) Marker, size: %lu x %lu", type, framewidth, framelength);
-                       if (framelength < 65535 && framelength > recvEOLCount) recvEOLCount = framelength;
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xC8) {
-                       copyQualityTrace("Found JPEG Extensions (JPG) Marker");
-                       i += 1;
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xC4) {
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       copyQualityTrace("Found Define Huffman Tables (DHT) Marker");
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xCC) {
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       copyQualityTrace("Found Define Arithmatic Coding Conditionings (DAC) Marker");
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] >= 0xD0 && buf[i+1] <= 0xD7) {
-                       copyQualityTrace("Found Restart (RST) Marker");
-                       i += 1;
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xD8) {
-                       copyQualityTrace("Found Start of Image (SOI) Marker");
-                       i += 1;
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xD9) {
-                       copyQualityTrace("Found End of Image (EOI) Marker");
-                       i += 1;
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xDA) {
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       copyQualityTrace("Found Start of Scan (SOS) Marker");
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xDB) {
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       copyQualityTrace("Found Define Quantization Tables (DQT) Marker");
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xDC) {
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       framelength = 256*buf[i+4];
-                       framelength += buf[i+5];
-                       copyQualityTrace("Found Define Number of Lines (DNL) Marker, lines: %lu", framelength);
-                       if (framelength < 65535) recvEOLCount = framelength;
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xDD) {
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       copyQualityTrace("Found Define Restart Interval (DRI) Marker");
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xDE) {
-                       copyQualityTrace("Found Define Hierarchial Progression (DHP) Marker");
-                       i += 1;
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xDF) {
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       copyQualityTrace("Found Expand Reference Components (EXP) Marker");
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] >= 0xE0 && buf[i+1] <= 0xEF) {
-                       u_short type = buf[i+1] - 0xE0;
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       fxStr comment = "";
-                       for (u_long cpos = 0; i+4+cpos < cc && cpos < fsize && cpos < 256; cpos++) {
-                           if (!isprint(buf[i+4+cpos])) break;         // only printables
-                           comment.append(buf[i+4+cpos]);
-                       }
-                       copyQualityTrace("Found Application Segment (APP%u) Marker \"%s\"", type, (const char*) comment);
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] >= 0xF0 && buf[i+1] <= 0xFD) {
-                       u_short type = buf[i+1] - 0xF0;
-                       copyQualityTrace("Found JPEG Extension (JPG%u) Marker", type);
-                       i += 1;
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0xFE) {
-                       fsize = 256*buf[i+2];
-                       fsize += buf[i+3];
-                       fxStr comment = "";
-                       for (u_long cpos = 0; i+4+cpos < cc && cpos < fsize && cpos < 256; cpos++) {
-                           if (!isprint(buf[i+4+cpos])) break;         // only printables
-                           comment.append(buf[i+4+cpos]);
-                       }
-                       copyQualityTrace("Found Comment (COM) Marker \"%s\"", (const char*) comment);
-                       i += (fsize + 1);
-                   } else if (buf[i] == 0xFF && buf[i+1] == 0x01) {
-                       copyQualityTrace("Found Temporary Private Use (TEM) Marker");
-                       i += 1;
-                   } else if (buf[i] == 0xFF && buf[i+1] >= 0x02 && buf[i+1] <= 0xBF) {
-                       copyQualityTrace("Found Reserved (RES) Marker 0x%X", buf[i+1]);
-                       i += 1;
-                   } else if (buf[i] == 0xFF && buf[i+1] != 0x00 && buf[i+1] != 0xFF) {
-                       copyQualityTrace("Found Unknown Marker 0x%X", buf[i+1]);
-                       i += 1;
-                   }
+                   if (dataform == DF_JBIG) parseJBIGStream(buf[i]);
+                   else parseJPEGStream(buf[i]);
                }
+               if (dataform == DF_JBIG) clearSDNORMCount();
            }
            break;
     }
-    if (params.df != DF_JPEG_GREY && params.df != DF_JPEG_COLOR) {
+    if (params.jp != JP_GREY && params.jp != JP_COLOR) {
        flushRawData(tif, 0, (const u_char*) buf, cc);
     } else {
        memcpy(recvRow, (const char*) buf, cc);
        recvRow += cc;
     }
-    if (seq & 2 && !recvEOLCount && (params.df ==  DF_JPEG_GREY || params.df == DF_JPEG_COLOR)) {
-       /*
-        * We didn't detect an image length marker (DNL/NEWLEN).  So
-        * we use the session parameters to guess at one, and we hope that
-        * the eventual viewing decoder can cope with things if the data
-        * is short.
-        *
-        * This approach doesn't seem to work with JBIG, so for now we only do it with JPEG.
-        */
-       u_int len, res;
-       switch (params.ln) {
-           case LN_A4: len = 297; break;
-           default:    len = 364; break;       // LN_INF, LN_B4
-       }
-       switch (params.vr) {    // values in mm/100 to avoid floats
-           case VR_NORMAL:     res = 385; break;
-           case VR_FINE:       res = 770;  break;
-           case VR_200X100:    res = 394; break;
-           case VR_200X200:    res = 787; break;
-           case VR_200X400:    res = 1575; break;
-           case VR_300X300:    res = 1181; break;
-           default:            res = 1540; break;      // VR_R16, VR_R8
-       }
-       recvEOLCount = (u_long) ((len * res) / 100);
-       protoTrace("RECV: assumed image length of %lu lines", recvEOLCount);
-    }
-    if (seq & 2 && (params.df == DF_JPEG_GREY || params.df == DF_JPEG_COLOR)) {
-       /*
-        * DNL markers generally are not usable in TIFF files.  Furthermore,
-        * many TIFF viewers cannot use them, either.  So, we go back 
-        * through the strip and replace any "zero" image length attributes
-        * of any SOF markers with recvEOLCount.  Perhaps we should strip
-         * out the DNL marker entirely, but leaving it in seems harmless.
-        */
-       u_long pagesize = recvRow - recvPageStart;
-       recvRow = recvPageStart;
-       for (uint32 i = 0; i < pagesize; i++) {
-           if (recvRow[i] == 0xFF && recvRow[i+1] == 0xC0 && recvRow[i+5] == 0x00 && recvRow[i+6] == 0x00) {
-               recvRow[i+5] = recvEOLCount >> 8;
-               recvRow[i+6] = recvEOLCount & 0xFF;
-               protoTrace("RECV: fixing zero image frame length in SOF marker at byte %lu to %lu", i, recvEOLCount);
-           }
-       }
-       if (TIFFWriteRawStrip(tif, 0, (tdata_t) recvRow, pagesize) == -1)
-           serverTrace("RECV: %s: write error", TIFFFileName(tif));
-       free(recvRow);
+    if (seq & 2 && (params.jp == JP_GREY || params.jp == JP_COLOR)) {
+       fixupJPEG(tif);
     }
 }
 
index 8e96595853ea2a6f0b6e151ecd672c30d693a2cf..7e1530188ffafef4b52fc948a348ee6a5f2f846f 100644 (file)
@@ -571,7 +571,8 @@ FaxModem::traceModemParams()
     traceBits(modemParams.br, Class2Params::bitRateNames);
     traceBits(modemParams.wd, Class2Params::pageWidthNames);
     traceBits(modemParams.ln, Class2Params::pageLengthNames);
-    traceBits(modemParams.df, Class2Params::dataFormatNames);
+    u_int dataforms = modemParams.df + ((modemParams.jp & (BIT(JP_GREY) | BIT(JP_COLOR))) << 4);
+    traceBits(dataforms, Class2Params::dataFormatNames);
     if (supportsECM())
        traceBits(modemParams.ec, Class2Params::ecmNames);
     if (modemParams.bf & BIT(BF_ENABLE))
index a397b37878d3eddc41d599d4d1d2127cb6d279c4..be998d117ed229d65a9308336a4f96121b89c391 100644 (file)
@@ -124,6 +124,8 @@ protected:
     void       copyQualityTrace(const char* fmt, ...);
     void       traceModemParams();
     void       traceFCF(const char* dir, u_int fcf);
+    void       parseJPEGStream(u_char c);
+    void       fixupJPEG(TIFF* tif);
     void       parseJBIGStream(u_char c);
     void       parseJBIGBIH(u_char* buf);
     void       clearSDNORMCount();
index b7d1fe8ffbf2007686b92f9a1cc42f8a7a37dd58..a44f54f9ff00234267474257ffbfee408cd54b3d 100644 (file)
@@ -596,6 +596,10 @@ FaxServer::sendSetupParams1(TIFF* tif,
     uint32 g3opts;
     if (!TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &g3opts))
        g3opts = 0;
+    /*
+     * JPEG sending disabled for now
+     */
+    params.jp = 0;
     /*
      * RTFCC lets us ignore our file data format, but our data
      * format may be based upon a requested data format, and without
index 75b30cc1eae897402752a0b5e26686676bcc3a0e..3ebf35e466fae77d91b56d37b2b97df43a822ccc 100644 (file)
@@ -255,6 +255,7 @@ ModemConfig::setupConfig()
     class2UseHex       = false;                // historical behavior
     class2HexNSF       = true;                 // most modems report NSF in hexadecimal
     class2UseLineCount = false;                // don't trust firmware decoders
+    class2JPEGSupport  = false;                // disable JPEG by default
     class1ECMSupport   = true;                 // support for ECM
     class1MRSupport    = true;                 // support for 2-D MR
     class1MMRSupport   = true;                 // support for 2-D MMR
@@ -742,6 +743,8 @@ ModemConfig::setConfigItem(const char* tag, const char* value)
        class1GreyJPEGSupport = getBoolean(value);
     else if (streq(tag, "class1colorjpegsupport"))
        class1ColorJPEGSupport = getBoolean(value);
+    else if (streq(tag, "class2jpegsupport"))
+       class2JPEGSupport = getBoolean(value);
 #endif
     else if (streq(tag, "class1jbigsupport"))
         class1JBIGSupport = getJBIGSupport(value);
index e268055abb2928bad4e017cfec36041125a29b9e..fff162b89e8d52257be3c3179cbebf8362ab151d 100644 (file)
@@ -199,6 +199,7 @@ public:
     fxStr      class2HFLOCmd;          // cmd to setup hardware flow control
     fxStr      class2MINSPCmd;         // cmd to setup min transmit speed
     fxStr      class2RecvDataTrigger;  // send to start recv
+    bool       class2JPEGSupport;      // use JPEG support as reported by the modem
     bool       class2XmitWaitForXON;   // wait for XON before send
     bool       class2RTFCC;            // real-time fax compression conversion
     bool       class2SendRTC;          // append RTC to page data on transmit
index 9a199777a73ef6f2ab571a011df1b88ac8b35bcc..45534b9a0a4c1b56f33defcbb7249a72e3861c32 100644 (file)
@@ -341,6 +341,7 @@ Class2DDISCmd       string  \-      Class 2: command to set session parameters before dialin
 Class2ECMType  string  \s-1``2''\s+1   Class 2: ECM specification type to follow
 Class2HexNSF   boolean \s-1Yes\s+1     Class 2: parse NSF strings as hex values
 Class2HFLOCmd  string  \-      Class 2: command to set hardware flow control
+Class2JPEGSupport      boolean \s-1No\s+1      Class 2: use modem JPEG support
 Class2LIDCmd   string  \s-1AT+FLID\s+1 Class 2: command to set local identifier string
 Class2MINSPCmd string  \s-1AT+FMINSP\s+1       Class 2: command to set minimum transmit speed
 Class2NFLOCmd  string  \-      Class 2: command to set no flow control
@@ -376,6 +377,7 @@ Class2DISCmd        string  \s-1AT+FIS\s+1  Class 2.0: command to set session parameters
 Class2ECMType  string  \s-1``2.0''\s+1 Class 2.0: ECM specification type to follow
 Class2HexNSF   boolean \s-1Yes\s+1     Class 2.0: parse NSF strings as hex values
 Class2HFLOCmd  string  \s-1AT+FLO=2\s+1        Class 2.0: command to set hardware flow control
+Class2JPEGSupport      boolean \s-1No\s+1      Class 2.0: use modem JPEG support
 Class2LIDCmd   string  \s-1AT+FLI\s+1  Class 2.0: command to set local identifier string
 Class2MINSPCmd string  \s-1AT+FMS\s+1  Class 2.0: command to set minimum transmit speed
 Class2NFLOCmd  string  \s-1AT+FLO=0\s+1        Class 2.0: command to set no flow control
@@ -2737,6 +2739,9 @@ This command is issued immediately after sending the
 to switch the modem to Class 2/2.0 operation.
 For Class 2.0 operation the default is ``\s-1AT+FLO=2\s+1''.
 .TP
+.B Class2JPEGSupport
+Whether or not to enable and utilize the JPEG support found in the modem.
+.TP
 .B Class2LIDCmd
 The command used to set the local identifier string.
 This string is inserted into the format ``\s-1%s="<id>"\s+1''
index 456ef387813c646acd1bba9b2c4e57f60d35d07f..01aebff5c54b17c51fec1647b482b54505777748 100644 (file)
@@ -38,6 +38,7 @@ Class2Params::Class2Params()
     ec = (u_int) -1;
     bf = (u_int) -1;
     st = (u_int) -1;
+    jp = (u_int) -1;
 }
 
 int
@@ -50,7 +51,8 @@ Class2Params::operator==(const Class2Params& other) const
        && df == other.df
        && ec == other.ec
        && bf == other.bf
-       && st == other.st;
+       && st == other.st
+       && jp == other.jp;
 }
 
 int
@@ -60,7 +62,7 @@ Class2Params::operator!=(const Class2Params& other) const
 }
 
 fxStr
-Class2Params::cmd(bool class2UseHex, bool ecm20, bool doDFbitmap) const
+Class2Params::cmd(bool class2UseHex, bool ecm20, bool doDFbitmap, bool useJP) const
 {
     u_int unset = (u_int) -1;
     fxStr comma(",");
@@ -80,8 +82,8 @@ Class2Params::cmd(bool class2UseHex, bool ecm20, bool doDFbitmap) const
     s.append(comma);
     if (doDFbitmap) {
        /*
-        * We need to produce a MultiTech data format extension
-        * bitmap rather than a point-value.
+        * We need to produce a T.32 Amendment 1 data format
+        * extension bitmap rather than a point-value.
         */
        u_int dfmap = 0;
        if (df &  BIT(DF_2DMR)) dfmap |= 0x1;
@@ -96,6 +98,10 @@ Class2Params::cmd(bool class2UseHex, bool ecm20, bool doDFbitmap) const
     if (bf != unset) s.append(fxStr::format(notation, bf));
     s.append(comma);
     if (st != unset) s.append(fxStr::format(notation, st));
+    if (useJP) {
+       s.append(comma);
+       if (df != unset) s.append(fxStr::format(notation, jp));
+    }
     return s;
 }
 
@@ -205,11 +211,9 @@ Class2Params::setFromDIS(FaxParams& dis_caps)
 
     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_JPEG)) jp |= BIT(JP_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);
+           if (jp & BIT(JP_GREY)) jp |= BIT(JP_COLOR);
        }
     }
 }
@@ -262,6 +266,7 @@ Class2Params::setFromDIS(u_int dis, u_int xinfo)
        ec = EC_DISABLE;
     bf = BF_DISABLE;                   // XXX from xinfo
     st = DISstTab[(dis & DIS_MINSCAN) >> 1];
+    jp = 0;
 }
 
 /*
@@ -293,14 +298,12 @@ Class2Params::setFromDCS(FaxParams& dcs_caps)
     }
     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_JPEG)) jp = JP_GREY;
     if (dcs_caps.isBitEnabled(FaxParams::BITNUM_FULLCOLOR)) {
-       if (df == DF_JPEG_GREY) df = DF_JPEG_COLOR;
-       //if (df == DF_JBIG_GREY) df = DF_JBIG_COLOR;
+       if (jp == JP_GREY) jp = JP_COLOR;
     }
     if (ec == EC_DISABLE &&
-       (df == DF_2DMMR || df == DF_JBIG || df == DF_JPEG_GREY || df == DF_JPEG_COLOR)) {
+       (df == DF_2DMMR || df == DF_JBIG || jp == JP_GREY || jp == JP_COLOR)) {
        // MMR, JBIG, and JPEG require ECM... we've seen cases where fax
        // senders screw up and don't signal ECM but do send ECM-framed
        // image data in the signalled format, and an RTN will break protocol,
@@ -461,6 +464,12 @@ Class2Params::update(bool 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);
+    if (CHECKPARAM(jp, JP_GREY, isDIS) && (CHECKPARAM(ec, EC_ENABLE64, isDIS) || CHECKPARAM(ec, EC_ENABLE256, isDIS)))
+       setBit(BITNUM_JPEG, true);
+    if (CHECKPARAM(jp, JP_COLOR, isDIS) && (CHECKPARAM(ec, EC_ENABLE64, isDIS) || CHECKPARAM(ec, EC_ENABLE256, isDIS))) {
+       setBit(BITNUM_JPEG, true);
+       setBit(BITNUM_FULLCOLOR, true);
+    }
 
     /*
      * MINIMUM SCANLINE TIME
@@ -869,11 +878,11 @@ const char* Class2Params::dataFormatNames[7] = {
     "2-D Uncompressed Mode",   // DF_2DMRUNCOMP
     "2-D MMR",                 // DF_2DMMR
     "JBIG",                    // DF_JBIG
-    "JPEG Greyscale",          // DF_JPEG_GREY
-    "JPEG Full-Color"          // DF_JPEG_COLOR
+    "JPEG Greyscale",          // JP_GREY
+    "JPEG Full-Color"          // JP_COLOR
 };
 const char* Class2Params::dataFormatName() const
-     { return (dataFormatNames[df]); }
+     { return (dataFormatNames[df+(jp > 0 && jp < (u_int) -1 ? jp + 4 : 0)]); }
 
 fxStr
 Class2Params::dataFormatsName()
@@ -883,8 +892,8 @@ Class2Params::dataFormatsName()
     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");
+    if (jp & BIT(JP_COLOR)) formats.append(", JPEG Full-Color");
+    else if (jp & BIT(JP_GREY))  formats.append(", JPEG Greyscale");
     return (formats);
 }
 
index 7007092d3c8dd1d4c0cc8bc1935d7b47f9127799..e5a1de1835a71ecce24abafba6152e8170013121 100644 (file)
@@ -54,6 +54,7 @@ public:
     u_int ec;          // error correction protocol (EC_*)
     u_int bf;          // binary file transfer protocol (BF_*)
     u_int st;          // minimum scanline time (ST_*)
+    u_int jp;          // JPEG support (JP_*)
 
 // tables for mapping Class 2 codes to T.30 DIS/DCS codes
     static u_int vrDISTab[2];          // vertical resolution
@@ -79,7 +80,7 @@ public:
     int operator==(const Class2Params&) const;
     int operator!=(const Class2Params&) const;
 
-    fxStr cmd(bool class2UseHex, bool ecm20 = false, bool doDFbitmap = false) const;   // format AT+F cmd string
+    fxStr cmd(bool class2UseHex, bool ecm20 = false, bool doDFbitmap = false, bool useJP = false) const;       // format AT+F cmd string
     void setFromDIS(FaxParams& dis);
     void setFromDIS(u_int dis, u_int xinfo = 0);
     void setFromDCS(FaxParams& dcs);
index e91b77c81736b5b03cdae2eee3d8213fd534fc88..5f32b684d28c349a203f7819517eb26510b69ef6 100644 (file)
@@ -93,12 +93,10 @@ const u_short LN_LET        = 3;            // XXX US Letter size (used internally)
 
 const u_short DF_1DMH  = 0;            // 1-D Modified Huffman
 const u_short DF_2DMR  = 1;            // 2-D Modified Read
-const u_short DF_2DMRUNCOMP    = 2;            // 2-D Uncompressed Mode
+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  = 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;
+const u_short DF_ALL   = BIT(DF_JBIG+1)-1 ^ BIT(DF_2DMRUNCOMP);        // no uncompressed
 
 /*
  * The EC definition varies between the Class 2 and Class 2.0 spec, so
@@ -131,6 +129,16 @@ const u_short ST_40MS2     = 6;            // scan time/line: 40 ms/20 ms
 const u_short ST_40MS  = 7;            // scan time/line: 40 ms/40 ms
 const u_short ST_ALL   = BIT(ST_40MS+1)-1;
 
+const u_short JP_NONE  = 0;            // disable JPEG
+const u_short JP_GREY  = 1;            // Greyscale JPEG (T.4 Annex E and T.81)
+const u_short JP_COLOR = 2;            // Full-color JPEG (T.4 Annex E and T.81)
+const u_short JP_HUFFMAN= 3;           // Enable preferred Huffman tables
+const u_short JP_12BIT = 4;            // 12 bits/pel/component
+const u_short JP_NOSUB = 5;            // no subsampling
+const u_short JP_ILLUM = 6;            // custom illuminant
+const u_short JP_GAMUT = 7;            // custom gamut range
+const u_short JP_ALL   = BIT(JP_GAMUT+1)-1;
+
 // post page message codes
 const u_short PPM_MPS  = 0;            // another page next, same document
 const u_short PPM_EOM  = 1;            // another document next
index f7568217b30ad405cc68f77cabc582d65f15d018..9f6bf9292f01bea28348f77890d4afd3d19ac2f3 100644 (file)
@@ -251,6 +251,7 @@ main(int argc, char** argv)
        }
 
        Class2Params params;
+       params.jp = 0;
        uint32 v;
        float vres = 3.85;                                      // XXX default
        float hres = 8.03;