# versions that respond to AT+FFC=? with non-zero data support RTFCC
# Class2RTFCC: yes
-# unfortunately, HylaFAX can't currently receive in color
+# unfortunately, HylaFAX can't currently send or receive in color
ModemAnswerCmd: AT+FCC=,,,,,,,,0;A
-# and, let's try to prevent any color sending attempts, also
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.
+# Class2DCCQueryCmd: "!(00-01),(00-05),(00-02),(00-02),(00-01),(00-01),(00),(00-07)"
+# ModemAnswerCmd: AT+FCC=,,,,1,,,,0;A
+# ModemDialCmd: AT+FCC=,,,,1,,,,0;DT%s
+
+
# If your line supports Caller-ID, you may want to uncomment this...
# QualifyCID: etc/cid # you must create this file
# ModemResetCmds: AT+VCID=1
# versions that respond to AT+FFC=? with non-zero data support RTFCC
# Class2RTFCC: yes
-# unfortunately, HylaFAX can't currently receive in color
-ModemAnswerCmd: AT+FCC=,,,,,,,,0;A
-# and, let's try to prevent any color sending attempts, also
-ModemDialCmd: AT+FCC=,,,,,,,,0;DT%s
+# 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.
+# Enabling the following lines should work around these things.
+# Compare this against the modem's AT+FCC=? response.
+# Class2DCCQueryCmd: "!(03),(00-05),(00-02),(00-02),(00-01),(00-01),(00),(00-07)"
+# ModemAnswerCmd: AT+FCC=03,,,,1,,,,0;A
+# ModemDialCmd: AT+FCC=03,,,,1,,,,0;DT%s
# If your line supports Caller-ID, you may want to uncomment this...
# QualifyCID: etc/cid # you must create this file
| DIS_864
| DIS_1728H
| DIS_1728L
-#ifdef notdef
| DIS_200X400 // additional resolutions
| DIS_300X300
| DIS_400X400
-#endif
+ | DIS_METRES // announce support for both metric
+ | DIS_INCHRES // and inch-based resolutions.
;
}
Class1Modem::processDCSFrame(const HDLCFrame& frame)
{
u_int dcs = frame.getDIS(); // NB: really DCS
- params.setFromDCS(dcs, frame.getXINFO());
+ u_int xinfo = frame.getXINFO();
+ params.setFromDCS(dcs, xinfo);
setDataTimeout(60, params.br);
curcap = findSRCapability(dcs&DCS_SIGRATE, recvCaps);
recvDCS(params); // announce session params
// some modems don't support an AP-query command
if (strcasecmp(conf.class2APQueryCmd, "none") != 0) {
if (doQuery(conf.class2APQueryCmd, s))
- (void) vparseRange(s, 3, &sub, &sep, &pwd);
+ (void) vparseRange(s, 0, 3, &sub, &sep, &pwd);
}
if (sub & BIT(1)) {
saCmd = conf.class2SACmd;
bool
Class2Modem::setupDCC()
{
- params.vr = getBestVRes();
+ params.vr = getVRes();
params.br = getBestSignallingRate();
params.wd = getBestPageWidth();
params.ln = getBestPageLength();
* Clamp values to insure modem doesn't feed us
* nonsense; should log bogus stuff also.
*/
- params.vr = fxmin(params.vr, (u_int) VR_FINE);
+ params.vr = params.vr & VR_ALL;
params.br = fxmin(params.br, (u_int) BR_33600);
params.wd = fxmin(params.wd, (u_int) WD_864);
params.ln = fxmin(params.ln, (u_int) LN_INF);
bool
Class2Modem::parseRange(const char* cp, Class2Params& p)
{
- if (!vparseRange(cp, 8, &p.vr,&p.br,&p.wd,&p.ln,&p.df,&p.ec,&p.bf,&p.st))
+ /*
+ * 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.
+ */
+ 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))
return (false);
p.vr &= VR_ALL;
p.br &= BR_ALL;
}
}
+/*
+ * Trace a modem capability true bit mask (VR, BF, JP).
+ */
+void
+ClassModem::traceBitMask(u_int bits, const char* bitNames[])
+{
+ u_int i = 0;
+ do {
+ if ((bits & i) == i) {
+ modemSupports(bitNames[i]);
+ bits -= i;
+ }
+ i++;
+ } while (bits);
+}
+
/*
* Modem i/o support.
*/
* that indicate they support ``Class Z'' are handled.
*/
bool
-ClassModem::vparseRange(const char* cp, int nargs ... )
+ClassModem::vparseRange(const char* cp, int masked, int nargs ... )
{
bool b = true;
va_list ap;
cp++;
}
if (v != -1) { // expand range or list
- r = fxmin(r, 31); // clamp to valid range
- for (; v <= r; v++)
- mask |= 1<<v;
+ if ((BIT(nargs) & masked) == BIT(nargs)) {
+ /*
+ * These are pre-masked values. T.32 Table 21 gives valid
+ * values as: 00, 01, 02, 04, 08, 10, 20, 40 (hex).
+ *
+ * Some modems may say "(00-7F)" when what's meant is
+ * "(00-40)" or simply "(7F)".
+ */
+ if (v == 00 && r == 127)
+ v = r = 127;
+ if (v == r)
+ mask = v;
+ else {
+ r = fxmin(r, 64); // clamp to valid range
+ mask = 0;
+ for (; v <= r; v++)
+ if (v == 0 || v == 1 || v == 2 || v == 4 || v == 8 || v == 16 || v == 32 || v == 64)
+ mask += v;
+ }
+ } else {
+ r = fxmin(r, 31); // clamp to valid range
+ for (; v <= r; v++)
+ mask |= 1<<v;
+ }
}
if (acceptList && cp[0] == COMMA) // (<item>,<item>...)
cp++;
bool
ClassModem::parseRange(const char* cp, u_int& a0)
{
- return vparseRange(cp, 1, &a0);
+ return vparseRange(cp, 0, 1, &a0);
}
void
void modemSupports(const char* fmt, ...);
void modemCapability(const char* fmt, ...);
void traceBits(u_int bits, const char* bitNames[]);
+ void traceBitMask(u_int bits, const char* bitNames[]);
public:
virtual ~ClassModem();
bool atQuery(const char* what, fxStr& v, long ms = 30*1000);
bool atQuery(const char* what, u_int& v, long ms = 30*1000);
bool parseRange(const char*, u_int&);
- bool vparseRange(const char*, int nargs ...);
+ bool vparseRange(const char*, int masked, int nargs ...);
// modem line control
bool sendBreak(bool pause);
bool setBaudRate(BaudRate rate);
{
setupDecoder(recvFillOrder, params.is2D());
- u_int rowpixels = params.pageWidth(); // NB: assume rowpixels <= 2432
- tiff_runlen_t runs[2*2432]; // run arrays for cur+ref rows
- setRuns(runs, runs+2432, rowpixels);
+ u_int rowpixels = params.pageWidth(); // NB: assume rowpixels <= 4864
+ tiff_runlen_t runs[2*4864]; // run arrays for cur+ref rows
+ setRuns(runs, runs+4864, rowpixels);
recvEOLCount = 0; // count of EOL codes
recvBadLineCount = 0; // rows with a decoding error
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32) -1);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_FILLORDER, (uint16) fillOrder);
- TIFFSetField(tif, TIFFTAG_XRESOLUTION, 204.);
+ TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float) params.horizontalRes());
TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float) params.verticalRes());
TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
TIFFSetField(tif, TIFFTAG_SOFTWARE, HYLAFAX_VERSION);
{
locked = other.locked;
- supportsHighRes = other.supportsHighRes;
+ supportsVRes = other.supportsVRes;
supports2DEncoding = other.supports2DEncoding;
supportsMMR = other.supportsMMR;
supportsPostScript = other.supportsPostScript;
void
FaxMachineInfo::resetConfig()
{
- supportsHighRes = true; // assume 196 lpi support
+ supportsVRes = VR_FINE; // normal and high res support
supports2DEncoding = true; // assume 2D-encoding support
supportsMMR = true; // assume MMR support
supportsPostScript = false; // no support for Adobe protocol
"20ms/10ms", "20ms", "40ms/20ms", "40ms" };
#define NST N(stnames)
-#define HIRES 0
+#define VR 0
#define G32D 1
#define G4 2
#define PS 3
{
int b = (tag[0] == '&' ? 1 : 0); // locked down indicator
if (b) tag++;
- if (streq(tag, "supportshighres")) {
- supportsHighRes = getBoolean(value);
- setLocked(b, HIRES);
+ if (streq(tag, "supportshighres")) { // obsolete tag
+ supportsVRes = VR_FINE;
+ setLocked(b, VR);
+ } else if (streq(tag, "supportsvres")) {
+ supportsVRes = getNumber(value);
+ setLocked(b, VR);
} else if (streq(tag, "supports2dencoding")) {
supports2DEncoding = getBoolean(value);
setLocked(b, G32D);
changed = true; \
}
-void FaxMachineInfo::setSupportsHighRes(bool b)
- { checkLock(HIRES, supportsHighRes, b); }
+void FaxMachineInfo::setSupportsVRes(int v)
+ { checkLock(VR, supportsVRes, v); }
void FaxMachineInfo::setSupports2DEncoding(bool b)
{ checkLock(G32D, supports2DEncoding, b); }
void FaxMachineInfo::setSupportsMMR(bool b)
void
FaxMachineInfo::writeConfig(fxStackBuffer& buf)
{
- putBoolean(buf, "supportsHighRes", isLocked(HIRES), supportsHighRes);
+ putDecimal(buf, "supportsVRes", isLocked(VR), supportsVRes);
putBoolean(buf, "supports2DEncoding", isLocked(G32D),supports2DEncoding);
putBoolean(buf, "supportsMMR", isLocked(G4),supportsMMR);
putBoolean(buf, "supportsPostScript", isLocked(PS), supportsPostScript);
fxStr file; // pathname to info file
u_int locked; // bit vector of locked items
bool changed; // changed since restore
- bool supportsHighRes; // capable of 7.7 line/mm vres
+ u_short supportsVRes; // VR support bitmask
bool supports2DEncoding; // handles Group 3 2D
bool supportsMMR; // handles Group 4
bool supportsPostScript; // handles Adobe NSF protocol
virtual void writeConfig();
virtual void resetConfig();
- bool getSupportsHighRes() const;
+ u_short getSupportsVRes() const;
bool getSupports2DEncoding() const;
bool getSupportsMMR() const;
bool getSupportsPostScript() const;
const fxStr& getLastSendFailure() const;
const fxStr& getLastDialFailure() const;
- void setSupportsHighRes(bool);
+ void setSupportsVRes(int);
void setSupports2DEncoding(bool);
void setSupportsMMR(bool);
void setSupportsPostScript(bool);
const fxStr& getPagerSetupCmds() const;
};
-inline bool FaxMachineInfo::getSupportsHighRes() const
- { return supportsHighRes; }
+inline u_short FaxMachineInfo::getSupportsVRes() const
+ { return supportsVRes; }
inline bool FaxMachineInfo::getSupports2DEncoding() const
{ return supports2DEncoding; }
inline bool FaxMachineInfo::getSupportsMMR() const
* Return the best vres the modem supports.
*/
u_int
-FaxModem::getBestVRes() const
+FaxModem::getVRes() const
{
- return bestBit(modemParams.vr, VR_FINE, VR_NORMAL);
+ /*
+ * We don't use bestBit() here because T.32 Table 21
+ * states that VR is to be reported as a bitmask
+ * of supported resolutions. So we already have it.
+ */
+ return (modemParams.vr);
}
/*
* rather tolerant because of potential precision
* problems and general sloppiness on the part of
* applications writing TIFF files.
+ *
+ * Because R8 and R16 vertical resolutions are the same
+ * but differ by horizontal resolution, R16 is "coded"
+ * as "20" in order to support it.
*/
bool
FaxModem::supportsVRes(float res) const
{
if (3.0 <= res && res < 4.75)
- return (modemParams.vr & BIT(VR_NORMAL)) != 0;
+ return ((modemParams.vr & VR_NORMAL) || (modemParams.vr & VR_200X100)) != 0;
else if (5.9 <= res && res < 9.8)
- return (modemParams.vr & BIT(VR_FINE)) != 0;
+ return ((modemParams.vr & VR_FINE) || (modemParams.vr & VR_200X200)) != 0;
+ else if (9.8 <= res && res < 13)
+ return (modemParams.vr & VR_300X300) != 0;
+ else if (13 <= res && res < 19)
+ return ((modemParams.vr & VR_R8) || (modemParams.vr & VR_200X400)) != 0;
+ else if (res == 20)
+ return (modemParams.vr & VR_R16) != 0;
else
return false;
}
* specified page width.
*/
bool
-FaxModem::supportsPageWidth(u_int w) const
-{
- switch (w) {
- case 1728: return (modemParams.wd & BIT(WD_1728)) != 0;
- case 2048: return (modemParams.wd & BIT(WD_2048)) != 0;
- case 2432: return (modemParams.wd & BIT(WD_2432)) != 0;
- case 1216: return (modemParams.wd & BIT(WD_1216)) != 0;
- case 864: return (modemParams.wd & BIT(WD_864)) != 0;
+FaxModem::supportsPageWidth(u_int w, u_int r) const
+{
+ switch (r) {
+ case VR_R16:
+ switch (w) {
+ case 3456: return (modemParams.wd & BIT(WD_1728)) != 0;
+ case 4096: return (modemParams.wd & BIT(WD_2048)) != 0;
+ case 4864: return (modemParams.wd & BIT(WD_2432)) != 0;
+ case 2432: return (modemParams.wd & BIT(WD_1216)) != 0;
+ case 1728: return (modemParams.wd & BIT(WD_864)) != 0;
+ }
+ case VR_300X300:
+ switch (w) {
+ case 2592: return (modemParams.wd & BIT(WD_1728)) != 0;
+ }
+ case VR_NORMAL:
+ case VR_FINE:
+ case VR_R8:
+ case VR_200X100:
+ case VR_200X200:
+ case VR_200X400:
+ switch (w) {
+ case 1728: return (modemParams.wd & BIT(WD_1728)) != 0;
+ case 2048: return (modemParams.wd & BIT(WD_2048)) != 0;
+ case 2432: return (modemParams.wd & BIT(WD_2432)) != 0;
+ case 1216: return (modemParams.wd & BIT(WD_1216)) != 0;
+ case 864: return (modemParams.wd & BIT(WD_864)) != 0;
+ }
}
return false;
}
FaxModem::modemDIS() const
{
return DIS_T4RCVR
- | Class2Params::vrDISTab[getBestVRes()]
+ | DIS_7MMVRES // not getVRes(), DIS only contains partial VR data
| Class2Params::brDISTab[getBestSignallingRate()]
| Class2Params::wdDISTab[getBestPageWidth()]
| Class2Params::lnDISTab[getBestPageWidth()]
void
FaxModem::traceModemParams()
{
- traceBits(modemParams.vr, Class2Params::verticalResNames);
+ traceBitMask(modemParams.vr, Class2Params::verticalResNames);
traceBits(modemParams.br, Class2Params::bitRateNames);
traceBits(modemParams.wd, Class2Params::pageWidthNames);
traceBits(modemParams.ln, Class2Params::pageLengthNames);
virtual bool supportsMMR() const;
virtual bool supportsEOLPadding() const;
virtual bool supportsVRes(float res) const;
- virtual bool supportsPageWidth(u_int w) const;
+ virtual bool supportsPageWidth(u_int w, u_int r) const;
virtual bool supportsPageLength(u_int l) const;
virtual bool supportsPolling() const;
virtual bool supportsECM() const;
u_int getBestScanlineTime() const;
virtual int selectScanlineTime(int st) const;
- u_int getBestVRes() const;
+ u_int getVRes() const;
u_int getBestDataFormat() const;
u_int getBestPageWidth() const;
u_int getBestPageLength() const;
totdials = 0, maxdials = (u_short) FAX_REDIALS;
tottries = 0, maxtries = (u_short) FAX_RETRIES;
useccover = true;
+ usexvres = false;
pagechop = chop_default;
chopthreshold = -1;
notify = no_notice;
{ "desireddf", &FaxRequest::desireddf },
{ "desiredtl", &FaxRequest::desiredtl },
{ "useccover", &FaxRequest::useccover },
+ { "usexvres", &FaxRequest::usexvres },
};
char* FaxRequest::opNames[18] = {
"fax",
case H_DESIREDDF: desireddf = tag[0] - '0'; break;
case H_DESIREDTL: desiredtl = tag[0] - '0'; break;
case H_USECCOVER: useccover = tag[0] - '0'; break;
+ case H_USEXVRES: usexvres = tag[0] - '0'; break;
case H_TTS:
tts = atoi(tag);
if (tts == 0) // distinguish ``now'' from unset
u_short maxtries; // max # attempts to deliver (answered calls)
u_short pagewidth; // desired output page width (mm)
u_short pagelength; // desired output page length (mm)
- u_short resolution; // desired vertical resolution (lpi)
+ u_short resolution; // desired vertical resolution (lpi) (normal/fine)
u_short usrpri; // user-requested scheduling priority
u_short pri; // current scheduling priority
u_short minsp; // minimum acceptable signalling rate
u_short desireddf; // desired data format
u_short desiredtl; // desired tagline handling
u_short useccover; // whether to use continuation cover page
+ u_short usexvres; // whether to use extended VR
u_short pagechop; // whether to do page chopping
u_short notify; // email notification flags
float chopthreshold; // minimum white space before chopping
* constructed here is also recorded in a private database for
* use in pre-formatting documents sent in future conversations.
*/
- clientInfo.setSupportsHighRes(clientCapabilities.vr == VR_FINE);
+ clientInfo.setSupportsVRes(clientCapabilities.vr);
clientInfo.setSupports2DEncoding(clientCapabilities.df >= DF_2DMR);
clientInfo.setSupportsMMR(clientCapabilities.df >= DF_2DMMR);
clientInfo.setMaxPageWidthInPixels(clientCapabilities.pageWidth());
traceProtocol("REMOTE best rate %s", clientCapabilities.bitRateName());
traceProtocol("REMOTE max %s", clientCapabilities.pageWidthName());
traceProtocol("REMOTE max %s", clientCapabilities.pageLengthName());
- traceProtocol("REMOTE best vres %s", clientCapabilities.verticalResName());
+ traceProtocol("REMOTE best vres %s", clientCapabilities.bestVerticalResName());
traceProtocol("REMOTE best format %s", clientCapabilities.dataFormatName());
if (clientCapabilities.ec != EC_DISABLE)
traceProtocol("REMOTE supports %s", clientCapabilities.ecmName());
} else
params.df = DF_1DMR;
}
- uint32 w;
- (void) TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
- if (w > (uint32)clientInfo.getMaxPageWidthInPixels()) {
- emsg = fxStr::format("Client does not support document page width"
- ", max remote page width %u pixels, image width %lu pixels",
- clientInfo.getMaxPageWidthInPixels(), w);
- return (send_reformat);
- }
- if (!modem->supportsPageWidth((u_int) w)) {
- static const char* widths[8] = {
- "1728", // 1728 in 215 mm line
- "2048", // 2048 in 255 mm line
- "2432", // 2432 in 303 mm line
- "1216", // 1216 in 151 mm line
- "864", // 864 in 107 mm line
- "<undefined>",
- "<undefined>",
- "<undefined>",
- };
- emsg = fxStr::format("Modem does not support document page width"
- ", max page width %s pixels, image width %lu pixels",
- widths[modem->getBestPageWidth()&7], w);
- return (send_reformat);
- }
- // NB: only common values
- params.wd = (w <= 1728 ? WD_1728 : w <= 2048 ? WD_2048 : WD_2432);
/*
- * Try to deduce the vertical resolution of the image
+ * Try to deduce the resolution of the image
* image. This can be problematical for arbitrary TIFF
* images 'cuz vendors sometimes don't give the units.
* We, however, can depend on the info in images that
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &l);
yres = (l < 1450 ? 3.85 : 7.7); // B4 at 98 lpi is ~1400 lines
}
- if (yres >= 7.) {
- if (!clientInfo.getSupportsHighRes()) {
+ float xres;
+ if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres)) {
+ short resunit = RESUNIT_INCH; // TIFF spec default
+ (void) TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
+ if (resunit == RESUNIT_INCH)
+ xres /= 25.4;
+ if (resunit == RESUNIT_NONE)
+ xres /= 720.0; // postscript units ?
+ } else {
+ /*
+ * No horizontal resolution is specified, try
+ * to deduce one from the image width.
+ */
+ u_long l;
+ TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &l);
+ xres = (l < 1729 ? 8 : 16); // R8 = 8 l/mm, R16 = 16 l/mm
+ }
+ if (yres >= 15.) {
+ if (xres > 10) {
+ if (!(clientInfo.getSupportsVRes() & VR_R16)) {
+ emsg = fxStr::format("Hyperfine resolution document is not supported"
+ " by client, image resolution %g x %g lines/mm", xres, yres);
+ return (send_reformat);
+ }
+ if (!modem->supportsVRes(20)) { // "20" is coded for R16
+ emsg = fxStr::format("Hyperfine resolution document is not supported"
+ " by modem, image resolution %g x %g lines/mm", xres, yres);
+ return (send_reformat);
+ }
+ params.vr = VR_R16;
+ } else {
+ if (!((clientInfo.getSupportsVRes() & VR_R8) || (clientInfo.getSupportsVRes() & VR_200X400))) {
+ emsg = fxStr::format("Superfine resolution document is not supported"
+ " by client, image resolution %g lines/mm", yres);
+ return (send_reformat);
+ }
+ if (!modem->supportsVRes(yres)) {
+ emsg = fxStr::format("Superfine resolution document is not supported"
+ " by modem, image resolution %g lines/mm", yres);
+ return (send_reformat);
+ }
+ if (clientInfo.getSupportsVRes() & VR_R8) params.vr = VR_R8;
+ else params.vr = VR_200X400;
+ }
+ } else if (yres >= 10.) {
+ if (!(clientInfo.getSupportsVRes() & VR_300X300)) {
+ emsg = fxStr::format("300x300 resolution document is not supported"
+ " by client, image resolution %g lines/mm", yres);
+ return (send_reformat);
+ }
+ if (!modem->supportsVRes(yres)) {
+ emsg = fxStr::format("300x300 resolution document is not supported"
+ " by modem, image resolution %g lines/mm", yres);
+ return (send_reformat);
+ }
+ params.vr = VR_300X300;
+ } else if (yres >= 7.) {
+ if (!((clientInfo.getSupportsVRes() & VR_FINE) || (clientInfo.getSupportsVRes() & VR_200X200))) {
emsg = fxStr::format("High resolution document is not supported"
- " by client, image resolution %g lines/mm", yres);
+ " by client, image resolution %g lines/mm", yres);
return (send_reformat);
}
if (!modem->supportsVRes(yres)) {
emsg = fxStr::format("High resolution document is not supported"
- " by modem, image resolution %g lines/mm", yres);
+ " by modem, image resolution %g lines/mm", yres);
return (send_reformat);
}
- params.vr = VR_FINE;
+ if (clientInfo.getSupportsVRes() & VR_FINE) params.vr = VR_FINE;
+ else params.vr = VR_200X200;
} else
- params.vr = VR_NORMAL;
+ params.vr = VR_NORMAL; // support required
+
+ /*
+ * Max page width depends on the resolution, so this must come after VR.
+ * maxPageWidth is stored in normal values, so we must compare against
+ * the corresponding normal resolution page width.
+ */
+ uint32 w;
+ (void) TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
+ double rf = (params.vr == VR_R16 ? 2 : params.vr == VR_300X300 ? 1.5 : 1);
+ if (w > (clientInfo.getMaxPageWidthInPixels()*rf)) {
+ emsg = fxStr::format("Client does not support document page width"
+ ", max remote page width %u pixels, image width %lu pixels",
+ (uint32) (clientInfo.getMaxPageWidthInPixels()*rf), w);
+ return (send_reformat);
+ }
+ if (!modem->supportsPageWidth((u_int) w, params.vr)) {
+ static const double widths[8] = {
+ 1728*rf, // 1728 in 215 mm line
+ 2048*rf, // 2048 in 255 mm line
+ 2432*rf, // 2432 in 303 mm line
+ 1216*rf, // 1216 in 151 mm line
+ 864*rf, // 864 in 107 mm line
+ 0,
+ 0,
+ 0,
+ };
+ emsg = fxStr::format("Modem does not support document page width"
+ ", max page width %u pixels, image width %lu pixels",
+ widths[modem->getBestPageWidth()&7], w);
+ return (send_reformat);
+ }
+ // NB: only common values
+ params.wd = ((uint32) (w/rf) <= 1728 ? WD_1728 : (uint32) (w/rf) <= 2048 ? WD_2048 : WD_2432);
/*
* Select page length according to the image size and
{ return modem ? modem->supportsEOLPadding() : false; }
bool FaxServer::modemSupportsVRes(float res) const
{ return modem ? modem->supportsVRes(res) : true; }
-bool FaxServer::modemSupportsPageWidth(u_int w) const
- { return modem ? modem->supportsPageWidth(w) : true; }
bool FaxServer::modemSupportsPageLength(u_int l) const
{ return modem ? modem->supportsPageLength(l) : true; }
bool modemSupportsMMR() const;
bool modemSupportsEOLPadding() const;
bool modemSupportsVRes(float res) const;
- bool modemSupportsPageWidth(u_int w) const;
bool modemSupportsPageLength(u_int l) const;
bool modemSupportsPolling() const;
};
{
u_int rowbytes = howmany(w, 8);
if (curruns == NULL) {
- tiff_runlen_t runs[2*2432]; // run arrays for cur+ref rows
- setRuns(runs, runs+2432, w);
+ tiff_runlen_t runs[2*4864]; // run arrays for cur+ref rows
+ setRuns(runs, runs+4864, w);
while (h-- > 0) {
decodeRow(raster, w);
if (raster)
u_short state; // scheduling state
u_short pagewidth; // desired output page width (mm)
u_short pagelength; // desired output page length (mm)
- u_short resolution; // desired vertical resolution (lpi)
+ u_short resolution; // desired vertical resolution (lpi) (normal/fine)
bool willpoll; // job has polling request
bool suspendPending; // suspend state change pending for job
/*
* Is the modem capable of handling the job.
- */
+ */
bool
Modem::isCapable(const Job& job) const
{
Modem::supportsVRes(float res) const
{
if (75 <= res && res < 120)
- return caps.vr & BIT(VR_NORMAL);
+ return (caps.vr & VR_NORMAL || caps.vr & VR_200X100);
else if (150 <= res && res < 250)
- return caps.vr & BIT(VR_FINE);
+ return (caps.vr & VR_FINE || caps.vr & VR_200X200);
+ else if (250 <= res && res < 350)
+ return caps.vr & VR_300X300;
+ else if (350 <= res && res < 500)
+ return (caps.vr & VR_R8 || caps.vr & VR_200X400 || caps.vr & VR_R16);
else
return false;
}
+/*
+ * Return whether or not the modem supports the
+ * specified VR setting.
+ */
+bool
+Modem::supportsVR(u_int r) const
+{
+ return caps.vr & r;
+}
+
/*
* Return whether or not the modem supports 2DMR.
*/
bool supports2D() const; // modem supports 2D-encoded fax
bool supportsMMR() const; // modem supports 2D-MMR encoding
bool supportsVRes(float) const; // modem supports vertical resolution
+ bool supportsVR(u_int) const; // modem supports VR setting
// modem support fax page width
bool supportsPageWidthInMM(u_int) const;
bool supportsPageWidthInPixels(u_int) const;
*/
u_int w = params.pageWidth();
u_int h = tagLineFont->fontHeight()+MARGIN_TOP+MARGIN_BOT;
- u_int th = (params.vr == VR_FINE) ?
- h : (tagLineFont->fontHeight()/2)+MARGIN_TOP+MARGIN_BOT;
+ u_int th = (params.vr == VR_R16 or params.vr == VR_R8 or params.vr == VR_200X400) ? h :
+ (params.vr == VR_300X300) ? (tagLineFont->fontHeight()/2)+MARGIN_TOP+MARGIN_BOT :
+ (params.vr == VR_FINE or params.vr == VR_200X200) ? (tagLineFont->fontHeight()/2)+MARGIN_TOP+MARGIN_BOT :
+ (tagLineFont->fontHeight()/2)+MARGIN_TOP+MARGIN_BOT;
/*
* imageText assumes that raster is word-aligned; we use
* longs here to optimize the scaling done below for the
* low res case. This should satisfy the word-alignment.
*
* NB: The +3 below is for the case where we need to re-encode
- * 2D-encoded data. An extra 3 rows is sufficinet because
+ * 2D-encoded data. An extra 3 rows is sufficient because
* the number of consecutive 2D-encoded rows is bounded
* by the K parameter in the CCITT spec.
*/
*/
TagLineMemoryDecoder dec(buf);
dec.setupDecoder(fillorder, params.is2D());
- tiff_runlen_t runs[2*2432]; // run arrays for cur+ref rows
- dec.setRuns(runs, runs+2432, w);
+ 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
/*
u_int look_ahead = roundup(dec.getPendingBits(),8) / 8;
u_int decoded = dec.current() - look_ahead - buf;
- if (params.vr == VR_NORMAL) {
+ if (params.vr != VR_R8 and params.vr != VR_R16 and params.vr != VR_200X400) {
/*
* Scale text vertically before encoding. Note the
* ``or'' used to generate the final samples.
MemoryDecoder::scanPage(u_int fillorder, const Class2Params& params)
{
setupDecoder(fillorder, params.is2D());
- u_int rowpixels = params.pageWidth(); // NB: assume rowpixels <= 2432
- tiff_runlen_t runs[2*2432]; // run arrays for cur+ref rows
- setRuns(runs, runs+2432, rowpixels);
+ u_int rowpixels = params.pageWidth(); // NB: assume rowpixels <= 4864
+ tiff_runlen_t runs[2*4864]; // run arrays for cur+ref rows
+ setRuns(runs, runs+4864, rowpixels);
if (!RTCraised()) {
/*
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32) -1);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_FILLORDER, (uint16) fillOrder);
- TIFFSetField(tif, TIFFTAG_XRESOLUTION, 204.);
+ TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float) params.horizontalRes());
TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float) params.verticalRes());
TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
TIFFSetField(tif, TIFFTAG_SOFTWARE, HYLAFAX_VERSION);
{
setupDecoder(recvFillOrder, params.is2D());
- u_int rowpixels = params.pageWidth(); // NB: assume rowpixels <= 2432
- tiff_runlen_t runs[2*2432]; // run arrays for cur+ref rows
- setRuns(runs, runs+2432, rowpixels);
+ u_int rowpixels = params.pageWidth(); // NB: assume rowpixels <= 4864
+ tiff_runlen_t runs[2*4864]; // run arrays for cur+ref rows
+ setRuns(runs, runs+4864, rowpixels);
recvEOLCount = 0; // count of EOL codes
recvBadLineCount = 0; // rows with a decoding error
* (based on the capabilities passed to us by faxgetty).
*/
Class2Params params;
- params.setVerticalRes(req.resolution > 150 && !info.getSupportsHighRes() ?
- 98 : req.resolution);
+ // use the highest resolution the client supports
+ params.vr = VR_NORMAL;
+ if (req.usexvres) {
+ if (info.getSupportsVRes() & VR_200X100 && job.modem->supportsVR(VR_200X100))
+ params.vr = VR_200X100;
+ if (info.getSupportsVRes() & VR_FINE && job.modem->supportsVR(VR_FINE))
+ params.vr = VR_FINE;
+ if (info.getSupportsVRes() & VR_200X200 && job.modem->supportsVR(VR_200X200))
+ params.vr = VR_200X200;
+ if (info.getSupportsVRes() & VR_R8 && job.modem->supportsVR(VR_R8))
+ params.vr = VR_R8;
+ if (info.getSupportsVRes() & VR_200X400 && job.modem->supportsVR(VR_200X400))
+ params.vr = VR_200X400;
+ if (info.getSupportsVRes() & VR_300X300 && job.modem->supportsVR(VR_300X300))
+ params.vr = VR_300X300;
+ if (info.getSupportsVRes() & VR_R16 && job.modem->supportsVR(VR_R16))
+ params.vr = VR_R16;
+ } else {
+ if (info.getSupportsVRes() & VR_200X100 && job.modem->supportsVR(VR_200X100))
+ params.vr = VR_200X100;
+ if (info.getSupportsVRes() & VR_FINE && req.resolution > 150 && job.modem->supportsVR(VR_FINE))
+ params.vr = VR_FINE;
+ if (info.getSupportsVRes() & VR_200X200 && req.resolution > 150 && job.modem->supportsVR(VR_200X200))
+ params.vr = VR_200X200;
+ }
params.setPageWidthInMM(
fxmin((u_int) req.pagewidth, (u_int) info.getMaxPageWidthInMM()));
params.setPageLengthInMM(
* We, however, can depend on the info in images that
* we generate 'cuz we're careful to include valid info.
*/
- float yres;
- if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres)) {
+ float yres, xres;
+ if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) && TIFFGetField(tif, TIFFTAG_YRESOLUTION, &xres)) {
uint16 resunit;
TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
if (resunit == RESUNIT_CENTIMETER)
yres *= 25.4;
- params.setVerticalRes((u_int) yres);
+ xres *= 25.4;
+ params.setRes((u_int) xres, (u_int) yres);
} else {
/*
- * No vertical resolution is specified, try
+ * No resolution is specified, try
* to deduce one from the image length.
*/
uint32 l;
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &l);
// B4 at 98 lpi is ~1400 lines
- params.setVerticalRes(l < 1450 ? 98 : 196);
+ params.setRes(204, (l < 1450 ? 98 : 196));
}
/*
MemoryDecoder::scanPageForBlanks(u_int fillorder, const Class2Params& params)
{
setupDecoder(fillorder, params.is2D());
- u_int rowpixels = params.pageWidth(); // NB: assume rowpixels <= 2432
- tiff_runlen_t runs[2*2432]; // run arrays for cur+ref rows
- setRuns(runs, runs+2432, rowpixels);
+ u_int rowpixels = params.pageWidth(); // NB: assume rowpixels <= 4864
+ tiff_runlen_t runs[2*4864]; // run arrays for cur+ref rows
+ setRuns(runs, runs+4864, rowpixels);
if (!RTCraised()) {
/*
hash("desireddf");
hash("desiredtl");
hash("useccover");
+ hash("usexvres");
hash("tts");
hash("killtime");
hash("retrytime");
*/
MemoryDecoder dec(buf);
dec.setupDecoder(fillorder, params.is2D());
- tiff_runlen_t runs[2*2432]; // run arrays for cur+ref rows
- dec.setRuns(runs, runs+2432, w);
+ 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++) {
*/
u_int look_ahead = roundup(dec.getPendingBits(),8) / 8;
u_int decoded = dec.current() - look_ahead - buf;
- if (params.vr == VR_NORMAL) {
+ if (params.vr == VR_NORMAL or params.vr == VR_200X100) {
/*
* Scale text vertically before encoding. Note the
* ``or'' used to generate the final samples.
uint32 l;
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &l);
params.vr = (l < 1500 ? VR_NORMAL : VR_FINE);
- TIFFSetField(otif, TIFFTAG_XRESOLUTION, 204.);
+ TIFFSetField(otif, TIFFTAG_XRESOLUTION, (float) params.horizontalRes());
TIFFSetField(otif, TIFFTAG_YRESOLUTION, (float) params.verticalRes());
TIFFSetField(otif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
TIFFSetField(otif, TIFFTAG_IMAGELENGTH, l);
T_RETRYTIME, T_SCHEDPRI, T_SENDTIME, T_STATE, T_STATUS,
T_SUBADDR, T_TAGLINE, T_TOTDIALS, T_TOTPAGES, T_TOTTRIES,
T_TO_COMPANY,T_TO_LOCATION, T_TO_USER, T_TO_VOICE, T_USE_CONTCOVER,
- T_USE_ECM, T_USE_TAGLINE, T_USRKEY, T_VRES,
+ T_USE_ECM, T_USE_TAGLINE, T_USE_XVRES, T_USRKEY, T_VRES,
/*
* SNPP tokens.
*/
{ T_USE_CONTCOVER, A_RUSR|A_RADM|A_WADM|A_ROTH },
{ T_USE_ECM, A_RUSR|A_WUSR|A_RADM|A_WADM|A_ROTH },
{ T_USE_TAGLINE, A_RUSR|A_WUSR|A_RADM|A_WADM|A_ROTH },
+ { T_USE_XVRES, A_RUSR|A_WUSR|A_RADM|A_WADM|A_ROTH },
{ T_USRKEY, A_RUSR|A_WUSR|A_RADM|A_WADM|A_ROTH },
{ T_VRES, A_RUSR|A_WUSR|A_RADM|A_WADM|A_ROTH },
{ T_NIL, 0 },
case T_USE_TAGLINE:
replyBoolean(code, job.desiredtl);
return;
+ case T_USE_XVRES:
+ replyBoolean(code, job.usexvres);
+ return;
case T_USE_CONTCOVER:
replyBoolean(code, job.useccover);
return;
jstatLine(T_USE_ECM, "%s", boolString(job.desiredec));
if (checkAccess(job, T_USE_TAGLINE, A_READ))
jstatLine(T_USE_TAGLINE,"%s", boolString(job.desiredtl));
+ if (checkAccess(job, T_USE_XVRES, A_READ))
+ jstatLine(T_USE_XVRES,"%s", boolString(job.usexvres));
if (checkAccess(job, T_USE_CONTCOVER, A_READ))
jstatLine(T_USE_CONTCOVER,"%s", boolString(job.useccover));
u_int i, n;
case T_USE_TAGLINE:
job.desiredtl = b;
return (true);
+ case T_USE_XVRES:
+ job.usexvres = b;
+ return (true);
case T_USE_CONTCOVER:
job.useccover = b;
return (true);
defJob.desiredec = EC_ENABLE;
defJob.desireddf = DF_2DMMR;
defJob.desiredtl = false;
+ defJob.usexvres = false;
defJob.useccover = true;
defJob.pagechop = FaxRequest::chop_default;
defJob.notify = FaxRequest::no_notice;// FAX_DEFNOTIFY
job->desiredec = curJob->desiredec;
job->desireddf = curJob->desireddf;
job->desiredtl = curJob->desiredtl;
+ job->usexvres = curJob->usexvres;
job->useccover = curJob->useccover;
job->pagechop = curJob->pagechop;
job->notify = curJob->notify;
'u', // w (pagewidth)
'u', // x (maxdials)
'u', // y (total pages)
- 's' // z (tts)
+ 's', // z (tts)
+ 'c', // 0 (usexvres as symbol)
};
/*
case 'z':
fprintf(fd, fspec, compactTime(job.tts));
break;
+ case '0':
+ fprintf(fd, fspec, "N "[job.usexvres]);
+ break;
}
} else
putc(*cp, fd);
{ "TOUSER", T_TO_USER, false, true, "[<string>]" },
{ "TOVOICE", T_TO_VOICE, false,false, "[<string>]" },
{ "USECONTCOVER", T_USE_CONTCOVER,false, true, "[YES|NO]" },
+{ "USEXVRES", T_USE_XVRES, false, true, "[YES|NO]" },
{ "USEECM", T_USE_ECM, false, true, "[YES|NO]" },
{ "USETAGLINE", T_USE_TAGLINE, false, true, "[YES|NO]" },
{ "USRKEY", T_USRKEY, false, true, "[<string>]" },
return (true);
}
break;
+ case T_USE_XVRES:
+ if (opt_CRLF()) {
+ replyJobParamValue(*curJob, 213, t);
+ return (true);
+ } else if (boolean_param(b) &&
+ setJobParameter(*curJob, t, b)) {
+ reply(213, "%s set to %s.", parmToken(t), b ? "YES" : "NO");
+ return (true);
+ }
+ break;
case T_CHOPTHRESH:
if (opt_CRLF()) {
replyJobParamValue(*curJob, 213, t);
if (resunit == RESUNIT_NONE)
vres /= 720.0; // postscript units ?
}
- ri.params.setVerticalRes((u_int) vres); // resolution
+ float hres = 8.03; // XXX default
+ if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &hres)) {
+ uint16 resunit = RESUNIT_INCH; // TIFF spec default
+ TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
+ if (resunit == RESUNIT_INCH)
+ hres /= 25.4;
+ if (resunit == RESUNIT_NONE)
+ hres /= 720.0; // postscript units ?
+ }
+ ri.params.setRes((u_int) hres, (u_int) vres); // resolution
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &v);
ri.params.setPageWidthInPixels((u_int) v); // page width
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &v);
tottries integer total number of attempts to send job
tts integer time to send job
useccover integer whether or not to use a continuation cover page
+usexvres integer whether or not to use highest vertical resolution
.fi
.RE
.SH "PARAMETERS"
.B useccover
1 if the job should use a continuation cover page during
retransmission, 0 otherwise.
+.TP 14
+.B usexvres
+1 if the job should use the highest possible vertical
+resolution, 0 otherwise.
See
.IR hylafax-server (${MANNUM4_5})
for a description of continuation cover pages.
dialFailures number count of consecutive failed dial operations
lastDialFailure string reason for last failed dial operation
lastSendFailure string reason for last failed send attempt
-maxPageWidth number maximum page width in pixels
+maxPageWidth number maximum page width in pixels in normal resolution
maxPageLength number maximum page length in millimeters
maxSignallingRate string maximum signalling rate (bits/sec) to use
minScanlineTime string minimum scanline time
pagingProtocol string protocol (IXO or UCP) for this provider
remoteCSI string remote device Called Subscriber Identification
sendFailures number count of consecutive failed send attempts
-supportsHighRes boolean accepts 196 line/inch images
+supportsHighRes boolean accepts 196 line/inch images (obsolete)
+supportsVRes number vertical resolution support bitmask
supports2DEncoding boolean accepts Group 3 2D encoding
supportsMMR boolean accepts Group 4 encoding
supportsPostScript boolean accepts Adobe \*(Ps transfer protocol
number) in the imaged taglines.\fP
.IR
.TP 12
+.B \-G
+Enable usage of any extended resolutions supported by the receiver.
+.B \-G
+supercedes the usage of any
+.B \-l
+or
+.B \-m
+options. Beware that increased resolution will increase transmission time.
+.TP 12
.BI \-h " \fR[\fPmodem\fR@]\fPhost\fR[\fP:port\fR]\fP"
Force the jobs to be processed on a specific
.I host
tottries integer total number of attempts to send job
tts integer time to send job
useccover integer whether or not to use a continuation cover page
+usexvres integer whether or not to use highest vertical resolution
.fi
.RE
.SH "PARAMETERS"
.B useccover
1 if the job should use a continuation cover page during
retransmission, 0 otherwise.
+.TP 14
+.B usexvres
+1 if the job should use the highest possible vertical
+resolution, 0 otherwise.
See
.IR hylafax-server (${MANNUM4_5})
for a description of continuation cover pages.
int verbose = 0;
SendFaxJob& proto = getProtoJob();
db = new FaxDB(tildeExpand(dbName));
- while ((c = Sys::getopt(argc, argv, "a:b:B:c:C:d:f:F:h:i:I:k:M:P:r:s:t:T:U:V:W:x:X:y:Y:z:123lmnpvwADENR")) != -1) {
+ while ((c = Sys::getopt(argc, argv, "a:b:B:c:C:d:f:F:h:i:I:k:M:P:r:s:t:T:U:V:W:x:X:y:Y:z:123lmnpvwADEGNR")) != -1) {
if (c != 'h')
optionsUsed = false;
switch (c) {
case 'f': // sender's identity
setFromIdentity(optarg);
break;
+ case 'G': // extended resolutions
+ proto.setUseXVRes(true);
+ break;
case 'h': // server's host
setHost(optarg);
break;
case 'M': // desired min-scanline time
proto.setDesiredMST(optarg);
break;
- case 'm': // medium resolution
+ case 'm': // fine resolution
proto.setVResolution(196.);
break;
case 'n': // no cover sheet
*/
u_int Class2Params::vrDISTab[2] = {
0, // VR_NORMAL
- DIS_7MMVRES // VR_FINE
+ DIS_7MMVRES, // VR_FINE
};
u_int Class2Params::dfDISTab[4] = {
0, // 1-D MR
void
Class2Params::setFromDIS(u_int dis, u_int xinfo)
{
+ // VR is a bitmap of available settings, not a maximum
vr = DISvrTab[(dis & DIS_7MMVRES) >> 9];
+ if (xinfo & DIS_METRES) {
+ if (xinfo & DIS_200X400) vr |= VR_R8;
+ if (xinfo & DIS_400X400) vr |= VR_R16;
+ }
+ if (xinfo & DIS_INCHRES) {
+ vr |= VR_200X100;
+ if (dis & DIS_7MMVRES) vr |= VR_200X200;
+ if (xinfo & DIS_200X400) vr |= VR_200X400;
+ if (xinfo & DIS_300X300) vr |= VR_300X300;
+ }
/*
* Beware that some modems (e.g. the Supra) indicate they
* support the V.17 bit rates, but not the normal V.27+V.29
Class2Params::setFromDCS(u_int dcs, u_int xinfo)
{
setFromDIS(dcs, xinfo);
- br = DCSbrTab[(dcs & DCS_SIGRATE) >> 10]; // override DIS setup
+ // override DIS setup
+ br = DCSbrTab[(dcs & DCS_SIGRATE) >> 10];
+ if (xinfo & DCS_INCHRES) {
+ if (xinfo & DCS_300X300) vr = VR_300X300;
+ else if (xinfo & DCS_200X400) vr = VR_200X400;
+ else if (dcs & DCS_7MMVRES) vr = VR_200X200;
+ else vr = VR_200X100;
+ } else { // bit 44 of DCS is 0
+ // some manufacturers don't send DCS_INCHRES with DCS_300X300
+ if (xinfo & DCS_300X300) vr = VR_300X300;
+ else if (xinfo & DCS_400X400) vr = VR_R16;
+ else if (xinfo & DCS_200X400) vr = VR_R8;
+ else vr = DISvrTab[(dcs & DCS_7MMVRES) >> 9];
+ }
}
/*
{
u_int dcs = DCS_T4RCVR
| vrDISTab[vr&1]
+ | vrDISTab[(vr>>4)&1] // check for VR_200X200
| brDCSTab[br&15]
| wdDISTab[wd&7]
| lnDISTab[ln&3]
| dfDISTab[df&3]
| stDCSTab[st&7]
+ | DCS_XTNDFIELD
;
return (dcs);
}
u_int
Class2Params::getXINFO() const
{
- return (0); // for now
+ // include support for extended resolutions
+ u_int dcs_xinfo = (1<<24) | (1<<16) | (1<<8) // extension flags for 3 more bytes
+ | (vr & VR_R8 ? DCS_200X400 : 0)
+ | (vr & VR_R16 ? DCS_400X400 : 0)
+ | (vr & VR_200X100 ? DCS_INCHRES : 0)
+ | (vr & VR_200X200 ? DCS_INCHRES : 0) // DCS_7MMVRES set in getDCS()
+ | (vr & VR_200X400 ? (DCS_200X400 | DCS_INCHRES) : 0)
+ | (vr & VR_300X300 ? (DCS_300X300 | DCS_INCHRES) : 0)
+ ;
+ return (dcs_xinfo);
}
/*
static const u_int stTimes[8] =
{ 0, 5, 10, 10, 20, 20, 40, 40 };
u_int ms = stTimes[st&7];
- if ((st & 1) == 0 && vr == VR_FINE)
+ if ((st & 1) == 0 && vr > VR_NORMAL)
ms /= 2;
return transferSize(ms);
}
u_int
Class2Params::pageWidth() const
{
- static const u_int widths[8] = {
+ u_int widths[8] = {
1728, // 1728 in 215 mm line
2048, // 2048 in 255 mm line
2432, // 2432 in 303 mm line
1728, // undefined
1728, // undefined
};
+ switch (vr) {
+ case VR_300X300:
+ widths[0] = 2592;
+ break;
+ case VR_R16:
+ widths[0] = 3456;
+ widths[1] = 4096;
+ widths[2] = 4864;
+ widths[3] = 2432;
+ widths[4] = 1728;
+ break;
+ case VR_NORMAL:
+ case VR_FINE:
+ case VR_R8:
+ case VR_200X100:
+ case VR_200X200:
+ case VR_200X400:
+ // nothing
+ break;
+ }
return (widths[wd&7]);
}
void
Class2Params::setPageWidthInMM(u_int w)
{
+ // This function is unused and doesn't support VR_300X300 and VR_R16.
wd = (w == 255 ? WD_2048 : w == 303 ? WD_2432 : WD_1728);
}
void
Class2Params::setPageWidthInPixels(u_int w)
{
+ /*
+ * Here we attempt to determine the WD parameter with
+ * a pixel width which is impossible to be perfect
+ * because there are colliding values. However,
+ * since we don't use > WD_2432, this is fine.
+ */
wd = (w == 1728 ? WD_1728 :
w == 2048 ? WD_2048 :
w == 2432 ? WD_2432 :
w == 1216 ? WD_1216 :
w == 864 ? WD_864 :
+ w == 3456 ? WD_1728 :
+ w == 4096 ? WD_2048 :
+ w == 4864 ? WD_2432 :
+ //w == 2432 ? WD_1216 : // collision
+ //w == 1728 ? WD_864 : // collision
+ w == 2592 ? WD_1728 :
WD_1728);
}
LN_B4);
}
+u_int
+Class2Params::horizontalRes() const
+{
+ /*
+ * Technically horizontal resolution depends upon the
+ * the number of pixels across the page and the page width.
+ * But, these are just used for writing TIFF tags for
+ * received faxes, so we do this to accomodate the session
+ * parameters, even though it may be slightly off.
+ */
+ return (vr == VR_NORMAL ? 204 :
+ vr == VR_FINE ? 204 :
+ vr == VR_R8 ? 204 :
+ vr == VR_R16 ? 408 :
+ vr == VR_200X100 ? 200 :
+ vr == VR_200X200 ? 200 :
+ vr == VR_200X400 ? 200 :
+ vr == VR_300X300 ? 300 :
+ (u_int) -1);
+}
+
u_int
Class2Params::verticalRes() const
{
- return (vr == VR_NORMAL ? 98 : vr == VR_FINE ? 196 : (u_int) -1);
+ return (vr == VR_NORMAL ? 98 :
+ vr == VR_FINE ? 196 :
+ vr == VR_R8 ? 391 :
+ vr == VR_R16 ? 391 :
+ vr == VR_200X100 ? 100 :
+ vr == VR_200X200 ? 200 :
+ vr == VR_200X400 ? 400 :
+ vr == VR_300X300 ? 300 :
+ (u_int) -1);
}
void
-Class2Params::setVerticalRes(u_int res)
+Class2Params::setRes(u_int xres, u_int yres)
{
- vr = (res > 150 ? VR_FINE : VR_NORMAL);
+ vr = (xres > 300 && yres > 391 ? VR_R16 :
+ xres > 204 && yres > 250 ? VR_300X300 :
+ yres > 391 ? VR_200X400 :
+ yres > 250 ? VR_R8 :
+ yres > 196 ? VR_200X200 :
+ yres > 150 ? VR_FINE :
+ yres > 98 ? VR_200X100 : VR_NORMAL);
}
fxStr
Class2Params::encodePage() const
{
- int v = (vr&1) | ((wd&7)<<1) | ((ln&3)<<4) | ((df&3)<<6);
+ int v = (vr&3) | ((wd&7)<<1) | ((ln&3)<<4) | ((df&3)<<6);
return fxStr(v, "%02x");
}
u_int
Class2Params::encode() const
{
- return ((vr&7)<<0)
+ return (vr > 4 ? ((vr>>4)&7)<<0 : (vr&7)<<0) // push inch resolutions
| ((br&15)<<3)
| ((wd&7)<<9)
| ((ln&3)<<12)
Class2Params::decode(u_int v)
{
if (v>>21 == 1) { // check version
- vr = (v>>0) & 7;
+ vr = ((v>>0) & 7); // VR is a bitmap
br = (v>>3) & 15;
wd = (v>>9) & 7;
ln = (v>>12) & 3;
/*
* Routines for printing some Class 2 capabilities.
*/
-const char* Class2Params::verticalResNames[2] = {
+const char* Class2Params::verticalResNames[65] = {
"3.85 line/mm",
"7.7 line/mm",
+ "15.4 line/mm", "",
+ "R16 x 15.4 line/mm", "", "", "",
+ "200 x 100 dpi", "", "", "", "", "", "", "",
+ "200 x 200 dpi", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "200 x 400 dpi", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "300 x 300 dpi"
};
+
const char* Class2Params::verticalResName() const
- { return (verticalResNames[vr&1]); }
+ { return (verticalResNames[vr & VR_ALL]); }
+
+const char* Class2Params::bestVerticalResName() const
+{
+ /*
+ * "Best" is determined by comparing pixels per square
+ * area of image. Thus 300x300 is "better" than 200x400.
+ */
+ u_int res = VR_NORMAL; // required default
+ if (vr & VR_200X100) res = VR_200X100;
+ if (vr & VR_FINE) res = VR_FINE;
+ if (vr & VR_200X200) res = VR_200X200;
+ if (vr & VR_R8) res = VR_R8;
+ if (vr & VR_200X400) res = VR_200X400;
+ if (vr & VR_300X300) res = VR_300X300;
+ if (vr & VR_R16) res = VR_R16;
+ return(verticalResNames[res]);
+}
+
const char* Class2Params::bitRateNames[16] = {
"2400 bit/s", // BR_2400
{ return (dataFormatNames[df&3]); }
const char* Class2Params::pageWidthNames[8] = {
- "page width 1728 pixels in 215 mm",
- "page width 2048 pixels in 255 mm",
- "page width 2432 pixels in 303 mm",
- "page width 1216 pixels in 151 mm",
- "page width 864 pixels in 107 mm",
+ "A4 page width (215 mm)",
+ "B4 page width (255 mm)",
+ "A3 page width (303 mm)",
+ "page width 151 mm",
+ "page width 107 mm",
"undefined page width (wd=5)",
"undefined page width (wd=6)",
"undefined page width (wd=7)",
// XXX these exist solely for FaxModem
static const char* pageWidthNames[8];
static const char* pageLengthNames[4];
- static const char* verticalResNames[2];
+ static const char* verticalResNames[65];
static const char* scanlineTimeNames[8];
static const char* dataFormatNames[4];
static const char* ecmNames[4];
u_int pageLength() const; // page length in mm
void setPageLengthInMM(u_int l);
u_int verticalRes() const; // lines/inch
- void setVerticalRes(u_int res);
+ u_int horizontalRes() const; // dpi
+ void setRes(u_int xres, u_int yres);
static const char* bitRateNames[16]; // XXX needed by Class 1 driver
const char* bitRateName() const;
u_int bitRate() const; // bits/second value
const char* verticalResName() const;
+ const char* bestVerticalResName() const;
const char* scanlineTimeName() const;
const char* dataFormatName() const;
const char* ecmName() const;
{ 'x', "MaxDials" }, // x (maxdials)
{ 'y', "TotPages" }, // y (totpages)
{ 'z', "TTS" }, // z (tts)
+ { '0', "UseXVres" }, // 0 (usexvres as symbol)
{ '\0' },
};
void FaxClient::getJobStatusHeader(fxStr& header)
autoCover = other.autoCover;
coverIsTemp = other.coverIsTemp;
sendTagLine = other.sendTagLine;
+ useXVRes = other.useXVRes;
retryTime = other.retryTime;
hres = other.hres;
vres = other.vres;
autoCover = true;
sendTagLine = false; // default is to use server config
+ useXVRes = false; // default is to use normal or fine
notify = FAX_DEFNOTIFY; // default notification
mailbox = "";
priority = FAX_DEFPRIORITY; // default transmit priority
setDesiredMST(value);
else if (streq(tag, "desiredec"))
setDesiredEC(FaxConfig::getBoolean(value));
+ else if (streq(tag, "usexvres"))
+ setUseXVRes(FaxConfig::getBoolean(value));
else if (streq(tag, "desireddf"))
setDesiredDF(value);
else if (streq(tag, "retrytime"))
}
void SendFaxJob::setDesiredMST(int v) { desiredst = v; }
void SendFaxJob::setDesiredEC(bool b) { desiredec = b; }
+void SendFaxJob::setUseXVRes(bool b) { useXVRes = b; }
void
SendFaxJob::setDesiredDF(const char* v)
{
CHECKPARM("USETAGLINE", true)
CHECKPARM("TAGLINE", tagline)
}
+ if (useXVRes) {
+ CHECKPARM("USEXVRES", true)
+ }
if (doneop == "archive") {
CHECKPARM("DONEOP", "archive")
}
fxStr fromcompany; // rec. from company for cover page
bool sendTagLine; // if true, use custom tagline format
+ bool useXVRes; // if true, use extended resolutions
fxStr killTime; // job's time to be killed
fxStr sendTime; // job's time to be sent
u_int retryTime; // retry time for failures (secs)
int getDesiredMST() const;
void setDesiredEC(bool b); // desired use of Error Correction mode
bool getDesiredEC() const;
+ void setUseXVRes(bool b); // desired use of extended resolutions
+ bool getUseXVRes() const;
void setDesiredDF(int); // desired data format
void setDesiredDF(const char*);
int getDesiredDF() const;
inline int SendFaxJob::getDesiredSpeed() const { return desiredbr; }
inline int SendFaxJob::getDesiredMST() const { return desiredst; }
inline bool SendFaxJob::getDesiredEC() const { return desiredec; }
+inline bool SendFaxJob::getUseXVRes() const { return useXVRes; }
inline int SendFaxJob::getDesiredDF() const { return desireddf; }
inline const fxStr& SendFaxJob::getTagLineFormat() const{ return tagline; }
inline u_int SendFaxJob::getChopHandling() const { return pagechop; }
const u_short SERVICE_ALL = BIT(9)-1;
// t.30 session subparameter codes
-// NB: only the first two are used
-const u_int VR_NORMAL = 0; // 98 lpi
-const u_int VR_FINE = 1; // 196 lpi
-const u_int VR_R8 = 2; // R8 x 15.4 l/mm
-const u_int VR_R16 = 4; // R16 x 15.4 l/mm
-const u_int VR_200X100 = 8; // 200 dpi x 100 l/25.4mm
-const u_int VR_200X200 = 10; // 200 dpi x 200 l/25.4mm
-const u_int VR_200X400 = 20; // 200 dpi x 400 l/25.4mm
-const u_int VR_300X300 = 40; // 300 dpi x 300 l/25.4mm
-const u_int VR_ALL = BIT(VR_FINE+1)-1;
+const u_int VR_NORMAL = 0x00; // 98 lpi
+const u_int VR_FINE = 0x01; // 196 lpi
+const u_int VR_R8 = 0x02; // R8 x 15.4 l/mm
+const u_int VR_R16 = 0x04; // R16 x 15.4 l/mm
+const u_int VR_200X100 = 0x08; // 200 dpi x 100 l/25.4mm
+const u_int VR_200X200 = 0x10; // 200 dpi x 200 l/25.4mm
+const u_int VR_200X400 = 0x20; // 200 dpi x 400 l/25.4mm
+const u_int VR_300X300 = 0x40; // 300 dpi x 300 l/25.4mm
+const u_int VR_ALL = 0x7F;
const u_short BR_2400 = 0; // 2400 bit/s
const u_short BR_4800 = 1; // 4800 bit/s
Class2Params params;
uint32 v;
+ float vres = 3.85; // XXX default
+ float hres = 8.03;
#ifdef TIFFTAG_FAXRECVPARAMS
- if (TIFFGetField(tif, TIFFTAG_FAXRECVPARAMS, &v))
+ if (TIFFGetField(tif, TIFFTAG_FAXRECVPARAMS, &v)) {
params.decode((u_int) v); // page transfer params
- else {
+ // inch & metric resolutions overlap and are distinguished by yres
+ TIFFGetField(tif, TIFFTAG_YRESOLUTION, &vres);
+ switch ((u_int) vres) {
+ case 100:
+ params.vr = VR_200X100;
+ break;
+ case 200:
+ params.vr = VR_200X200;
+ break;
+ case 400:
+ params.vr = VR_200X400;
+ break;
+ case 300:
+ params.vr = VR_300X300;
+ break;
+ }
+ } else {
#endif
- float vres = 3.85; // XXX default
if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &vres)) {
uint16 resunit = RESUNIT_INCH; // TIFF spec default
TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
vres /= 25.4;
if (resunit == RESUNIT_NONE)
vres /= 720.0; // postscript units ?
+ if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &hres)) {
+ if (resunit == RESUNIT_INCH)
+ hres /= 25.4;
+ if (resunit == RESUNIT_NONE)
+ hres /= 720.0; // postscript units ?
+ }
}
- params.setVerticalRes((u_int) vres); // resolution
+ params.setRes((u_int) hres, (u_int) vres); // resolution
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &v);
params.setPageWidthInPixels((u_int) v); // page width
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &v);
date = buf;
}
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &v);
- float h = v / (params.verticalRes() < 100 ? 3.85 : 7.7);
+ float h = v / (params.vr == VR_NORMAL ? 3.85 :
+ params.vr == VR_200X100 ? 3.94 :
+ params.vr == VR_FINE ? 7.7 :
+ params.vr == VR_200X200 ? 7.87 :
+ params.vr == VR_R8 ? 15.4 :
+ params.vr == VR_200X400 ? 12.81 :
+ params.vr == VR_300X300 ? 9.14 : 15.4);
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &v);
- float w = (v / 204.) * 25.4;
+ float w = v / (params.vr == VR_NORMAL ? 8.0 :
+ params.vr == VR_200X100 ? 8.00 :
+ params.vr == VR_FINE ? 8.00 :
+ params.vr == VR_200X200 ? 8.00 :
+ params.vr == VR_R8 ? 8.00 :
+ params.vr == VR_200X400 ? 8.00 :
+ params.vr == VR_300X300 ? 12.01 : 16.01);
time_t time = 0;
u_int npages = 0; // page count
do {
TIFFClose(tif);
printf("%11s %u\n", "Pages:", npages);
- if (params.verticalRes() == 98)
+ if (params.vr == VR_NORMAL)
printf("%11s Normal\n", "Quality:");
- else if (params.verticalRes() == 196)
+ else if (params.vr == VR_FINE)
printf("%11s Fine\n", "Quality:");
+ else if (params.vr == VR_R8)
+ printf("%11s Superfine\n", "Quality:");
+ else if (params.vr == VR_R16)
+ printf("%11s Hyperfine\n", "Quality:");
else
printf("%11s %u lines/inch\n", "Quality:", params.verticalRes());
PageSizeInfo* info = PageSizeInfo::getPageSizeBySize(w, h);
-o) shift; out="$1" ;;
-w) shift; pagewidth="$1" ;;
-l) shift; pagelength="$1" ;;
- -r) shift; vres="$1" ;;
+ -r) shift; vres="$1" ;;
-m) shift;; # NB: not implemented
-1) device=tiffg3 ;;
-2) ($PS -h | grep tiffg32d >/dev/null 2>&1) \
done
test -z "$fil" && fil="-" # read from stdin
case "${pagewidth}x${pagelength}" in
-1728x280|1728x279) # 279.4mm is actually correct...
+1728x280|1728x279|2592x280|2592x279|3456x280|3456x279) # 279.4mm is actually correct...
paper=letter;;
-1728x364)
+1728x364|2592x364|3456x364)
paper=legal;;
*x296|*x297) # more roundoff problems...
paper=a4;;
exit 254;; # causes document to be rejected
esac
+# The image must end up with a pixel width according to T.32 Table 21.
+# Ghostscript contains code to fixate a4 and letter to 1728 pixels
+# when using 196-204 dpi, but not for the others, thus the floats.
+case "$paper" in
+ a4)
+ case "$pagewidth" in
+ 2592) hres=313.65;; # VR_R300X300
+ 3456) hres=418.20;; # VR_R16
+ *) hres=209.10;; # everything else, 1728 pixels
+ esac;;
+ b4) # no 300x300 available with B4
+ case "$pagewidth" in
+ 4096) hres=415.95;; # VR_R16
+ *) hres=207.98;; # everything else, 2048 pixels
+ esac;;
+ *) # letter, legal
+ case "$pagewidth" in
+ 2592) hres=304.94;; # VR_R300X300
+ 3456) hres=406.59;; # VR_R16
+ *) hres=203.29;; # everything else, 1728 pixels
+ esac;;
+esac
+
#
# The sed work fixes bug in Windows-generated
# PostScript that causes certain international
-sPAPERSIZE=$paper \
-dFIXEDMEDIA \
-dBATCH \
- -r204x$vres \
+ -r$hres\x$vres \
"-sOutputFile=$out" \
$fil
done
test -z "$fil" && fil="-" # read from stdin
case "${pagewidth}x${pagelength}" in
-1728x280|1728x279) # 279.4mm is actually correct...
+1728x280|1728x279|2592x280|2592x279|3456x280|3456x279) # 279.4mm is actually correct...
paper=letter;;
-1728x364)
+1728x364|2592x364|3456x364)
paper=legal;;
*x296|*x297) # more roundoff problems...
paper=a4;;
exit 254;; # causes document to be rejected
esac
+# The image must end up with a pixel width according to T.32 Table 21.
+# Ghostscript contains code to fixate a4 and letter to 1728 pixels
+# when using 196-204 dpi, but not for the others, thus the floats.
+case "$paper" in
+ a4)
+ case "$pagewidth" in
+ 2592) hres=313.65;; # VR_R300X300
+ 3456) hres=418.20;; # VR_R16
+ *) hres=209.10;; # everything else, 1728 pixels
+ esac;;
+ b4) # no 300x300 available with B4
+ case "$pagewidth" in
+ 4096) hres=415.95;; # VR_R16
+ *) hres=207.98;; # everything else, 2048 pixels
+ esac;;
+ *) # letter, legal
+ case "$pagewidth" in
+ 2592) hres=304.94;; # VR_R300X300
+ 3456) hres=406.59;; # VR_R16
+ *) hres=203.29;; # everything else, 1728 pixels
+ esac;;
+esac
+
#
# The sed work fixes bug in Windows-generated
# PostScript that causes certain international
-dSAFER=true \
-sPAPERSIZE=$paper \
-dFIXEDMEDIA \
- -r204x$vres \
+ -r$hres\x$vres \
"-sOutputFile=$out" \
-