diff spandsp-0.0.6pre17/src/t4_tx.c @ 4:26cd8f1ef0b1

import spandsp-0.0.6pre17
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 15:50:58 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spandsp-0.0.6pre17/src/t4_tx.c	Fri Jun 25 15:50:58 2010 +0200
@@ -0,0 +1,1539 @@
+//#define T4_STATE_DEBUGGING
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t4_tx.c - ITU T.4 FAX transmit processing
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2003, 2007 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: t4_tx.c,v 1.13.2.9 2009/12/21 17:18:40 steveu Exp $
+ */
+
+/*
+ * Much of this file is based on the T.4 and T.6 support in libtiff, which requires
+ * the following notice in any derived source code:
+ *
+ * Copyright (c) 1990-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <memory.h>
+#include <string.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include "floating_fudge.h"
+#include <tiffio.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/bit_operations.h"
+#include "spandsp/async.h"
+#include "spandsp/t4_rx.h"
+#include "spandsp/t4_tx.h"
+#include "spandsp/version.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/t4_rx.h"
+#include "spandsp/private/t4_tx.h"
+
+/*! The number of centimetres in one inch */
+#define CM_PER_INCH                 2.54f
+
+/*! The number of EOLs to be sent at the end of a T.4 page */
+#define EOLS_TO_END_T4_TX_PAGE      6
+/*! The number of EOLs to be sent at the end of a T.6 page */
+#define EOLS_TO_END_T6_TX_PAGE      2
+
+#if defined(T4_STATE_DEBUGGING)
+static void STATE_TRACE(const char *format, ...)
+{
+    va_list arg_ptr;
+
+    va_start(arg_ptr, format);
+    vprintf(format, arg_ptr);
+    va_end(arg_ptr);
+}
+/*- End of function --------------------------------------------------------*/
+#else
+#define STATE_TRACE(...) /**/
+#endif
+
+/*! T.4 run length table entry */
+typedef struct
+{
+    /*! Length of T.4 code, in bits */
+    uint16_t length;
+    /*! T.4 code */
+    uint16_t code;
+    /*! Run length, in bits */
+    int16_t run_length;
+} t4_run_table_entry_t;
+
+#include "faxfont.h"
+
+/* Legitimate runs of zero bits which are the tail end of one code
+   plus the start of the next code do not exceed 10 bits. */
+
+/*
+ * Note that these tables are ordered such that the index into the table
+ * is known to be either the run length, or (run length / 64) + a fixed
+ * offset.
+ */
+static const t4_run_table_entry_t t4_white_codes[] =
+{
+    { 8, 0x00AC,    0},         /* 0011 0101 */
+    { 6, 0x0038,    1},         /* 0001 11 */
+    { 4, 0x000E,    2},         /* 0111 */
+    { 4, 0x0001,    3},         /* 1000 */
+    { 4, 0x000D,    4},         /* 1011 */
+    { 4, 0x0003,    5},         /* 1100 */
+    { 4, 0x0007,    6},         /* 1110 */
+    { 4, 0x000F,    7},         /* 1111 */
+    { 5, 0x0019,    8},         /* 1001 1 */
+    { 5, 0x0005,    9},         /* 1010 0 */
+    { 5, 0x001C,   10},         /* 0011 1 */
+    { 5, 0x0002,   11},         /* 0100 0 */
+    { 6, 0x0004,   12},         /* 0010 00 */
+    { 6, 0x0030,   13},         /* 0000 11 */
+    { 6, 0x000B,   14},         /* 1101 00 */
+    { 6, 0x002B,   15},         /* 1101 01 */
+    { 6, 0x0015,   16},         /* 1010 10 */
+    { 6, 0x0035,   17},         /* 1010 11 */
+    { 7, 0x0072,   18},         /* 0100 111 */
+    { 7, 0x0018,   19},         /* 0001 100 */
+    { 7, 0x0008,   20},         /* 0001 000 */
+    { 7, 0x0074,   21},         /* 0010 111 */
+    { 7, 0x0060,   22},         /* 0000 011 */
+    { 7, 0x0010,   23},         /* 0000 100 */
+    { 7, 0x000A,   24},         /* 0101 000 */
+    { 7, 0x006A,   25},         /* 0101 011 */
+    { 7, 0x0064,   26},         /* 0010 011 */
+    { 7, 0x0012,   27},         /* 0100 100 */
+    { 7, 0x000C,   28},         /* 0011 000 */
+    { 8, 0x0040,   29},         /* 0000 0010 */
+    { 8, 0x00C0,   30},         /* 0000 0011 */
+    { 8, 0x0058,   31},         /* 0001 1010 */
+    { 8, 0x00D8,   32},         /* 0001 1011 */
+    { 8, 0x0048,   33},         /* 0001 0010 */
+    { 8, 0x00C8,   34},         /* 0001 0011 */
+    { 8, 0x0028,   35},         /* 0001 0100 */
+    { 8, 0x00A8,   36},         /* 0001 0101 */
+    { 8, 0x0068,   37},         /* 0001 0110 */
+    { 8, 0x00E8,   38},         /* 0001 0111 */
+    { 8, 0x0014,   39},         /* 0010 1000 */
+    { 8, 0x0094,   40},         /* 0010 1001 */
+    { 8, 0x0054,   41},         /* 0010 1010 */
+    { 8, 0x00D4,   42},         /* 0010 1011 */
+    { 8, 0x0034,   43},         /* 0010 1100 */
+    { 8, 0x00B4,   44},         /* 0010 1101 */
+    { 8, 0x0020,   45},         /* 0000 0100 */
+    { 8, 0x00A0,   46},         /* 0000 0101 */
+    { 8, 0x0050,   47},         /* 0000 1010 */
+    { 8, 0x00D0,   48},         /* 0000 1011 */
+    { 8, 0x004A,   49},         /* 0101 0010 */
+    { 8, 0x00CA,   50},         /* 0101 0011 */
+    { 8, 0x002A,   51},         /* 0101 0100 */
+    { 8, 0x00AA,   52},         /* 0101 0101 */
+    { 8, 0x0024,   53},         /* 0010 0100 */
+    { 8, 0x00A4,   54},         /* 0010 0101 */
+    { 8, 0x001A,   55},         /* 0101 1000 */
+    { 8, 0x009A,   56},         /* 0101 1001 */
+    { 8, 0x005A,   57},         /* 0101 1010 */
+    { 8, 0x00DA,   58},         /* 0101 1011 */
+    { 8, 0x0052,   59},         /* 0100 1010 */
+    { 8, 0x00D2,   60},         /* 0100 1011 */
+    { 8, 0x004C,   61},         /* 0011 0010 */
+    { 8, 0x00CC,   62},         /* 0011 0011 */
+    { 8, 0x002C,   63},         /* 0011 0100 */
+    { 5, 0x001B,   64},         /* 1101 1 */
+    { 5, 0x0009,  128},         /* 1001 0 */
+    { 6, 0x003A,  192},         /* 0101 11 */
+    { 7, 0x0076,  256},         /* 0110 111 */
+    { 8, 0x006C,  320},         /* 0011 0110 */
+    { 8, 0x00EC,  384},         /* 0011 0111 */
+    { 8, 0x0026,  448},         /* 0110 0100 */
+    { 8, 0x00A6,  512},         /* 0110 0101 */
+    { 8, 0x0016,  576},         /* 0110 1000 */
+    { 8, 0x00E6,  640},         /* 0110 0111 */
+    { 9, 0x0066,  704},         /* 0110 0110 0 */
+    { 9, 0x0166,  768},         /* 0110 0110 1 */
+    { 9, 0x0096,  832},         /* 0110 1001 0 */
+    { 9, 0x0196,  896},         /* 0110 1001 1 */
+    { 9, 0x0056,  960},         /* 0110 1010 0 */
+    { 9, 0x0156, 1024},         /* 0110 1010 1 */
+    { 9, 0x00D6, 1088},         /* 0110 1011 0 */
+    { 9, 0x01D6, 1152},         /* 0110 1011 1 */
+    { 9, 0x0036, 1216},         /* 0110 1100 0 */
+    { 9, 0x0136, 1280},         /* 0110 1100 1 */
+    { 9, 0x00B6, 1344},         /* 0110 1101 0 */
+    { 9, 0x01B6, 1408},         /* 0110 1101 1 */
+    { 9, 0x0032, 1472},         /* 0100 1100 0 */
+    { 9, 0x0132, 1536},         /* 0100 1100 1 */
+    { 9, 0x00B2, 1600},         /* 0100 1101 0 */
+    { 6, 0x0006, 1664},         /* 0110 00 */
+    { 9, 0x01B2, 1728},         /* 0100 1101 1 */
+    {11, 0x0080, 1792},         /* 0000 0001 000 */
+    {11, 0x0180, 1856},         /* 0000 0001 100 */
+    {11, 0x0580, 1920},         /* 0000 0001 101 */
+    {12, 0x0480, 1984},         /* 0000 0001 0010 */
+    {12, 0x0C80, 2048},         /* 0000 0001 0011 */
+    {12, 0x0280, 2112},         /* 0000 0001 0100 */
+    {12, 0x0A80, 2176},         /* 0000 0001 0101 */
+    {12, 0x0680, 2240},         /* 0000 0001 0110 */
+    {12, 0x0E80, 2304},         /* 0000 0001 0111 */
+    {12, 0x0380, 2368},         /* 0000 0001 1100 */
+    {12, 0x0B80, 2432},         /* 0000 0001 1101 */
+    {12, 0x0780, 2496},         /* 0000 0001 1110 */
+    {12, 0x0F80, 2560},         /* 0000 0001 1111 */
+};
+
+static const t4_run_table_entry_t t4_black_codes[] =
+{
+    {10, 0x03B0,    0},         /* 0000 1101 11 */
+    { 3, 0x0002,    1},         /* 010 */
+    { 2, 0x0003,    2},         /* 11 */
+    { 2, 0x0001,    3},         /* 10 */
+    { 3, 0x0006,    4},         /* 011 */
+    { 4, 0x000C,    5},         /* 0011 */
+    { 4, 0x0004,    6},         /* 0010 */
+    { 5, 0x0018,    7},         /* 0001 1 */
+    { 6, 0x0028,    8},         /* 0001 01 */
+    { 6, 0x0008,    9},         /* 0001 00 */
+    { 7, 0x0010,   10},         /* 0000 100 */
+    { 7, 0x0050,   11},         /* 0000 101 */
+    { 7, 0x0070,   12},         /* 0000 111 */
+    { 8, 0x0020,   13},         /* 0000 0100 */
+    { 8, 0x00E0,   14},         /* 0000 0111 */
+    { 9, 0x0030,   15},         /* 0000 1100 0 */
+    {10, 0x03A0,   16},         /* 0000 0101 11 */
+    {10, 0x0060,   17},         /* 0000 0110 00 */
+    {10, 0x0040,   18},         /* 0000 0010 00 */
+    {11, 0x0730,   19},         /* 0000 1100 111 */
+    {11, 0x00B0,   20},         /* 0000 1101 000 */
+    {11, 0x01B0,   21},         /* 0000 1101 100 */
+    {11, 0x0760,   22},         /* 0000 0110 111 */
+    {11, 0x00A0,   23},         /* 0000 0101 000 */
+    {11, 0x0740,   24},         /* 0000 0010 111 */
+    {11, 0x00C0,   25},         /* 0000 0011 000 */
+    {12, 0x0530,   26},         /* 0000 1100 1010 */
+    {12, 0x0D30,   27},         /* 0000 1100 1011 */
+    {12, 0x0330,   28},         /* 0000 1100 1100 */
+    {12, 0x0B30,   29},         /* 0000 1100 1101 */
+    {12, 0x0160,   30},         /* 0000 0110 1000 */
+    {12, 0x0960,   31},         /* 0000 0110 1001 */
+    {12, 0x0560,   32},         /* 0000 0110 1010 */
+    {12, 0x0D60,   33},         /* 0000 0110 1011 */
+    {12, 0x04B0,   34},         /* 0000 1101 0010 */
+    {12, 0x0CB0,   35},         /* 0000 1101 0011 */
+    {12, 0x02B0,   36},         /* 0000 1101 0100 */
+    {12, 0x0AB0,   37},         /* 0000 1101 0101 */
+    {12, 0x06B0,   38},         /* 0000 1101 0110 */
+    {12, 0x0EB0,   39},         /* 0000 1101 0111 */
+    {12, 0x0360,   40},         /* 0000 0110 1100 */
+    {12, 0x0B60,   41},         /* 0000 0110 1101 */
+    {12, 0x05B0,   42},         /* 0000 1101 1010 */
+    {12, 0x0DB0,   43},         /* 0000 1101 1011 */
+    {12, 0x02A0,   44},         /* 0000 0101 0100 */
+    {12, 0x0AA0,   45},         /* 0000 0101 0101 */
+    {12, 0x06A0,   46},         /* 0000 0101 0110 */
+    {12, 0x0EA0,   47},         /* 0000 0101 0111 */
+    {12, 0x0260,   48},         /* 0000 0110 0100 */
+    {12, 0x0A60,   49},         /* 0000 0110 0101 */
+    {12, 0x04A0,   50},         /* 0000 0101 0010 */
+    {12, 0x0CA0,   51},         /* 0000 0101 0011 */
+    {12, 0x0240,   52},         /* 0000 0010 0100 */
+    {12, 0x0EC0,   53},         /* 0000 0011 0111 */
+    {12, 0x01C0,   54},         /* 0000 0011 1000 */
+    {12, 0x0E40,   55},         /* 0000 0010 0111 */
+    {12, 0x0140,   56},         /* 0000 0010 1000 */
+    {12, 0x01A0,   57},         /* 0000 0101 1000 */
+    {12, 0x09A0,   58},         /* 0000 0101 1001 */
+    {12, 0x0D40,   59},         /* 0000 0010 1011 */
+    {12, 0x0340,   60},         /* 0000 0010 1100 */
+    {12, 0x05A0,   61},         /* 0000 0101 1010 */
+    {12, 0x0660,   62},         /* 0000 0110 0110 */
+    {12, 0x0E60,   63},         /* 0000 0110 0111 */
+    {10, 0x03C0,   64},         /* 0000 0011 11 */
+    {12, 0x0130,  128},         /* 0000 1100 1000 */
+    {12, 0x0930,  192},         /* 0000 1100 1001 */
+    {12, 0x0DA0,  256},         /* 0000 0101 1011 */
+    {12, 0x0CC0,  320},         /* 0000 0011 0011 */
+    {12, 0x02C0,  384},         /* 0000 0011 0100 */
+    {12, 0x0AC0,  448},         /* 0000 0011 0101 */
+    {13, 0x06C0,  512},         /* 0000 0011 0110 0 */
+    {13, 0x16C0,  576},         /* 0000 0011 0110 1 */
+    {13, 0x0A40,  640},         /* 0000 0010 0101 0 */
+    {13, 0x1A40,  704},         /* 0000 0010 0101 1 */
+    {13, 0x0640,  768},         /* 0000 0010 0110 0 */
+    {13, 0x1640,  832},         /* 0000 0010 0110 1 */
+    {13, 0x09C0,  896},         /* 0000 0011 1001 0 */
+    {13, 0x19C0,  960},         /* 0000 0011 1001 1 */
+    {13, 0x05C0, 1024},         /* 0000 0011 1010 0 */
+    {13, 0x15C0, 1088},         /* 0000 0011 1010 1 */
+    {13, 0x0DC0, 1152},         /* 0000 0011 1011 0 */
+    {13, 0x1DC0, 1216},         /* 0000 0011 1011 1 */
+    {13, 0x0940, 1280},         /* 0000 0010 1001 0 */
+    {13, 0x1940, 1344},         /* 0000 0010 1001 1 */
+    {13, 0x0540, 1408},         /* 0000 0010 1010 0 */
+    {13, 0x1540, 1472},         /* 0000 0010 1010 1 */
+    {13, 0x0B40, 1536},         /* 0000 0010 1101 0 */
+    {13, 0x1B40, 1600},         /* 0000 0010 1101 1 */
+    {13, 0x04C0, 1664},         /* 0000 0011 0010 0 */
+    {13, 0x14C0, 1728},         /* 0000 0011 0010 1 */
+    {11, 0x0080, 1792},         /* 0000 0001 000 */
+    {11, 0x0180, 1856},         /* 0000 0001 100 */
+    {11, 0x0580, 1920},         /* 0000 0001 101 */
+    {12, 0x0480, 1984},         /* 0000 0001 0010 */
+    {12, 0x0C80, 2048},         /* 0000 0001 0011 */
+    {12, 0x0280, 2112},         /* 0000 0001 0100 */
+    {12, 0x0A80, 2176},         /* 0000 0001 0101 */
+    {12, 0x0680, 2240},         /* 0000 0001 0110 */
+    {12, 0x0E80, 2304},         /* 0000 0001 0111 */
+    {12, 0x0380, 2368},         /* 0000 0001 1100 */
+    {12, 0x0B80, 2432},         /* 0000 0001 1101 */
+    {12, 0x0780, 2496},         /* 0000 0001 1110 */
+    {12, 0x0F80, 2560},         /* 0000 0001 1111 */
+};
+
+static int encode_row(t4_state_t *s);
+
+static void make_header(t4_state_t *s, char *header)
+{
+    time_t now;
+    struct tm tm;
+    static const char *months[] =
+    {
+        "Jan",
+        "Feb",
+        "Mar",
+        "Apr",
+        "May",
+        "Jun",
+        "Jul",
+        "Aug",
+        "Sep",
+        "Oct",
+        "Nov",
+        "Dec"
+    };
+
+    time(&now);
+    tm = *localtime(&now);
+    snprintf(header,
+             132,
+             "  %2d-%s-%d  %02d:%02d    %-50s %-21s   p.%d",
+             tm.tm_mday,
+             months[tm.tm_mon],
+             tm.tm_year + 1900,
+             tm.tm_hour,
+             tm.tm_min,
+             s->t4_t6_tx.header_info,
+             s->tiff.local_ident,
+             s->current_page + 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+static int t4_tx_put_fax_header(t4_state_t *s)
+{
+    int row;
+    int i;
+    int repeats;
+    int pattern;
+    int row_bufptr;
+    char *t;
+    char header[132 + 1];
+
+    /* Modify the resulting image to include a header line, typical of hardware FAX machines */
+    make_header(s, header);
+    switch (s->y_resolution)
+    {
+    case T4_Y_RESOLUTION_1200:
+        repeats = 12;
+        break;
+    case T4_Y_RESOLUTION_800:
+        repeats = 8;
+        break;
+    case T4_Y_RESOLUTION_600:
+        repeats = 6;
+        break;
+    case T4_Y_RESOLUTION_SUPERFINE:
+        repeats = 4;
+        break;
+    case T4_Y_RESOLUTION_300:
+        repeats = 3;
+        break;
+    case T4_Y_RESOLUTION_FINE:
+        repeats = 2;
+        break;
+    default:
+        repeats = 1;
+        break;
+    }
+    for (row = 0;  row < 16;  row++)
+    {
+        t = header;
+        row_bufptr = 0;
+        for (t = header;  *t  &&  row_bufptr <= s->bytes_per_row - 2;  t++)
+        {
+            pattern = header_font[(uint8_t) *t][row];
+            s->row_buf[row_bufptr++] = (uint8_t) (pattern >> 8);
+            s->row_buf[row_bufptr++] = (uint8_t) (pattern & 0xFF);
+        }
+        for (  ;  row_bufptr < s->bytes_per_row;  )
+            s->row_buf[row_bufptr++] = 0;
+        for (i = 0;  i < repeats;  i++)
+        {
+            if (encode_row(s))
+                return -1;
+        }
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int test_resolution(int res_unit, float actual, float expected)
+{
+    if (res_unit == RESUNIT_INCH)
+        actual *= 1.0f/CM_PER_INCH;
+    return (expected*0.95f <= actual  &&  actual <= expected*1.05f);
+}
+/*- End of function --------------------------------------------------------*/
+
+static int get_tiff_directory_info(t4_state_t *s)
+{
+    static const struct
+    {
+        float resolution;
+        int code;
+    } x_res_table[] =
+    {
+        { 102.0f/CM_PER_INCH, T4_X_RESOLUTION_R4},
+        { 204.0f/CM_PER_INCH, T4_X_RESOLUTION_R8},
+        { 300.0f/CM_PER_INCH, T4_X_RESOLUTION_300},
+        { 408.0f/CM_PER_INCH, T4_X_RESOLUTION_R16},
+        { 600.0f/CM_PER_INCH, T4_X_RESOLUTION_600},
+        { 800.0f/CM_PER_INCH, T4_X_RESOLUTION_800},
+        {1200.0f/CM_PER_INCH, T4_X_RESOLUTION_1200},
+        {             -1.00f, -1}
+    };
+    static const struct
+    {
+        float resolution;
+        int code;
+        int max_rows_to_next_1d_row;
+    } y_res_table[] =
+    {
+        {             38.50f, T4_Y_RESOLUTION_STANDARD, 2},
+        {             77.00f, T4_Y_RESOLUTION_FINE, 4},
+        { 300.0f/CM_PER_INCH, T4_Y_RESOLUTION_300, 6},
+        {            154.00f, T4_Y_RESOLUTION_SUPERFINE, 8},
+        { 600.0f/CM_PER_INCH, T4_Y_RESOLUTION_600, 12},
+        { 800.0f/CM_PER_INCH, T4_Y_RESOLUTION_800, 16},
+        {1200.0f/CM_PER_INCH, T4_Y_RESOLUTION_1200, 24},
+        {             -1.00f, -1, -1}
+    };
+    uint16_t res_unit;
+    uint16_t parm16;
+    uint32_t parm32;
+    float x_resolution;
+    float y_resolution;
+    int i;
+    t4_tiff_state_t *t;
+
+    t = &s->tiff;
+    parm16 = 0;
+    TIFFGetField(t->tiff_file, TIFFTAG_BITSPERSAMPLE, &parm16);
+    if (parm16 != 1)
+        return -1;
+    TIFFGetField(t->tiff_file, TIFFTAG_SAMPLESPERPIXEL, &parm16);
+    if (parm16 != 1)
+        return -1;
+    parm32 = 0;
+    TIFFGetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, &parm32);
+    s->image_width = parm32;
+    s->bytes_per_row = (s->image_width + 7)/8;
+    parm32 = 0;
+    TIFFGetField(t->tiff_file, TIFFTAG_IMAGELENGTH, &parm32);
+    s->image_length = parm32;
+    x_resolution = 0.0f;
+    TIFFGetField(t->tiff_file, TIFFTAG_XRESOLUTION, &x_resolution);
+    y_resolution = 0.0f;
+    TIFFGetField(t->tiff_file, TIFFTAG_YRESOLUTION, &y_resolution);
+    res_unit = RESUNIT_INCH;
+    TIFFGetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, &res_unit);
+    t->photo_metric = PHOTOMETRIC_MINISWHITE;
+    TIFFGetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, &t->photo_metric);
+    if (t->photo_metric != PHOTOMETRIC_MINISWHITE)
+        span_log(&s->logging, SPAN_LOG_FLOW, "%s: Photometric needs swapping.\n", t->file);
+    t->fill_order = FILLORDER_LSB2MSB;
+#if 0
+    TIFFGetField(t->tiff_file, TIFFTAG_FILLORDER, &t->fill_order);
+    if (t->fill_order != FILLORDER_LSB2MSB)
+        span_log(&s->logging, SPAN_LOG_FLOW, "%s: Fill order needs swapping.\n", t->file);
+#endif
+
+    /* Allow a little range for the X resolution in centimeters. The spec doesn't pin down the
+       precise value. The other value should be exact. */
+    /* Treat everything we can't match as R8. Most FAXes are this resolution anyway. */
+    s->x_resolution = T4_X_RESOLUTION_R8;
+    for (i = 0;  x_res_table[i].code > 0;  i++)
+    {
+        if (test_resolution(res_unit, x_resolution, x_res_table[i].resolution))
+        {
+            s->x_resolution = x_res_table[i].code;
+            break;
+        }
+    }
+
+    s->y_resolution = T4_Y_RESOLUTION_STANDARD;
+    s->t4_t6_tx.max_rows_to_next_1d_row = 2;
+    for (i = 0;  y_res_table[i].code > 0;  i++)
+    {
+        if (test_resolution(res_unit, y_resolution, y_res_table[i].resolution))
+        {
+            s->y_resolution = y_res_table[i].code;
+            s->t4_t6_tx.max_rows_to_next_1d_row = y_res_table[i].max_rows_to_next_1d_row;
+            break;
+        }
+    }
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int test_tiff_directory_info(t4_state_t *s)
+{
+    static const struct
+    {
+        float resolution;
+        int code;
+    } x_res_table[] =
+    {
+        { 102.0f/CM_PER_INCH, T4_X_RESOLUTION_R4},
+        { 204.0f/CM_PER_INCH, T4_X_RESOLUTION_R8},
+        { 300.0f/CM_PER_INCH, T4_X_RESOLUTION_300},
+        { 408.0f/CM_PER_INCH, T4_X_RESOLUTION_R16},
+        { 600.0f/CM_PER_INCH, T4_X_RESOLUTION_600},
+        { 800.0f/CM_PER_INCH, T4_X_RESOLUTION_800},
+        {1200.0f/CM_PER_INCH, T4_X_RESOLUTION_1200},
+        {             -1.00f, -1}
+    };
+    static const struct
+    {
+        float resolution;
+        int code;
+        int max_rows_to_next_1d_row;
+    } y_res_table[] =
+    {
+        {             38.50f, T4_Y_RESOLUTION_STANDARD, 2},
+        {             77.00f, T4_Y_RESOLUTION_FINE, 4},
+        { 300.0f/CM_PER_INCH, T4_Y_RESOLUTION_300, 6},
+        {            154.00f, T4_Y_RESOLUTION_SUPERFINE, 8},
+        { 600.0f/CM_PER_INCH, T4_Y_RESOLUTION_600, 12},
+        { 800.0f/CM_PER_INCH, T4_Y_RESOLUTION_800, 16},
+        {1200.0f/CM_PER_INCH, T4_Y_RESOLUTION_1200, 24},
+        {             -1.00f, -1, -1}
+    };
+    uint16_t res_unit;
+    uint16_t parm16;
+    uint32_t parm32;
+    float x_resolution;
+    float y_resolution;
+    int i;
+    t4_tiff_state_t *t;
+
+    t = &s->tiff;
+    parm16 = 0;
+    TIFFGetField(t->tiff_file, TIFFTAG_BITSPERSAMPLE, &parm16);
+    if (parm16 != 1)
+        return -1;
+    parm32 = 0;
+    TIFFGetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, &parm32);
+    if (s->image_width != (int) parm32)
+        return 1;
+    x_resolution = 0.0f;
+    TIFFGetField(t->tiff_file, TIFFTAG_XRESOLUTION, &x_resolution);
+    y_resolution = 0.0f;
+    TIFFGetField(t->tiff_file, TIFFTAG_YRESOLUTION, &y_resolution);
+    res_unit = RESUNIT_INCH;
+    TIFFGetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, &res_unit);
+
+    /* Allow a little range for the X resolution in centimeters. The spec doesn't pin down the
+       precise value. The other value should be exact. */
+    /* Treat everything we can't match as R8. Most FAXes are this resolution anyway. */
+    for (i = 0;  x_res_table[i].code > 0;  i++)
+    {
+        if (test_resolution(res_unit, x_resolution, x_res_table[i].resolution))
+            break;
+    }
+    if (s->x_resolution != x_res_table[i].code)
+        return 1;
+    for (i = 0;  y_res_table[i].code > 0;  i++)
+    {
+        if (test_resolution(res_unit, y_resolution, y_res_table[i].resolution))
+            break;
+    }
+    if (s->y_resolution != y_res_table[i].code)
+        return 1;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int get_tiff_total_pages(t4_state_t *s)
+{
+    int max;
+
+    /* Each page *should* contain the total number of pages, but can this be
+       trusted? Some files say 0. Actually searching for the last page is
+       more reliable. */
+    max = 0;
+    while (TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) max))
+        max++;
+    /* Back to the previous page */
+    if (!TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) s->current_page))
+        return -1;
+    return max;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int open_tiff_input_file(t4_state_t *s, const char *file)
+{
+    if ((s->tiff.tiff_file = TIFFOpen(file, "r")) == NULL)
+        return -1;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int read_tiff_image(t4_state_t *s)
+{
+    int row;
+    int image_length;
+    int i;
+
+    image_length = 0;
+    TIFFGetField(s->tiff.tiff_file, TIFFTAG_IMAGELENGTH, &image_length);
+    for (row = 0;  row < image_length;  row++)
+    {
+        if (TIFFReadScanline(s->tiff.tiff_file, s->row_buf, row, 0) <= 0)
+        {
+            span_log(&s->logging, SPAN_LOG_WARNING, "%s: Read error at row %d.\n", s->tiff.file, row);
+            break;
+        }
+        if (s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE)
+        {
+            for (i = 0;  i < s->bytes_per_row;  i++)
+                s->row_buf[i] = ~s->row_buf[i];
+        }
+        if (s->tiff.fill_order != FILLORDER_LSB2MSB)
+            bit_reverse(s->row_buf, s->row_buf, s->bytes_per_row);
+        if (encode_row(s))
+            return -1;
+    }
+    return image_length;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int close_tiff_input_file(t4_state_t *s)
+{
+    TIFFClose(s->tiff.tiff_file);
+    s->tiff.tiff_file = NULL;
+    if (s->tiff.file)
+        free((char *) s->tiff.file);
+    s->tiff.file = NULL;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void update_row_bit_info(t4_state_t *s)
+{
+    if (s->row_bits > s->max_row_bits)
+        s->max_row_bits = s->row_bits;
+    if (s->row_bits < s->min_row_bits)
+        s->min_row_bits = s->row_bits;
+    s->row_bits = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int free_buffers(t4_state_t *s)
+{
+    if (s->image_buffer)
+    {
+        free(s->image_buffer);
+        s->image_buffer = NULL;
+        s->image_buffer_size = 0;
+    }
+    if (s->cur_runs)
+    {
+        free(s->cur_runs);
+        s->cur_runs = NULL;
+    }
+    if (s->ref_runs)
+    {
+        free(s->ref_runs);
+        s->ref_runs = NULL;
+    }
+    if (s->row_buf)
+    {
+        free(s->row_buf);
+        s->row_buf = NULL;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int row_to_run_lengths(uint32_t list[], const uint8_t row[], int width)
+{
+    uint32_t flip;
+    uint32_t x;
+    int span;
+    int entry;
+    int frag;
+    int rem;
+    int limit;
+    int i;
+    int pos;
+
+    /* Deal with whole words first. We know we are starting on a word boundary. */
+    entry = 0;
+    flip = 0;
+    limit = (width >> 3) & ~3;
+    span = 0;
+    pos = 0;
+    for (i = 0;  i < limit;  i += sizeof(uint32_t))
+    {
+        x = *((uint32_t *) &row[i]);
+        if (x != flip)
+        {
+            x = ((uint32_t) row[i] << 24) | ((uint32_t) row[i + 1] << 16) | ((uint32_t) row[i + 2] << 8) | ((uint32_t) row[i + 3]);
+            /* We know we are going to find at least one transition. */
+            frag = 31 - top_bit(x ^ flip);
+            pos += ((i << 3) - span + frag);
+            list[entry++] = pos;
+            x <<= frag;
+            flip ^= 0xFFFFFFFF;
+            rem = 32 - frag;
+            /* Now see if there are any more */
+            while ((frag = 31 - top_bit(x ^ flip)) < rem)
+            {
+                pos += frag;
+                list[entry++] = pos;
+                x <<= frag;
+                flip ^= 0xFFFFFFFF;
+                rem -= frag;
+            }
+            /* Save the remainder of the word */
+            span = (i << 3) + 32 - rem;
+        }
+    }
+    /* Now deal with some whole bytes, if there are any left. */
+    limit = width >> 3;
+    flip &= 0xFF000000;
+    if (i < limit)
+    {
+        for (  ;  i < limit;  i++)
+        {
+            x = (uint32_t) row[i] << 24;
+            if (x != flip)
+            {
+                /* We know we are going to find at least one transition. */
+                frag = 31 - top_bit(x ^ flip);
+                pos += ((i << 3) - span + frag);
+                list[entry++] = pos;
+                x <<= frag;
+                flip ^= 0xFF000000;
+                rem = 8 - frag;
+                /* Now see if there are any more */
+                while ((frag = 31 - top_bit(x ^ flip)) < rem)
+                {
+                    pos += frag;
+                    list[entry++] = pos;
+                    x <<= frag;
+                    flip ^= 0xFF000000;
+                    rem -= frag;
+                }   
+                /* Save the remainder of the word */
+                span = (i << 3) + 8 - rem;
+            }
+        }
+    }
+    /* Deal with any left over fractional byte. */
+    span = (i << 3) - span;
+    if ((rem = width & 7))
+    {
+        x = row[i];
+        x <<= 24;
+        do
+        {
+            frag = 31 - top_bit(x ^ flip);
+            if (frag > rem)
+                frag = rem;
+            pos += (span + frag);
+            list[entry++] = pos;
+            x <<= frag;
+            span = 0;
+            flip ^= 0xFF000000;
+            rem -= frag;
+        }
+        while (rem > 0);
+    }
+    else
+    {
+        if (span)
+        {
+            pos += span;
+            list[entry++] = pos;
+        }
+    }
+    return entry;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ int put_encoded_bits(t4_state_t *s, uint32_t bits, int length)
+{
+    uint8_t *t;
+
+    /* We might be called with a large length value, to spew out a mass of zero bits for
+       minimum row length padding. */
+    s->tx_bitstream |= (bits << s->tx_bits);
+    s->tx_bits += length;
+    s->row_bits += length;
+    if ((s->image_size + (s->tx_bits + 7)/8) >= s->image_buffer_size)
+    {
+        if ((t = realloc(s->image_buffer, s->image_buffer_size + 100*s->bytes_per_row)) == NULL)
+            return -1;
+        s->image_buffer = t;
+        s->image_buffer_size += 100*s->bytes_per_row;
+    }
+    while (s->tx_bits >= 8)
+    {
+        s->image_buffer[s->image_size++] = (uint8_t) (s->tx_bitstream & 0xFF);
+        s->tx_bitstream >>= 8;
+        s->tx_bits -= 8;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+/*
+ * Write the sequence of codes that describes
+ * the specified span of zero's or one's.  The
+ * appropriate table that holds the make-up and
+ * terminating codes is supplied.
+ */
+static __inline__ int put_1d_span(t4_state_t *s, int32_t span, const t4_run_table_entry_t *tab)
+{
+    const t4_run_table_entry_t *te;
+
+    te = &tab[63 + (2560 >> 6)];
+    while (span >= 2560 + 64)
+    {
+        if (put_encoded_bits(s, te->code, te->length))
+            return -1;
+        span -= te->run_length;
+    }
+    te = &tab[63 + (span >> 6)];
+    if (span >= 64)
+    {
+        if (put_encoded_bits(s, te->code, te->length))
+            return -1;
+        span -= te->run_length;
+    }
+    if (put_encoded_bits(s, tab[span].code, tab[span].length))
+        return -1;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+#define pixel_is_black(x,bit) (((x)[(bit) >> 3] << ((bit) & 7)) & 0x80)
+
+/*
+ * Write an EOL code to the output stream.  We also handle writing the tag
+ * bit for the next scanline when doing 2D encoding.
+ */
+static void encode_eol(t4_state_t *s)
+{
+    uint32_t code;
+    int length;
+
+    if (s->line_encoding == T4_COMPRESSION_ITU_T4_2D)
+    {
+        code = 0x0800 | ((!s->row_is_2d) << 12);
+        length = 13;
+    }
+    else
+    {
+        /* T.4 1D EOL, or T.6 EOFB */
+        code = 0x800;
+        length = 12;
+    }
+    if (s->row_bits)
+    {
+        /* We may need to pad the row to a minimum length, unless we are in T.6 mode.
+           In T.6 we only come here at the end of the page to add the EOFB marker, which
+           is like two 1D EOLs. */
+        if (s->line_encoding != T4_COMPRESSION_ITU_T6)
+        {
+            if (s->row_bits + length < s->t4_t6_tx.min_bits_per_row)
+                put_encoded_bits(s, 0, s->t4_t6_tx.min_bits_per_row - (s->row_bits + length));
+        }
+        put_encoded_bits(s, code, length);
+        update_row_bit_info(s);
+    }
+    else
+    {
+        /* We don't pad zero length rows. They are the consecutive EOLs which end a page. */
+        put_encoded_bits(s, code, length);
+        /* Don't do the full update row bit info, or the minimum suddenly drops to the
+           length of an EOL. Just clear the row bits, so we treat the next EOL as an
+           end of page EOL, with no padding. */
+        s->row_bits = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+/*
+ * 2D-encode a row of pixels.  Consult ITU specification T.4 for the algorithm.
+ */
+static void encode_2d_row(t4_state_t *s)
+{
+    static const t4_run_table_entry_t codes[] =
+    {
+        { 7, 0x60, 0 },         /* VR3          0000 011 */
+        { 6, 0x30, 0 },         /* VR2          0000 11 */
+        { 3, 0x06, 0 },         /* VR1          011 */
+        { 1, 0x01, 0 },         /* V0           1 */
+        { 3, 0x02, 0 },         /* VL1          010 */
+        { 6, 0x10, 0 },         /* VL2          0000 10 */
+        { 7, 0x20, 0 },         /* VL3          0000 010 */
+        { 3, 0x04, 0 },         /* horizontal   001 */
+        { 4, 0x08, 0 }          /* pass         0001 */
+    };
+
+    /* The reference or starting changing element on the coding line. At the start of the coding
+       line, a0 is set on an imaginary white changing element situated just before the first element
+       on the line. During the coding of the coding line, the position of a0 is defined by the
+       previous coding mode. (See T.4/4.2.1.3.2.) */
+    int a0;
+    /* The next changing element to the right of a0 on the coding line. */
+    int a1;
+    /* The next changing element to the right of a1 on the coding line. */
+    int a2;
+    /* The first changing element on the reference line to the right of a0 and of opposite colour to a0. */
+    int b1;
+    /* The next changing element to the right of b1 on the reference line. */
+    int b2;
+    int diff;
+    int a_cursor;
+    int b_cursor;
+    int cur_steps;
+    uint32_t *p;
+
+    /*
+                                                    b1          b2 
+            XX  XX  XX  XX  XX  --  --  --  --  --  XX  XX  XX  --  --  --  --  --
+            XX  XX  XX  --  --  --  --  --  XX  XX  XX  XX  XX  XX  --  --  --  --
+                        a0                  a1                      a2
+
+
+        a)  Pass mode
+            This mode is identified when the position of b2 lies to the left of a1. When this mode
+            has been coded, a0 is set on the element of the coding line below b2 in preparation for
+            the next coding (i.e. on a0').
+            
+                                    b1          b2 
+            XX  XX  XX  XX  --  --  XX  XX  XX  --  --  --  --  --
+            XX  XX  --  --  --  --  --  --  --  --  --  --  XX  XX 
+                    a0                          a0'         a1
+                                Pass mode
+                                
+
+            However, the state where b2 occurs just above a1, as shown in the figure below, is not
+            considered as a pass mode.
+
+                                    b1          b2 
+            XX  XX  XX  XX  --  --  XX  XX  XX  --  --  --  --  --
+            XX  XX  --  --  --  --  --  --  --  XX  XX  XX  XX  XX
+                    a0                          a1
+                                Not pass mode
+
+
+        b)  Vertical mode
+            When this mode is identified, the position of a1 is coded relative to the position of b1.
+            The relative distance a1b1 can take on one of seven values V(0), VR(1), VR(2), VR(3),
+            VL(1), VL(2) and VL(3), each of which is represented by a separate code word. The
+            subscripts R and L indicate that a1 is to the right or left respectively of b1, and the
+            number in brackets indicates the value of the distance a1b1. After vertical mode coding
+            has occurred, the position of a0 is set on a1 (see figure below).
+
+        c)  Horizontal mode
+            When this mode is identified, both the run-lengths a0a1 and a1a2 are coded using the code
+            words H + M(a0a1) + M(a1a2). H is the flag code word 001 taken from the two-dimensional
+            code table. M(a0a1) and M(a1a2) are code words which represent the length and "colour"
+            of the runs a0a1 and a1a2 respectively and are taken from the appropriate white or black
+            one-dimensional code tables. After a horizontal mode coding, the position of a0 is set on
+            a2 (see figure below).
+
+                                                            Vertical
+                                                            <a1 b1>
+                                                                    b1              b2 
+            --  XX  XX  XX  XX  XX  --  --  --  --  --  --  --  --  XX  XX  XX  XX  --  --  --
+            --  --  --  --  --  --  --  --  --  --  --  --  XX  XX  XX  XX  XX  XX  XX  --  --
+                                    a0                      a1                          a2
+                                   <-------- a0a1 --------><-------- a1a2 ------------>
+                                                    Horizontal mode
+                          Vertical and horizontal modes
+     */
+    /* The following implements the 2-D encoding section of the flow chart in Figure7/T.4 */
+    cur_steps = row_to_run_lengths(s->cur_runs, s->row_buf, s->image_width);
+    /* Stretch the row a little, so when we step by 2 we are guaranteed to
+       hit an entry showing the row length */
+    s->cur_runs[cur_steps] =
+    s->cur_runs[cur_steps + 1] =
+    s->cur_runs[cur_steps + 2] = s->cur_runs[cur_steps - 1];
+
+    a0 = 0;
+    a1 = s->cur_runs[0];
+    b1 = s->ref_runs[0];
+    a_cursor = 0;
+    b_cursor = 0;
+    for (;;)
+    {
+        b2 = s->ref_runs[b_cursor + 1];
+        if (b2 >= a1)
+        {
+            diff = b1 - a1;
+            if (abs(diff) <= 3)
+            {
+                /* Vertical mode coding */
+                put_encoded_bits(s, codes[diff + 3].code, codes[diff + 3].length);
+                a0 = a1;
+                a_cursor++;
+            }
+            else
+            {
+                /* Horizontal mode coding */
+                a2 = s->cur_runs[a_cursor + 1];
+                put_encoded_bits(s, codes[7].code, codes[7].length);
+                if (a0 + a1 == 0  ||  pixel_is_black(s->row_buf, a0) == 0)
+                {
+                    put_1d_span(s, a1 - a0, t4_white_codes);
+                    put_1d_span(s, a2 - a1, t4_black_codes);
+                }
+                else
+                {
+                    put_1d_span(s, a1 - a0, t4_black_codes);
+                    put_1d_span(s, a2 - a1, t4_white_codes);
+                }
+                a0 = a2;
+                a_cursor += 2;
+            }
+            if (a0 >= s->image_width)
+                break;
+            if (a_cursor >= cur_steps)
+                a_cursor = cur_steps - 1;
+            a1 = s->cur_runs[a_cursor];
+        }
+        else
+        {
+            /* Pass mode coding */
+            put_encoded_bits(s, codes[8].code, codes[8].length);
+            /* We now set a0 to somewhere in the middle of its current run,
+               but we know are aren't moving beyond that run. */
+            a0 = b2;
+            if (a0 >= s->image_width)
+                break;
+        }
+        /* We need to hunt for the correct position in the reference row, as the
+           runs there have no particular alignment with the runs in the current
+           row. */
+        if (pixel_is_black(s->row_buf, a0))
+            b_cursor |= 1;
+        else
+            b_cursor &= ~1;
+        if (a0 < (int) s->ref_runs[b_cursor])
+        {
+            for (  ;  b_cursor >= 0;  b_cursor -= 2)
+            {
+                if (a0 >= (int) s->ref_runs[b_cursor])
+                    break;
+            }
+            b_cursor += 2;
+        }
+        else
+        {
+            for (  ;  b_cursor < s->t4_t6_tx.ref_steps;  b_cursor += 2)
+            {
+                if (a0 < (int) s->ref_runs[b_cursor])
+                    break;
+            }
+            if (b_cursor >= s->t4_t6_tx.ref_steps)
+                b_cursor = s->t4_t6_tx.ref_steps - 1;
+        }
+        b1 = s->ref_runs[b_cursor];
+    }
+    /* Swap the buffers */
+    s->t4_t6_tx.ref_steps = cur_steps;
+    p = s->cur_runs;
+    s->cur_runs = s->ref_runs;
+    s->ref_runs = p;
+}
+/*- End of function --------------------------------------------------------*/
+
+/*
+ * 1D-encode a row of pixels. The encoding is a sequence of all-white or
+ * all-black spans of pixels encoded with Huffman codes.
+ */
+static void encode_1d_row(t4_state_t *s)
+{
+    int i;
+
+    /* Do our work in the reference row buffer, and it is already in place if
+       we need a reference row for a following 2D encoded row. */
+    s->t4_t6_tx.ref_steps = row_to_run_lengths(s->ref_runs, s->row_buf, s->image_width);
+    put_1d_span(s, s->ref_runs[0], t4_white_codes);
+    for (i = 1;  i < s->t4_t6_tx.ref_steps;  i++)
+        put_1d_span(s, s->ref_runs[i] - s->ref_runs[i - 1], (i & 1)  ?  t4_black_codes  :  t4_white_codes);
+    /* Stretch the row a little, so when we step by 2 we are guaranteed to
+       hit an entry showing the row length */
+    s->ref_runs[s->t4_t6_tx.ref_steps] =
+    s->ref_runs[s->t4_t6_tx.ref_steps + 1] =
+    s->ref_runs[s->t4_t6_tx.ref_steps + 2] = s->ref_runs[s->t4_t6_tx.ref_steps - 1];
+}
+/*- End of function --------------------------------------------------------*/
+
+static int encode_row(t4_state_t *s)
+{
+    switch (s->line_encoding)
+    {
+    case T4_COMPRESSION_ITU_T6:
+        /* T.6 compression is a trivial step up from T.4 2D, so we just
+           throw it in here. T.6 is only used with error correction,
+           so it does not need independantly compressed (i.e. 1D) lines
+           to recover from data errors. It doesn't need EOLs, either. */
+        if (s->row_bits)
+            update_row_bit_info(s);
+        encode_2d_row(s);
+        break;
+    case T4_COMPRESSION_ITU_T4_2D:
+        encode_eol(s);
+        if (s->row_is_2d)
+        {
+            encode_2d_row(s);
+            s->t4_t6_tx.rows_to_next_1d_row--;
+        }
+        else
+        {
+            encode_1d_row(s);
+            s->row_is_2d = TRUE;
+        }
+        if (s->t4_t6_tx.rows_to_next_1d_row <= 0)
+        {
+            /* Insert a row of 1D encoding */
+            s->row_is_2d = FALSE;
+            s->t4_t6_tx.rows_to_next_1d_row = s->t4_t6_tx.max_rows_to_next_1d_row - 1;
+        }
+        break;
+    default:
+    case T4_COMPRESSION_ITU_T4_1D:
+        encode_eol(s);
+        encode_1d_row(s);
+        break;
+    }
+    s->row++;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_set_row_read_handler(t4_state_t *s, t4_row_read_handler_t handler, void *user_data)
+{
+    s->t4_t6_tx.row_read_handler = handler;
+    s->t4_t6_tx.row_read_user_data = user_data;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t4_state_t *) t4_tx_init(t4_state_t *s, const char *file, int start_page, int stop_page)
+{
+    int run_space;
+
+    if (s == NULL)
+    {
+        if ((s = (t4_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "T.4");
+    s->rx = FALSE;
+
+    span_log(&s->logging, SPAN_LOG_FLOW, "Start tx document\n");
+
+    if (open_tiff_input_file(s, file) < 0)
+        return NULL;
+    s->tiff.file = strdup(file);
+    s->current_page =
+    s->tiff.start_page = (start_page >= 0)  ?  start_page  :  0;
+    s->tiff.stop_page = (stop_page >= 0)  ?  stop_page : INT_MAX;
+
+    if (!TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) s->current_page))
+        return NULL;
+    if (get_tiff_directory_info(s))
+    {
+        close_tiff_input_file(s);
+        return NULL;
+    }
+
+    s->t4_t6_tx.rows_to_next_1d_row = s->t4_t6_tx.max_rows_to_next_1d_row - 1;
+
+    s->tiff.pages_in_file = -1;
+
+    run_space = (s->image_width + 4)*sizeof(uint32_t);
+    if ((s->cur_runs = (uint32_t *) malloc(run_space)) == NULL)
+        return NULL;
+    if ((s->ref_runs = (uint32_t *) malloc(run_space)) == NULL)
+    {
+        free_buffers(s);
+        close_tiff_input_file(s);
+        return NULL;
+    }
+    if ((s->row_buf = malloc(s->bytes_per_row)) == NULL)
+    {
+        free_buffers(s);
+        close_tiff_input_file(s);
+        return NULL;
+    }
+    s->ref_runs[0] =
+    s->ref_runs[1] =
+    s->ref_runs[2] =
+    s->ref_runs[3] = s->image_width;
+    s->t4_t6_tx.ref_steps = 1;
+    s->image_buffer_size = 0;
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_start_page(t4_state_t *s)
+{
+    int row;
+    int i;
+    int run_space;
+    int len;
+    int old_image_width;
+    uint8_t *bufptr8;
+    uint32_t *bufptr;
+
+    span_log(&s->logging, SPAN_LOG_FLOW, "Start tx page %d\n", s->current_page);
+    if (s->current_page > s->tiff.stop_page)
+        return -1;
+    if (s->tiff.tiff_file == NULL)
+        return -1;
+    old_image_width = s->image_width;
+    if (s->t4_t6_tx.row_read_handler == NULL)
+    {
+#if defined(HAVE_LIBTIFF)
+        if (!TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) s->current_page))
+            return -1;
+        get_tiff_directory_info(s);
+#endif
+    }
+    s->image_size = 0;
+    s->tx_bitstream = 0;
+    s->tx_bits = 0;
+    s->row_is_2d = (s->line_encoding == T4_COMPRESSION_ITU_T6);
+    s->t4_t6_tx.rows_to_next_1d_row = s->t4_t6_tx.max_rows_to_next_1d_row - 1;
+
+    /* Allow for pages being of different width. */
+    run_space = (s->image_width + 4)*sizeof(uint32_t);
+    if (old_image_width != s->image_width)
+    {
+        s->bytes_per_row = (s->image_width + 7)/8;
+
+        if ((bufptr = (uint32_t *) realloc(s->cur_runs, run_space)) == NULL)
+            return -1;
+        s->cur_runs = bufptr;
+        if ((bufptr = (uint32_t *) realloc(s->ref_runs, run_space)) == NULL)
+            return -1;
+        s->ref_runs = bufptr;
+        if ((bufptr8 = realloc(s->row_buf, s->bytes_per_row)) == NULL)
+            return -1;
+        s->row_buf = bufptr8;
+    }
+    s->ref_runs[0] =
+    s->ref_runs[1] =
+    s->ref_runs[2] =
+    s->ref_runs[3] = s->image_width;
+    s->t4_t6_tx.ref_steps = 1;
+
+    s->row_bits = 0;
+    s->min_row_bits = INT_MAX;
+    s->max_row_bits = 0;
+
+    if (s->t4_t6_tx.header_info  &&  s->t4_t6_tx.header_info[0])
+    {
+        if (t4_tx_put_fax_header(s))
+            return -1;
+    }
+    if (s->t4_t6_tx.row_read_handler)
+    {
+        for (row = 0;  ;  row++)
+        {
+            if ((len = s->t4_t6_tx.row_read_handler(s->t4_t6_tx.row_read_user_data, s->row_buf, s->bytes_per_row)) < 0)
+            {
+                span_log(&s->logging, SPAN_LOG_WARNING, "%s: Read error at row %d.\n", s->tiff.file, row);
+                break;
+            }
+            if (len == 0)
+                break;
+            if (encode_row(s))
+                return -1;
+        }
+        s->image_length = row;
+    }
+    else
+    {
+        if ((s->image_length = read_tiff_image(s)) < 0)
+            return -1;
+    }
+    if (s->line_encoding == T4_COMPRESSION_ITU_T6)
+    {
+        /* Attach an EOFB (end of facsimile block == 2 x EOLs) to the end of the page */
+        for (i = 0;  i < EOLS_TO_END_T6_TX_PAGE;  i++)
+            encode_eol(s);
+    }
+    else
+    {
+        /* Attach an RTC (return to control == 6 x EOLs) to the end of the page */
+        s->row_is_2d = FALSE;
+        for (i = 0;  i < EOLS_TO_END_T4_TX_PAGE;  i++)
+            encode_eol(s);
+    }
+
+    /* Force any partial byte in progress to flush using ones. Any post EOL padding when
+       sending is normally ones, so this is consistent. */
+    put_encoded_bits(s, 0xFF, 7);
+    s->t4_t6_tx.bit_pos = 7;
+    s->t4_t6_tx.bit_ptr = 0;
+    s->line_image_size = s->image_size*8;
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_next_page_has_different_format(t4_state_t *s)
+{
+    span_log(&s->logging, SPAN_LOG_FLOW, "Checking for the existance of page %d\n", s->current_page + 1);
+    if (s->current_page >= s->tiff.stop_page)
+        return -1;
+    if (s->t4_t6_tx.row_read_handler == NULL)
+    {
+#if defined(HAVE_LIBTIFF)
+        if (s->tiff.tiff_file == NULL)
+            return -1;
+        if (!TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) s->current_page + 1))
+            return -1;
+        return test_tiff_directory_info(s);
+#endif
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_restart_page(t4_state_t *s)
+{
+    s->t4_t6_tx.bit_pos = 7;
+    s->t4_t6_tx.bit_ptr = 0;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_end_page(t4_state_t *s)
+{
+    s->current_page++;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_get_bit(t4_state_t *s)
+{
+    int bit;
+
+    if (s->t4_t6_tx.bit_ptr >= s->image_size)
+        return SIG_STATUS_END_OF_DATA;
+    bit = (s->image_buffer[s->t4_t6_tx.bit_ptr] >> (7 - s->t4_t6_tx.bit_pos)) & 1;
+    if (--s->t4_t6_tx.bit_pos < 0)
+    {
+        s->t4_t6_tx.bit_pos = 7;
+        s->t4_t6_tx.bit_ptr++;
+    }
+    return bit;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_get_byte(t4_state_t *s)
+{
+    if (s->t4_t6_tx.bit_ptr >= s->image_size)
+        return 0x100;
+    return s->image_buffer[s->t4_t6_tx.bit_ptr++];
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_get_chunk(t4_state_t *s, uint8_t buf[], int max_len)
+{
+    if (s->t4_t6_tx.bit_ptr >= s->image_size)
+        return 0;
+    if (s->t4_t6_tx.bit_ptr + max_len > s->image_size)
+        max_len = s->image_size - s->t4_t6_tx.bit_ptr;
+    memcpy(buf, &s->image_buffer[s->t4_t6_tx.bit_ptr], max_len);
+    s->t4_t6_tx.bit_ptr += max_len;
+    return max_len;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_check_bit(t4_state_t *s)
+{
+    int bit;
+
+    if (s->t4_t6_tx.bit_ptr >= s->image_size)
+        return SIG_STATUS_END_OF_DATA;
+    bit = (s->image_buffer[s->t4_t6_tx.bit_ptr] >> s->t4_t6_tx.bit_pos) & 1;
+    return bit;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_release(t4_state_t *s)
+{
+    if (s->rx)
+        return -1;
+    if (s->tiff.tiff_file)
+        close_tiff_input_file(s);
+    free_buffers(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_free(t4_state_t *s)
+{
+    int ret;
+
+    ret = t4_tx_release(s);
+    free(s);
+    return ret;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t4_tx_set_tx_encoding(t4_state_t *s, int encoding)
+{
+    s->line_encoding = encoding;
+    s->t4_t6_tx.rows_to_next_1d_row = s->t4_t6_tx.max_rows_to_next_1d_row - 1;
+    s->row_is_2d = FALSE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t4_tx_set_min_row_bits(t4_state_t *s, int bits)
+{
+    s->t4_t6_tx.min_bits_per_row = bits;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t4_tx_set_local_ident(t4_state_t *s, const char *ident)
+{
+    s->tiff.local_ident = (ident  &&  ident[0])  ?  ident  :  NULL;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t4_tx_set_header_info(t4_state_t *s, const char *info)
+{
+    s->t4_t6_tx.header_info = (info  &&  info[0])  ?  info  :  NULL;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_get_y_resolution(t4_state_t *s)
+{
+    return s->y_resolution;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_get_x_resolution(t4_state_t *s)
+{
+    return s->x_resolution;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_get_image_width(t4_state_t *s)
+{
+    return s->image_width;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_get_pages_in_file(t4_state_t *s)
+{
+    int max;
+
+    max = 0;
+    if (s->t4_t6_tx.row_read_handler == NULL)
+        max = get_tiff_total_pages(s);
+    if (max >= 0)
+        s->tiff.pages_in_file = max;
+    return max;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t4_tx_get_current_page_in_file(t4_state_t *s)
+{
+    return s->current_page;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.