diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am
index 368f457311..3d63ae7274 100644
--- a/libs/spandsp/src/Makefile.am
+++ b/libs/spandsp/src/Makefile.am
@@ -52,6 +52,7 @@ EXTRA_DIST = floating_fudge.h \
              make_cielab_luts.c \
              make_math_fixed_tables.c \
              make_modem_filter.c \
+             make_t43_gray_code_tables.c \
              msvc/config.h \
              msvc/Download_TIFF.2005.vcproj \
              msvc/Download_TIFF.2008.vcproj \
@@ -356,6 +357,9 @@ make_math_fixed_tables$(EXEEXT): $(top_srcdir)/src/make_math_fixed_tables.c
 make_modem_filter$(EXEEXT): $(top_srcdir)/src/make_modem_filter.c $(top_srcdir)/src/filter_tools.c
 	$(CC_FOR_BUILD) -o make_modem_filter$(EXEEXT) $(top_srcdir)/src/make_modem_filter.c $(top_srcdir)/src/filter_tools.c -DHAVE_CONFIG_H -I$(top_builddir)/src -lm
 
+make_t43_gray_code_tables$(EXEEXT): $(top_srcdir)/src/make_t43_gray_code_tables.c
+	$(CC_FOR_BUILD) -o make_t43_gray_code_tables$(EXEEXT) $(top_srcdir)/src/make_t43_gray_code_tables.c -DHAVE_CONFIG_H -I$(top_builddir)/src -lm
+
 # We need to run make_at_dictionary, so it generates the
 # at_interpreter_dictionary.h file
 
@@ -384,6 +388,11 @@ t42.lo: cielab_luts.h
 cielab_luts.h: make_cielab_luts$(EXEEXT)
 	./make_cielab_luts$(EXEEXT) >cielab_luts.h
 
+t43.lo: t43_gray_code_tables.h
+
+t43_gray_code_tables.h: make_t43_gray_code_tables$(EXEEXT)
+	./make_t43_gray_code_tables$(EXEEXT) >t43_gray_code_tables.h
+
 V17_V32BIS_RX_INCL = v17_v32bis_rx_rrc.h
 
 v17rx.$(OBJEXT): ${V17_V32BIS_RX_INCL}
diff --git a/libs/spandsp/src/at_interpreter.c b/libs/spandsp/src/at_interpreter.c
index 1878ab2ed6..2ecdbb866d 100644
--- a/libs/spandsp/src/at_interpreter.c
+++ b/libs/spandsp/src/at_interpreter.c
@@ -5527,6 +5527,9 @@ SPAN_DECLARE(void) at_interpreter(at_state_t *s, const char *cmd, int len)
                     {
                         if ((entry = command_search(t, &matched)) <= 0)
                             break;
+                        /* The following test shouldn't be needed, but let's keep it here for completeness. */
+                        if (entry > sizeof(at_commands)/sizeof(at_commands[0]))
+                            break;
                         if ((t = at_commands[entry - 1](s, t)) == NULL)
                             break;
                         if (t == (const char *) -1)
diff --git a/libs/spandsp/src/image_translate.c b/libs/spandsp/src/image_translate.c
index 878323ff2d..b504ca9074 100644
--- a/libs/spandsp/src/image_translate.c
+++ b/libs/spandsp/src/image_translate.c
@@ -253,6 +253,7 @@ static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[])
         case T4_IMAGE_TYPE_COLOUR_12BIT:
             image_gray16_to_colour16_row((uint16_t *) buf, (uint16_t *) buf, s->input_width);
             break;
+        case T4_IMAGE_TYPE_COLOUR_BILEVEL:
         case T4_IMAGE_TYPE_COLOUR_8BIT:
             image_gray16_to_colour8_row(buf, (uint16_t *) buf, s->input_width);
             break;
@@ -267,6 +268,7 @@ static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[])
         case T4_IMAGE_TYPE_COLOUR_12BIT:
             image_gray8_to_colour16_row((uint16_t *) buf, buf, s->input_width);
             break;
+        case T4_IMAGE_TYPE_COLOUR_BILEVEL:
         case T4_IMAGE_TYPE_COLOUR_8BIT:
             image_gray8_to_colour8_row(buf, buf, s->input_width);
             break;
@@ -282,11 +284,13 @@ static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[])
         case T4_IMAGE_TYPE_GRAY_8BIT:
             image_colour16_to_gray8_row(buf, (uint16_t *) buf, s->input_width);
             break;
+        case T4_IMAGE_TYPE_COLOUR_BILEVEL:
         case T4_IMAGE_TYPE_COLOUR_8BIT:
             image_colour16_to_colour8_row(buf, (uint16_t *) buf, s->input_width);
             break;
         }
         break;
+    case T4_IMAGE_TYPE_COLOUR_BILEVEL:
     case T4_IMAGE_TYPE_COLOUR_8BIT:
         switch (s->output_format)
         {
@@ -374,6 +378,7 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[])
 
     switch (s->output_format)
     {
+    case T4_IMAGE_TYPE_COLOUR_BILEVEL:
     case T4_IMAGE_TYPE_COLOUR_8BIT:
         row8[0] = s->raw_pixel_row[0];
         row8[1] = s->raw_pixel_row[1];
@@ -611,12 +616,14 @@ SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[],
 
     if (s->output_row < 0)
         return 0;
-    if (s->output_format == T4_IMAGE_TYPE_BILEVEL)
+    switch (s->output_format)
     {
+    case T4_IMAGE_TYPE_BILEVEL:
+    case T4_IMAGE_TYPE_COLOUR_BILEVEL:
+    case T4_IMAGE_TYPE_4COLOUR_BILEVEL:
         i = floyd_steinberg_dither_row(s, buf);
-    }
-    else
-    {
+        break;
+    default:
         s->output_row++;
         if (s->resize)
         {
@@ -631,6 +638,7 @@ SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[],
         if (s->output_row < 0)
             return 0;
         i = s->output_width*s->output_bytes_per_pixel;
+        break;
     }
     return i;
 }
@@ -656,84 +664,42 @@ SPAN_DECLARE(int) image_translate_set_row_read_handler(image_translate_state_t *
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s,
-                                                             int output_format,
-                                                             int output_width,
-                                                             int output_length,
-                                                             int input_format,
-                                                             int input_width,
-                                                             int input_length,
-                                                             t4_row_read_handler_t row_read_handler,
-                                                             void *row_read_user_data)
+static int image_format_to_bytes_per_pixel(int image_format)
+{
+    switch (image_format)
+    {
+    default:
+    case T4_IMAGE_TYPE_BILEVEL:
+    case T4_IMAGE_TYPE_GRAY_8BIT:
+        return 1;
+    case T4_IMAGE_TYPE_GRAY_12BIT:
+        return 2;
+    case T4_IMAGE_TYPE_COLOUR_BILEVEL:
+    case T4_IMAGE_TYPE_COLOUR_8BIT:
+        return 3;
+    case T4_IMAGE_TYPE_4COLOUR_BILEVEL:
+    case T4_IMAGE_TYPE_4COLOUR_8BIT:
+        return 4;
+    case T4_IMAGE_TYPE_COLOUR_12BIT:
+        return 6;
+    case T4_IMAGE_TYPE_4COLOUR_12BIT:
+        return 8;
+    }
+    return 1;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_restart(image_translate_state_t *s, int input_length)
 {
     int i;
     int raw_row_size;
     int row_size;
 
-    if (s == NULL)
-    {
-        if ((s = (image_translate_state_t *) malloc(sizeof(*s))) == NULL)
-            return NULL;
-    }
-    memset(s, 0, sizeof(*s));
-
-    s->input_format = input_format;
-    s->input_width = input_width;
     s->input_length = input_length;
-
-    s->output_format = output_format;
-
-    if ((s->resize = (output_width > 0)))
-    {
-        s->output_width = output_width;
-        if (output_length > 0)
-            s->output_length = output_length;
-        else
-            s->output_length = (s->input_length*s->output_width)/s->input_width;
-    }
+    if (s->resize)
+        s->output_length = (s->input_length*s->output_width)/s->input_width;
     else
-    {
-        s->output_width = s->input_width;
         s->output_length = s->input_length;
-    }
-
-    switch (s->input_format)
-    {
-    case T4_IMAGE_TYPE_GRAY_8BIT:
-        s->input_bytes_per_pixel = 1;
-        break;
-    case T4_IMAGE_TYPE_GRAY_12BIT:
-        s->input_bytes_per_pixel = 2;
-        break;
-    case T4_IMAGE_TYPE_COLOUR_8BIT:
-        s->input_bytes_per_pixel = 3;
-        break;
-    case T4_IMAGE_TYPE_COLOUR_12BIT:
-        s->input_bytes_per_pixel = 6;
-        break;
-    default:
-        s->input_bytes_per_pixel = 1;
-        break;
-    }
-
-    switch (s->output_format)
-    {
-    case T4_IMAGE_TYPE_GRAY_8BIT:
-        s->output_bytes_per_pixel = 1;
-        break;
-    case T4_IMAGE_TYPE_GRAY_12BIT:
-        s->output_bytes_per_pixel = 2;
-        break;
-    case T4_IMAGE_TYPE_COLOUR_8BIT:
-        s->output_bytes_per_pixel = 3;
-        break;
-    case T4_IMAGE_TYPE_COLOUR_12BIT:
-        s->output_bytes_per_pixel = 6;
-        break;
-    default:
-        s->output_bytes_per_pixel = 1;
-        break;
-    }
 
     /* Allocate the two row buffers we need, using the space requirements we now have */
     raw_row_size = s->input_width*s->input_bytes_per_pixel;
@@ -744,30 +710,78 @@ SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_sta
     {
         for (i = 0;  i < 2;  i++)
         {
-            if ((s->raw_pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL)
-                return NULL;
+            if (s->raw_pixel_row[i] == NULL)
+            {
+                if ((s->raw_pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL)
+                    return -1;
+            }
             memset(s->raw_pixel_row[i], 0, raw_row_size);
         }
     }
-    if (s->output_format == T4_IMAGE_TYPE_BILEVEL)
+    switch (s->output_format)
     {
+    case T4_IMAGE_TYPE_BILEVEL:
+    case T4_IMAGE_TYPE_COLOUR_BILEVEL:
+    case T4_IMAGE_TYPE_4COLOUR_BILEVEL:
         if (s->resize)
             raw_row_size = row_size;
         for (i = 0;  i < 2;  i++)
         {
-            if ((s->pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL)
-                return NULL;
+            if (s->pixel_row[i] == NULL)
+            {
+                if ((s->pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL)
+                    return -1;
+            }
             memset(s->pixel_row[i], 0, raw_row_size);
         }
+        break;
     }
 
-    s->row_read_handler = row_read_handler;
-    s->row_read_user_data = row_read_user_data;
-
     s->raw_input_row = 0;
     s->raw_output_row = 0;
     s->output_row = 0;
 
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s,
+                                                             int output_format,
+                                                             int output_width,
+                                                             int output_length,
+                                                             int input_format,
+                                                             int input_width,
+                                                             int input_length,
+                                                             t4_row_read_handler_t row_read_handler,
+                                                             void *row_read_user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (image_translate_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+
+    s->row_read_handler = row_read_handler;
+    s->row_read_user_data = row_read_user_data;
+
+    s->input_format = input_format;
+    s->input_width = input_width;
+    s->input_length = input_length;
+    s->input_bytes_per_pixel = image_format_to_bytes_per_pixel(s->input_format);
+
+    s->output_format = output_format;
+    s->output_bytes_per_pixel = image_format_to_bytes_per_pixel(s->output_format);
+
+    s->resize = (output_width > 0);
+    if (s->resize)
+        s->output_width = output_width;
+    else
+        s->output_width = s->input_width;
+
+    if (image_translate_restart(s, input_length))
+        return NULL;
+
     return s;
 }
 /*- End of function --------------------------------------------------------*/
diff --git a/libs/spandsp/src/make_math_fixed_tables.c b/libs/spandsp/src/make_math_fixed_tables.c
index 2bddd12ced..64b33ca328 100644
--- a/libs/spandsp/src/make_math_fixed_tables.c
+++ b/libs/spandsp/src/make_math_fixed_tables.c
@@ -114,3 +114,5 @@ int main(int argc, char *argv[])
 
     return 0;
 }
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/make_t43_gray_code_tables.c b/libs/spandsp/src/make_t43_gray_code_tables.c
new file mode 100644
index 0000000000..9e45b120ef
--- /dev/null
+++ b/libs/spandsp/src/make_t43_gray_code_tables.c
@@ -0,0 +1,104 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * make_t43_gray_code_tables.c - Generate the Gray code tables for T.43 image
+ *                               compression.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2012 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <math.h>
+
+int main(int argc, char *argv[])
+{
+    int i;
+    int j;
+    int gray;
+    int new_gray;
+    int restore;
+
+    printf("static const int16_t gray_code[4096] =\n{\n");
+    for (i = 0;  i < 4096;  i++)
+    {
+        gray = i & 0x800;
+        for (j = 10;  j >= 0;  j--)
+        {
+            if (((i >> (j + 1)) & 1) ^ ((i >> j) & 1))
+                gray |= (1 << j);
+        }
+        printf("    0x%04x, /* 0x%04x */\n", gray, restore);
+
+        /* Now reverse the process and check we get back where we start */
+        restore = gray & 0x800;
+        for (j = 10;  j >= 0;  j--)
+        {
+            if (((restore >> (j + 1)) & 1) ^ ((gray >> j) & 1))
+                restore |= (1 << j);
+        }
+
+        if (i != restore)
+        {
+            printf("Ah\n");
+            exit(2);
+        }
+    }
+    printf("};\n\n");
+
+    printf("static const int16_t anti_gray_code[4096] =\n{\n");
+    for (i = 0;  i < 4096;  i++)
+    {
+        gray = i;
+        restore = gray & 0x800;
+        for (j = 10;  j >= 0;  j--)
+        {
+            if (((restore >> (j + 1)) & 1) ^ ((gray >> j) & 1))
+                restore |= (1 << j);
+        }
+        printf("    0x%04x, /* 0x%04x */\n", restore, gray);
+
+        /* Now reverse the process and check we get back where we start */
+        new_gray = restore & 0x800;
+        for (j = 10;  j >= 0;  j--)
+        {
+            if (((restore >> (j + 1)) & 1) ^ ((restore >> j) & 1))
+                new_gray |= (1 << j);
+        }
+
+        if (gray != new_gray)
+        {
+            printf("Ah\n");
+            exit(2);
+        }
+    }
+    printf("};\n");
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/expose.h b/libs/spandsp/src/spandsp/expose.h
index 49d2a8d52f..5424b7c67e 100644
--- a/libs/spandsp/src/spandsp/expose.h
+++ b/libs/spandsp/src/spandsp/expose.h
@@ -83,7 +83,7 @@
 #include <spandsp/private/t81_t82_arith_coding.h>
 #include <spandsp/private/t85.h>
 #include <spandsp/private/t42.h>
-/*#include <spandsp/private/t43.h>*/
+#include <spandsp/private/t43.h>
 #include <spandsp/private/t4_rx.h>
 #include <spandsp/private/t4_tx.h>
 #include <spandsp/private/t30.h>
diff --git a/libs/spandsp/src/spandsp/t42.h b/libs/spandsp/src/spandsp/t42.h
index b864b17536..343780b9fe 100644
--- a/libs/spandsp/src/spandsp/t42.h
+++ b/libs/spandsp/src/spandsp/t42.h
@@ -48,8 +48,24 @@ extern "C"
 {
 #endif
 
+/*! \brief Convert an X0, Y0, Z0 coordinate to a colour tempature */
+SPAN_DECLARE(int) xyz_to_corrected_color_temp(float *temp, float xyz[3]);
+
+/*! \brief Convert a colour temperature to an X0, Y0, Z0 coordinate */
+SPAN_DECLARE(int) colour_temp_to_xyz(float xyz[3], float temp);
+
+/*! \brief Convert a row of 8 bit pixels from Lab to sRGB
+    \param s The Lab parameters context.
+    \param lab The output pixels
+    \param srgb The input pixels
+    \param pixel The number of pixels in the row. */
 SPAN_DECLARE(void) srgb_to_lab(lab_params_t *s, uint8_t lab[], const uint8_t srgb[], int pixels);
 
+/*! \brief Convert a row of 8 bit pixels from sRGB to Lab
+    \param s The Lab parameters context.
+    \param srgb The output pixels
+    \param lab The input pixels
+    \param pixel The number of pixels in the row. */
 SPAN_DECLARE(void) lab_to_srgb(lab_params_t *s, uint8_t srgb[], const uint8_t lab[], int pixels);
 
 SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *s, float new_xn, float new_yn, float new_zn);
@@ -58,6 +74,8 @@ SPAN_DECLARE(void) set_lab_gamut(lab_params_t *s, int L_min, int L_max, int a_mi
 
 SPAN_DECLARE(void) set_lab_gamut2(lab_params_t *s, int L_P, int L_Q, int a_P, int a_Q, int b_P, int b_Q);
 
+SPAN_DECLARE(void) get_lab_gamut2(lab_params_t *s, int *L_P, int *L_Q, int *a_P, int *a_Q, int *b_P, int *b_Q);
+
 SPAN_DECLARE(int) t42_itulab_to_itulab(logging_state_t *logging, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height);
 
 SPAN_DECLARE(int) t42_itulab_to_jpeg(logging_state_t *logging, lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen);
@@ -68,15 +86,14 @@ SPAN_DECLARE(int) t42_srgb_to_itulab(logging_state_t *logging, lab_params_t *s,
 
 SPAN_DECLARE(int) t42_itulab_to_srgb(logging_state_t *logging, lab_params_t *s, tdata_t dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t *width, uint32_t *height);
 
-SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s,
-                                          uint32_t l0,
-                                          int mx,
-                                          int options);
+SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s, uint32_t l0, int quality, int options);
 
 SPAN_DECLARE(int) t42_encode_set_image_width(t42_encode_state_t *s, uint32_t image_width);
 
 SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t length);
 
+SPAN_DECLARE(int) t42_encode_set_image_type(t42_encode_state_t *s, int image_type);
+
 SPAN_DECLARE(void) t42_encode_abort(t42_encode_state_t *s);
 
 SPAN_DECLARE(void) t42_encode_comment(t42_encode_state_t *s, const uint8_t comment[], size_t len);
@@ -92,11 +109,12 @@ SPAN_DECLARE(uint32_t) t42_encode_get_image_width(t42_encode_state_t *s);
 
 SPAN_DECLARE(uint32_t) t42_encode_get_image_length(t42_encode_state_t *s);
 
+/*! \brief Get the size of the compressed image in bits.
+    \param s The T.42 context.
+    \return The size of the image, in bits. */
 SPAN_DECLARE(int) t42_encode_get_compressed_image_size(t42_encode_state_t *s);
 
-SPAN_DECLARE(int) t42_encode_set_row_read_handler(t42_encode_state_t *s,
-                                                  t4_row_read_handler_t handler,
-                                                  void *user_data);
+SPAN_DECLARE(int) t42_encode_set_row_read_handler(t42_encode_state_t *s, t4_row_read_handler_t handler, void *user_data);
 
 /*! Get the logging context associated with a T.42 encode context.
     \brief Get the logging context associated with a T.42 encode context.
@@ -163,6 +181,14 @@ SPAN_DECLARE(int) t42_decode_set_comment_handler(t42_decode_state_t *s,
                                                  t4_row_write_handler_t handler,
                                                  void *user_data);
 
+/*! A maliciously constructed T.42 image could consume too much memory, and constitute
+    a denial of service attack on the system. This function allows constraints to be
+    applied.
+    \brief Set constraints on the received image size.
+    \param s The T.42 context.
+    \param max_xd The maximum permitted width of the full image, in pixels
+    \param max_yd The maximum permitted height of the full image, in pixels
+    \return 0 for OK */
 SPAN_DECLARE(int) t42_decode_set_image_size_constraints(t42_decode_state_t *s,
                                                         uint32_t max_xd,
                                                         uint32_t max_yd);
@@ -177,6 +203,9 @@ SPAN_DECLARE(uint32_t) t42_decode_get_image_width(t42_decode_state_t *s);
     \return The length of the image, in pixels. */
 SPAN_DECLARE(uint32_t) t42_decode_get_image_length(t42_decode_state_t *s);
 
+/*! \brief Get the size of the compressed image in bits.
+    \param s The T.42 context.
+    \return The size of the image, in bits. */
 SPAN_DECLARE(int) t42_decode_get_compressed_image_size(t42_decode_state_t *s);
 
 /*! Get the logging context associated with a T.42 decode context.
@@ -185,6 +214,8 @@ SPAN_DECLARE(int) t42_decode_get_compressed_image_size(t42_decode_state_t *s);
     \return A pointer to the logging context */
 SPAN_DECLARE(logging_state_t *) t42_decode_get_logging_state(t42_decode_state_t *s);
 
+/*! \brief Restart a T.42 decode context.
+    \param s The T.42 context. */
 SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s);
 
 /*! \brief Prepare to decode an image in T.42 format.
diff --git a/libs/spandsp/src/spandsp/t43.h b/libs/spandsp/src/spandsp/t43.h
index c6718d18ff..ecc0f2a224 100644
--- a/libs/spandsp/src/spandsp/t43.h
+++ b/libs/spandsp/src/spandsp/t43.h
@@ -68,10 +68,10 @@ SPAN_DECLARE(int) t43_encode_set_image_width(t43_encode_state_t *s, uint32_t ima
 
 SPAN_DECLARE(int) t43_encode_set_image_length(t43_encode_state_t *s, uint32_t length);
 
-SPAN_DECLARE(void) t43_encode_abort(t43_encode_state_t *s);
-
 SPAN_DECLARE(int) t43_encode_set_image_type(t43_encode_state_t *s, int image_type);
 
+SPAN_DECLARE(void) t43_encode_abort(t43_encode_state_t *s);
+
 SPAN_DECLARE(void) t43_encode_comment(t43_encode_state_t *s, const uint8_t comment[], size_t len);
 
 /*! \brief Check if we are at the end of the current document page.
diff --git a/libs/spandsp/src/spandsp/t4_rx.h b/libs/spandsp/src/spandsp/t4_rx.h
index 7ebe5b92ce..ed56cbcf8f 100644
--- a/libs/spandsp/src/spandsp/t4_rx.h
+++ b/libs/spandsp/src/spandsp/t4_rx.h
@@ -537,7 +537,7 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t
 
 /*! Get the short text name of a compression format.
     \brief Get the short text name of an encoding format.
-    \param encoding The encoding type.
+    \param compression The compression type.
     \return A pointer to the string. */
 SPAN_DECLARE(const char *) t4_compression_to_str(int compression);
 
diff --git a/libs/spandsp/src/spandsp/t4_t6_encode.h b/libs/spandsp/src/spandsp/t4_t6_encode.h
index 7431d64f08..a157b444b9 100644
--- a/libs/spandsp/src/spandsp/t4_t6_encode.h
+++ b/libs/spandsp/src/spandsp/t4_t6_encode.h
@@ -114,20 +114,23 @@ SPAN_DECLARE(logging_state_t *) t4_t6_encode_get_logging_state(t4_t6_encode_stat
 
 /*! \brief Restart a T.4 or T.6 encode context.
     \param s The T.4/T.6 context.
-    \param image width The image width, in pixels.
+    \param image_width The image width, in pixels.
+    \param image_length The image length, in pixels. This can be set to -1, if the length is not known.
     \return 0 for success, otherwise -1. */
-SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width);
+SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width, int image_length);
 
 /*! \brief Prepare to encode an image in T.4 or T.6 format.
     \param s The T.4/T.6 context.
     \param encoding The encoding mode.
-    \param image width The image width, in pixels.
+    \param image_width The image width, in pixels.
+    \param image_length The image length, in pixels. This can be set to -1, if the length is not known.
     \param handler A callback routine to handle decoded image rows.
     \param user_data An opaque pointer passed to handler.
     \return A pointer to the context, or NULL if there was a problem. */
 SPAN_DECLARE(t4_t6_encode_state_t *) t4_t6_encode_init(t4_t6_encode_state_t *s,
                                                        int encoding,
                                                        int image_width,
+                                                       int image_length,
                                                        t4_row_read_handler_t handler,
                                                        void *user_data);
 
diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c
index a70db393c9..c3e4eadecc 100644
--- a/libs/spandsp/src/t30.c
+++ b/libs/spandsp/src/t30.c
@@ -751,10 +751,12 @@ static uint8_t check_next_tx_step(t30_state_t *s)
         more = FALSE;
     if (more)
     {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Another document to send\n");
         //if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MULTIPLE_SELECTIVE_POLLING_CAPABLE))
         //    return T30_EOS;
         return (s->local_interrupt_pending)  ?  T30_PRI_EOM  :  T30_EOM;
     }
+    span_log(&s->logging, SPAN_LOG_FLOW, "No more pages to send\n");
     return (s->local_interrupt_pending)  ?  T30_PRI_EOP  :  T30_EOP;
 }
 /*- End of function --------------------------------------------------------*/
@@ -1169,9 +1171,9 @@ int t30_build_dis_or_dtc(t30_state_t *s)
     /* Ready to receive a fax will be determined separately, and this message edited. */
     /* With no modems set we are actually selecting V.27ter fallback at 2400bps */
     if ((s->supported_modems & T30_SUPPORT_V27TER))
-        set_ctrl_bit(s->local_dis_dtc_frame, 12);
+        set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_MODEM_TYPE_2);
     if ((s->supported_modems & T30_SUPPORT_V29))
-        set_ctrl_bit(s->local_dis_dtc_frame, 11);
+        set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_MODEM_TYPE_1);
     /* V.17 is only valid when combined with V.29 and V.27ter, so if we enable V.17 we force the others too. */
     if ((s->supported_modems & T30_SUPPORT_V17))
         s->local_dis_dtc_frame[4] |= (DISBIT6 | DISBIT4 | DISBIT3);
@@ -1359,9 +1361,11 @@ int t30_build_dis_or_dtc(t30_state_t *s)
 
     /* No double sided printing (alternate mode) */
     /* No double sided printing (continuous mode) */
+
     /* No black and white mixed raster content profile */
     /* No shared data memory */
     /* No T.44 colour space */
+
     if ((s->iaf & T30_IAF_MODE_FLOW_CONTROL))
         set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T38_FLOW_CONTROL_CAPABLE);
     /* No k > 4 */
@@ -1455,27 +1459,23 @@ static int build_dcs(t30_state_t *s)
 
     /* Select the compression to use. */
     use_bilevel = TRUE;
+    set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
     switch (s->line_compression)
     {
     case T4_COMPRESSION_T4_1D:
         /* There is nothing to set to select this encoding. */
-        set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         break;
     case T4_COMPRESSION_T4_2D:
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_2D_MODE);
-        set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         break;
     case T4_COMPRESSION_T6:
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T6_MODE);
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         break;
     case T4_COMPRESSION_T85:
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_MODE);
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         break;
     case T4_COMPRESSION_T85_L0:
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_L0_MODE);
-        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         break;
 #if defined(SPANDSP_SUPPORT_T88)
     case T4_COMPRESSION_T88:
@@ -1487,7 +1487,7 @@ static int build_dcs(t30_state_t *s)
             set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE);
         if (image_type == T4_IMAGE_TYPE_GRAY_12BIT  ||  image_type == T4_IMAGE_TYPE_COLOUR_12BIT)
             set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_12BIT_COMPONENT);
-        //if (???????? & T4_COMPRESSION_?????))
+        //if (???????? & T4_COMPRESSION_NO_SUBSAMPLING))
         //    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING);
         //if (???????? & T4_COMPRESSION_?????))
         //    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_PREFERRED_HUFFMAN_TABLES);
@@ -1501,7 +1501,7 @@ static int build_dcs(t30_state_t *s)
             set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE);
         if (image_type == T4_IMAGE_TYPE_GRAY_12BIT  ||  image_type == T4_IMAGE_TYPE_COLOUR_12BIT)
             set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_12BIT_COMPONENT);
-        //if (???????? & T4_COMPRESSION_?????))
+        //if (???????? & T4_COMPRESSION_NO_SUBSAMPLING))
         //    set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING);
         set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1);
         use_bilevel = FALSE;
@@ -1684,7 +1684,7 @@ static int build_dcs(t30_state_t *s)
         ||
         ((s->image_width == T4_WIDTH_1200_A4)  &&  (s->x_resolution == T4_X_RESOLUTION_1200)))
     {
-        span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A4\n");
+        span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A4 at %ddpi x %ddpi\n", s->x_resolution, s->y_resolution);
         /* No width related bits need to be set. */
     }
     else if (((s->image_width == T4_WIDTH_200_B4)  &&  (s->x_resolution == T4_X_RESOLUTION_200  ||  s->x_resolution == T4_X_RESOLUTION_R8))
@@ -1699,7 +1699,7 @@ static int build_dcs(t30_state_t *s)
     {
         if ((s->mutual_image_sizes & T4_SUPPORT_WIDTH_255MM))
         {
-            span_log(&s->logging, SPAN_LOG_FLOW, "Image width is B4\n");
+            span_log(&s->logging, SPAN_LOG_FLOW, "Image width is B4 at %ddpi x %ddpi\n", s->x_resolution, s->y_resolution);
             set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_255MM_WIDTH);
         }
         else
@@ -1720,7 +1720,7 @@ static int build_dcs(t30_state_t *s)
     {
         if ((s->mutual_image_sizes & T4_SUPPORT_WIDTH_303MM))
         {
-            span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A3\n");
+            span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A3 at %ddpi x %ddpi\n", s->x_resolution, s->y_resolution);
             set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_303MM_WIDTH);
         }
         else
@@ -1747,7 +1747,7 @@ static int build_dcs(t30_state_t *s)
         return -1;
     }
 
-    /* Deal with the image length */
+    /* Set the image length */
     /* If the other end supports unlimited length, then use that. Otherwise, if the other end supports
        B4 use that, as its longer than the default A4 length. */
     if ((s->mutual_image_sizes & T4_SUPPORT_LENGTH_UNLIMITED))
@@ -1768,10 +1768,10 @@ static int build_dcs(t30_state_t *s)
     if ((s->iaf & T30_IAF_MODE_CONTINUOUS_FLOW)  &&  test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38_FAX_CAPABLE))
     {
         /* Clear the modem type bits, in accordance with note 77 of Table 2/T.30 */
-        clr_ctrl_bit(s->local_dis_dtc_frame, 11);
-        clr_ctrl_bit(s->local_dis_dtc_frame, 12);
-        clr_ctrl_bit(s->local_dis_dtc_frame, 13);
-        clr_ctrl_bit(s->local_dis_dtc_frame, 14);
+        clr_ctrl_bit(s->local_dis_dtc_frame, T30_DCS_BIT_MODEM_TYPE_1);
+        clr_ctrl_bit(s->local_dis_dtc_frame, T30_DCS_BIT_MODEM_TYPE_2);
+        clr_ctrl_bit(s->local_dis_dtc_frame, T30_DCS_BIT_MODEM_TYPE_3);
+        clr_ctrl_bit(s->local_dis_dtc_frame, T30_DCS_BIT_MODEM_TYPE_4);
         set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T38_FAX_MODE);
     }
     s->dcs_len = 19;
@@ -2013,14 +2013,16 @@ static int analyze_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len)
 
 static int analyze_rx_dcs(t30_state_t *s, const uint8_t *msg, int len)
 {
+    /* The following treats a width field of 11 like 10, which does what note 6 of Table 2/T.30
+       says we should do with the invalid value 11. */
     static const int widths[6][4] =
     {
-        { T4_WIDTH_100_A4,  T4_WIDTH_100_B4,  T4_WIDTH_100_A3, -1}, /* 100/inch */
-        { T4_WIDTH_200_A4,  T4_WIDTH_200_B4,  T4_WIDTH_200_A3, -1}, /* 200/inch / R8 resolution */
-        { T4_WIDTH_300_A4,  T4_WIDTH_300_B4,  T4_WIDTH_300_A3, -1}, /* 300/inch resolution */
-        { T4_WIDTH_400_A4,  T4_WIDTH_400_B4,  T4_WIDTH_400_A3, -1}, /* 400/inch / R16 resolution */
-        { T4_WIDTH_600_A4,  T4_WIDTH_600_B4,  T4_WIDTH_600_A3, -1}, /* 600/inch resolution */
-        {T4_WIDTH_1200_A4, T4_WIDTH_1200_B4, T4_WIDTH_1200_A3, -1}  /* 1200/inch resolution */
+        { T4_WIDTH_100_A4,  T4_WIDTH_100_B4,  T4_WIDTH_100_A3,  T4_WIDTH_100_A3}, /* 100/inch */
+        { T4_WIDTH_200_A4,  T4_WIDTH_200_B4,  T4_WIDTH_200_A3,  T4_WIDTH_200_A3}, /* 200/inch / R8 resolution */
+        { T4_WIDTH_300_A4,  T4_WIDTH_300_B4,  T4_WIDTH_300_A3,  T4_WIDTH_300_A3}, /* 300/inch resolution */
+        { T4_WIDTH_400_A4,  T4_WIDTH_400_B4,  T4_WIDTH_400_A3,  T4_WIDTH_400_A3}, /* 400/inch / R16 resolution */
+        { T4_WIDTH_600_A4,  T4_WIDTH_600_B4,  T4_WIDTH_600_A3,  T4_WIDTH_600_A3}, /* 600/inch resolution */
+        {T4_WIDTH_1200_A4, T4_WIDTH_1200_B4, T4_WIDTH_1200_A3, T4_WIDTH_1200_A3}  /* 1200/inch resolution */
     };
     uint8_t dcs_frame[T30_MAX_DIS_DTC_DCS_LEN];
     int i;
@@ -2588,7 +2590,28 @@ static int send_cfr_sequence(t30_state_t *s, int start)
     /* (CSA) CFR */
     /* CFR is usually a simple frame, but can become a sequence with Internet
        FAXing. */
-    send_simple_frame(s, T30_CFR);
+    if (start)
+    {
+        s->step = 0;
+    }
+    switch (s->step)
+    {
+    case 0:
+        s->step++;
+        if (send_csa_frame(s))
+            break;
+        /* Fall through */
+    case 1:
+        s->step++;
+        send_simple_frame(s, T30_CFR);
+        break;
+    case 2:
+        s->step++;
+        shut_down_hdlc_tx(s);
+        break;
+    default:
+        return -1;
+    }
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
@@ -6291,12 +6314,7 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status)
             }
             break;
         case T30_STATE_F_CFR:
-            if (s->step == 0)
-            {
-                shut_down_hdlc_tx(s);
-                s->step++;
-            }
-            else
+            if (send_cfr_sequence(s, FALSE))
             {
                 if (s->error_correcting_mode)
                 {
diff --git a/libs/spandsp/src/t42.c b/libs/spandsp/src/t42.c
index 3028c03472..c1f9802fbc 100644
--- a/libs/spandsp/src/t42.c
+++ b/libs/spandsp/src/t42.c
@@ -38,6 +38,7 @@
 #include <time.h>
 #include <memory.h>
 #include <string.h>
+#include <float.h>
 #if defined(HAVE_TGMATH_H)
 #include <tgmath.h>
 #endif
@@ -48,7 +49,6 @@
 #include <tiffio.h>
 #include <assert.h>
 #include <jpeglib.h>
-#include <setjmp.h>
 
 #include "spandsp/telephony.h"
 #include "spandsp/fast_convert.h"
@@ -90,12 +90,6 @@ typedef struct
     float zn;
 } illuminant_t;
 
-typedef struct
-{
-    jmp_buf escape;
-    char error_message[JMSG_LENGTH_MAX];
-} escape_route_t;
-
 static const illuminant_t illuminants[] =
 {
     {"\0D50",  "CIE D50/2°",   96.422f, 100.000f,  82.521f},
@@ -119,8 +113,86 @@ static const illuminant_t illuminants[] =
     {"",       "",              0.000f,   0.000f,   0.000f}
 };
 
-/* This is the error catcher */
-static struct jpeg_error_mgr error_handler;
+/* LERP(a,b,c) = linear interpolation macro, is 'a' when c == 0.0 and 'b' when c == 1.0 */
+#define LERP(a,b,c)     (((b) - (a))*(c) + (a))
+
+typedef struct UVT
+{
+    double u;
+    double v;
+    double t;
+} UVT;
+
+static const double rt[31] =
+{
+    /* Reciprocal temperature (K) */
+     FLT_MIN,
+     10.0e-6,
+     20.0e-6,
+     30.0e-6,
+     40.0e-6,
+     50.0e-6,
+     60.0e-6,
+     70.0e-6,
+     80.0e-6,
+     90.0e-6,
+    100.0e-6,
+    125.0e-6,
+    150.0e-6,
+    175.0e-6,
+    200.0e-6,
+    225.0e-6,
+    250.0e-6,
+    275.0e-6,
+    300.0e-6,
+    325.0e-6,
+    350.0e-6,
+    375.0e-6,
+    400.0e-6,
+    425.0e-6,
+    450.0e-6,
+    475.0e-6,
+    500.0e-6,
+    525.0e-6,
+    550.0e-6,
+    575.0e-6,
+    600.0e-6
+};
+
+static const UVT uvt[31] =
+{
+    {0.18006, 0.26352, -0.24341},
+    {0.18066, 0.26589, -0.25479},
+    {0.18133, 0.26846, -0.26876},
+    {0.18208, 0.27119, -0.28539},
+    {0.18293, 0.27407, -0.30470},
+    {0.18388, 0.27709, -0.32675},
+    {0.18494, 0.28021, -0.35156},
+    {0.18611, 0.28342, -0.37915},
+    {0.18740, 0.28668, -0.40955},
+    {0.18880, 0.28997, -0.44278},
+    {0.19032, 0.29326, -0.47888},
+    {0.19462, 0.30141, -0.58204},
+    {0.19962, 0.30921, -0.70471},
+    {0.20525, 0.31647, -0.84901},
+    {0.21142, 0.32312, -1.01820},
+    {0.21807, 0.32909, -1.21680},
+    {0.22511, 0.33439, -1.45120},
+    {0.23247, 0.33904, -1.72980},
+    {0.24010, 0.34308, -2.06370},
+    {0.24792, 0.34655, -2.46810},   /* Note: 0.24792 is a corrected value for the error found in W&S as 0.24702 */
+    {0.25591, 0.34951, -2.96410},
+    {0.26400, 0.35200, -3.58140},
+    {0.27218, 0.35407, -4.36330},
+    {0.28039, 0.35577, -5.37620},
+    {0.28863, 0.35714, -6.72620},
+    {0.29685, 0.35823, -8.59550},
+    {0.30505, 0.35907, -11.3240},
+    {0.31320, 0.35968, -15.6280},
+    {0.32129, 0.36011, -23.3250},
+    {0.32931, 0.36038, -40.7700},
+    {0.33724, 0.36051, -116.450}
+};
 
 static __inline__ uint16_t pack_16(const uint8_t *s)
 {
@@ -148,88 +220,158 @@ static __inline__ int unpack_16(uint8_t *s, uint16_t value)
 }
 /*- End of function --------------------------------------------------------*/
 
-/* Error handler for IJG library */
-static void jpg_error_exit(j_common_ptr cinfo)
+SPAN_DECLARE(int) xyz_to_corrected_color_temp(float *temp, float xyz[3])
 {
-    escape_route_t *escape;
+    float us;
+    float vs;
+    float p;
+    float di;
+    float dm;
+    int i;
 
-    escape = (escape_route_t *) cinfo->client_data;
-    (*cinfo->err->format_message)(cinfo, escape->error_message);
-    longjmp(escape->escape, 1);
+    /* Protect against possible divide-by-zero failure */
+    if ((xyz[0] < 1.0e-20f)  &&  (xyz[1] < 1.0e-20f)  &&  (xyz[2] < 1.0e-20f))
+        return -1;
+    us = (4.0f*xyz[0])/(xyz[0] + 15.0f*xyz[1] + 3.0f*xyz[2]);
+    vs = (6.0f*xyz[1])/(xyz[0] + 15.0f*xyz[1] + 3.0f*xyz[2]);
+    dm = 0.0f;
+    for (i = 0;  i < 31;  i++)
+    {
+        di = (vs - uvt[i].v) - uvt[i].t*(us - uvt[i].u);
+        if ((i > 0)  &&  (((di < 0.0f)  &&  (dm >= 0.0f))  ||  ((di >= 0.0f)  &&  (dm < 0.0f))))
+            break;  /* found lines bounding (us, vs) : i-1 and i */
+        dm = di;
+    }
+    if (i == 31)
+    {
+        /* Bad XYZ input, color temp would be less than minimum of 1666.7 degrees, or too far towards blue */
+        return -1;
+    }
+    di = di/sqrtf(1.0f + uvt[i    ].t*uvt[i    ].t);
+    dm = dm/sqrtf(1.0f + uvt[i - 1].t*uvt[i - 1].t);
+    p = dm/(dm - di);     /* p = interpolation parameter, 0.0 : i-1, 1.0 : i */
+    p = 1.0f/(LERP(rt[i - 1], rt[i], p));
+    *temp = p;
+    return 0;
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *s, float new_xn, float new_yn, float new_zn)
+SPAN_DECLARE(int) colour_temp_to_xyz(float xyz[3], float temp)
+{
+    float x;
+    float y;
+
+    /* Should be good for 1667K to 25000K according to Wikipedia */
+    if (temp < 1667.0f  ||  temp > 25000.0f)
+        return -1;
+
+    if (temp < 4000.0f)
+        x = -0.2661239e9f/(temp*temp*temp) - 0.2343580e6f/(temp*temp) + 0.8776956e3f/temp + 0.179910f;
+    else
+        x = -3.0258469e9f/(temp*temp*temp) + 2.1070379e6f/(temp*temp) + 0.2226347e3f/temp + 0.240390f;
+
+    if (temp < 2222.0f)
+        y = -1.1063814f*x*x*x - 1.34811020f*x*x + 2.18555832f*x - 0.20219683f;
+    else if (temp < 4000.0f)
+        y = -0.9549476f*x*x*x - 1.37418593f*x*x + 2.09137015f*x - 0.16748867f;
+    else
+        y =  3.0817580f*x*x*x - 5.87338670f*x*x + 3.75112997f*x - 0.37001483f;
+
+    xyz[0] = x/y;
+    xyz[1] = 1.0f;
+    xyz[2] = (1.0f - x - y)/y;
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *lab, float new_xn, float new_yn, float new_zn)
 {
     if (new_yn > 10.0f)
     {
-        s->x_n = new_xn/100.0f;
-        s->y_n = new_yn/100.0f;
-        s->z_n = new_zn/100.0f;
+        lab->x_n = new_xn/100.0f;
+        lab->y_n = new_yn/100.0f;
+        lab->z_n = new_zn/100.0f;
     }
     else
     {
-        s->x_n = new_xn;
-        s->y_n = new_yn;
-        s->z_n = new_zn;
+        lab->x_n = new_xn;
+        lab->y_n = new_yn;
+        lab->z_n = new_zn;
     }
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(void) set_lab_gamut(lab_params_t *s, int L_min, int L_max, int a_min, int a_max, int b_min, int b_max, int ab_are_signed)
+SPAN_DECLARE(void) set_lab_gamut(lab_params_t *lab, int L_min, int L_max, int a_min, int a_max, int b_min, int b_max, int ab_are_signed)
 {
-    s->range_L = L_max - L_min;
-    s->range_a = a_max - a_min;
-    s->range_b = b_max - b_min;
+    lab->range_L = L_max - L_min;
+    lab->range_a = a_max - a_min;
+    lab->range_b = b_max - b_min;
 
-    s->offset_L = -256.0f*L_min/s->range_L;
-    s->offset_a = -256.0f*a_min/s->range_a;
-    s->offset_b = -256.0f*b_min/s->range_b;
+    lab->offset_L = -256.0f*L_min/lab->range_L;
+    lab->offset_a = -256.0f*a_min/lab->range_a;
+    lab->offset_b = -256.0f*b_min/lab->range_b;
 
-    s->range_L /= (256.0f - 1.0f);
-    s->range_a /= (256.0f - 1.0f);
-    s->range_b /= (256.0f - 1.0f);
+    lab->range_L /= (256.0f - 1.0f);
+    lab->range_a /= (256.0f - 1.0f);
+    lab->range_b /= (256.0f - 1.0f);
 
-    s->ab_are_signed = ab_are_signed;
+    lab->ab_are_signed = ab_are_signed;
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(void) set_lab_gamut2(lab_params_t *s, int L_P, int L_Q, int a_P, int a_Q, int b_P, int b_Q)
+SPAN_DECLARE(void) set_lab_gamut2(lab_params_t *lab, int L_P, int L_Q, int a_P, int a_Q, int b_P, int b_Q)
 {
-    s->range_L = L_Q/(256.0f - 1.0f);
-    s->range_a = a_Q/(256.0f - 1.0f);
-    s->range_b = b_Q/(256.0f - 1.0f);
+    lab->range_L = L_Q/(256.0f - 1.0f);
+    lab->range_a = a_Q/(256.0f - 1.0f);
+    lab->range_b = b_Q/(256.0f - 1.0f);
 
-    s->offset_L = L_P;
-    s->offset_a = a_P;
-    s->offset_b = b_P;
+    lab->offset_L = L_P;
+    lab->offset_a = a_P;
+    lab->offset_b = b_P;
 
-    s->ab_are_signed = FALSE;
+    lab->ab_are_signed = FALSE;
 }
 /*- End of function --------------------------------------------------------*/
 
-void set_illuminant_from_code(logging_state_t *logging, lab_params_t *s, const uint8_t code[4])
+SPAN_DECLARE(void) get_lab_gamut2(lab_params_t *lab, int *L_P, int *L_Q, int *a_P, int *a_Q, int *b_P, int *b_Q)
+{
+    *L_Q = lab->range_L*(256.0f - 1.0f);
+    *a_Q = lab->range_a*(256.0f - 1.0f);
+    *b_Q = lab->range_b*(256.0f - 1.0f);
+
+    *L_P = lab->offset_L;
+    *a_P = lab->offset_a;
+    *b_P = lab->offset_b;
+}
+/*- End of function --------------------------------------------------------*/
+
+int set_illuminant_from_code(logging_state_t *logging, lab_params_t *lab, const uint8_t code[4])
 {
     int i;
     int colour_temp;
+    float xyz[3];
 
-    if (code[0] == 'C'  &&  code[1] == 'T')
+    if (memcmp(code, "CT", 2) == 0)
     {
         colour_temp = pack_16(&code[2]);
         span_log(logging, SPAN_LOG_FLOW, "Illuminant colour temp %dK\n", colour_temp);
-        return;
+        colour_temp_to_xyz(xyz, (float) colour_temp);
+        set_lab_illuminant(lab, xyz[0], xyz[1], xyz[2]);
+        return colour_temp;
     }
     for (i = 0;  illuminants[i].name[0];  i++)
     {
         if (memcmp(code, illuminants[i].tag, 4) == 0)
         {
             span_log(logging, SPAN_LOG_FLOW, "Illuminant %s\n", illuminants[i].name);
-            set_lab_illuminant(s, illuminants[i].xn, illuminants[i].yn, illuminants[i].zn);
-            break;
+            set_lab_illuminant(lab, illuminants[i].xn, illuminants[i].yn, illuminants[i].zn);
+            return 0;
         }
     }
     if (illuminants[i].name[0] == '\0')
         span_log(logging, SPAN_LOG_FLOW, "Unrecognised illuminant 0x%x 0x%x 0x%x 0x%x\n", code[0], code[1], code[2], code[3]);
+    return -1;
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -253,102 +395,6 @@ void set_gamut_from_code(logging_state_t *logging, lab_params_t *s, const uint8_
 }
 /*- End of function --------------------------------------------------------*/
 
-static int is_itu_fax(logging_state_t *logging, lab_params_t *s, jpeg_saved_marker_ptr ptr)
-{
-    const uint8_t *data;
-    int ok;
-    int val[2];
-    int i;
-
-    ok = FALSE;
-    while (ptr)
-    {
-        if (ptr->marker == (JPEG_APP0 + 1)  &&  ptr->data_length >= 6)
-        {
-            /* Markers are:
-                JPEG_RST0
-                JPEG_EOI
-                JPEG_APP0
-                JPEG_COM */
-            data = (const uint8_t *) ptr->data;
-            if (strncmp((const char *) data, "G3FAX", 5) == 0)
-            {
-                switch (data[5])
-                {
-                case 0:
-                    for (i = 0;  i < 2;  i++)
-                        val[i] = pack_16(&data[6 + 2*i]);
-                    span_log(logging, SPAN_LOG_FLOW, "Version %d, resolution %d dpi\n", val[0], val[1]);
-                    ok = TRUE;
-                    break;
-                case 1:
-                    span_log(logging, SPAN_LOG_FLOW, "Set gamut\n");
-                    if (ptr->data_length >= 6 + 12)
-                    {
-                        set_gamut_from_code(logging, s, &data[6]);
-                        ok = TRUE;
-                    }
-                    else
-                    {
-                        span_log(logging, SPAN_LOG_FLOW, "Got bad G3FAX1 length - %d\n", ptr->data_length);
-                    }
-                    break;
-                case 2:
-                    span_log(logging, SPAN_LOG_FLOW, "Set illuminant\n");
-                    if (ptr->data_length >= 6 + 4)
-                    {
-                        set_illuminant_from_code(logging, s, &data[6]);
-                        ok = TRUE;
-                    }
-                    else
-                    {
-                        span_log(logging, SPAN_LOG_FLOW, "Got bad G3FAX2 length - %d\n", ptr->data_length);
-                    }
-                    break;
-                case 3:
-                    /* Colour palette table */
-                    span_log(logging, SPAN_LOG_FLOW, "Set colour palette\n");
-                    if (ptr->data_length >= 6 + 2)
-                    {
-                        val[0] = pack_16(&data[6]);
-                        span_log(logging, SPAN_LOG_FLOW, "Colour palette %d\n", val[0]);
-                    }
-                    else
-                    {
-                        span_log(logging, SPAN_LOG_FLOW, "Got bad G3FAX3 length - %d\n", ptr->data_length);
-                    }
-                    break;
-                default:
-                    span_log(logging, SPAN_LOG_FLOW, "Got unexpected G3FAX%d length - %d\n", data[5], ptr->data_length);
-                    break;
-                }
-            }
-        }
-        else
-        {
-            span_log(logging, SPAN_LOG_FLOW, "Got marker 0x%x, length %d\n", ptr->marker, ptr->data_length);
-            span_log_buf(logging, SPAN_LOG_FLOW, "Got marker", (const uint8_t *) ptr->data, ptr->data_length);
-        }
-        ptr = ptr->next;
-    }
-
-    return ok;
-}
-/*- End of function --------------------------------------------------------*/
-
-static void set_itu_fax(j_compress_ptr cinfo)
-{
-    uint8_t marker[10] =
-    {
-        'G', '3', 'F', 'A', 'X', '\x00', '\x07', '\xCA', '\x00', '\x00'
-    };
-
-    unpack_16(marker + 8, 200);
-
-    jpeg_write_marker(cinfo, (JPEG_APP0 + 1), marker, 10);
-}
-/*- End of function --------------------------------------------------------*/
-
 static __inline__ void itu_to_lab(lab_params_t *s, cielab_t *lab, const uint8_t in[3])
 {
     uint8_t a;
@@ -496,695 +542,149 @@ SPAN_DECLARE(void) lab_to_srgb(lab_params_t *s, uint8_t srgb[], const uint8_t la
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(int) t42_itulab_to_jpeg(logging_state_t *logging, lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen)
+static int is_itu_fax(t42_decode_state_t *s, jpeg_saved_marker_ptr ptr)
 {
-    struct jpeg_decompress_struct decompressor;
-    struct jpeg_compress_struct compressor;
-    FILE *in;
-    FILE *out;
-    int m;
-    JSAMPROW scan_line_in;
-    JSAMPROW scan_line_out;
-    escape_route_t escape;
-#if defined(HAVE_OPEN_MEMSTREAM)
-    char *outptr;
-    size_t outsize;
-#endif
+    const uint8_t *data;
+    int ok;
+    int val[6];
 
-    escape.error_message[0] = '\0';
-
-#if defined(HAVE_OPEN_MEMSTREAM)
-    if ((in = fmemopen(src, srclen, "r")) == NULL)
+    ok = FALSE;
+    for (  ;  ptr;  ptr = ptr->next)
     {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to fmemopen().\n");
-        return FALSE;
-    }
-    outsize = 0;
-    if ((out = open_memstream(&outptr, &outsize)) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n");
-        fclose(in);
-        return FALSE;
-    }
-    if (fseek(out, 0, SEEK_SET) != 0)
-    {
-        fclose(in);
-        fclose(out);
-        return FALSE;
-    }
-#else
-    if ((in = tmpfile()) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
-        return FALSE;
-    }
-    if (fwrite(src, 1, srclen, in) != srclen)
-    {
-        fclose(in);
-        return FALSE;
-    }
-    if (fseek(in, 0, SEEK_SET) != 0)
-    {
-        fclose(in);
-        return FALSE;
-    }
-    if ((out = tmpfile()) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
-        fclose(in);
-        return FALSE;
-    }
-#endif
-    scan_line_out = NULL;
-
-    if (setjmp(escape.escape))
-    {
-        if (escape.error_message[0])
-            span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message);
-        else
-            span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n");
-        if (scan_line_out)
-            free(scan_line_out);
-        fclose(in);
-        fclose(out);
-        return FALSE;
+        if (ptr->marker != (JPEG_APP0 + 1))
+            continue;
+        if (ptr->data_length < 6)
+            return FALSE;
+        /* Markers are:
+            JPEG_RST0
+            JPEG_EOI
+            JPEG_APP0
+            JPEG_COM */
+        data = (const uint8_t *) ptr->data;
+        if (strncmp((const char *) data, "G3FAX", 5))
+            return FALSE;
+        switch (data[5])
+        {
+        case 0:
+            if (ptr->data_length < 6 + 4)
+            {
+                span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX0 length - %d\n", ptr->data_length);
+                return FALSE;
+            }
+            val[0] = pack_16(&data[6]);
+            s->spatial_resolution = pack_16(&data[6 + 2]);
+            span_log(&s->logging, SPAN_LOG_FLOW, "Version %d, resolution %ddpi\n", val[0], s->spatial_resolution);
+            ok = TRUE;
+            break;
+        case 1:
+            span_log(&s->logging, SPAN_LOG_FLOW, "Set gamut\n");
+            if (ptr->data_length < 6 + 12)
+            {
+                span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX1 length - %d\n", ptr->data_length);
+                return FALSE;
+            }
+            set_gamut_from_code(&s->logging, &s->lab, &data[6]);
+            break;
+        case 2:
+            span_log(&s->logging, SPAN_LOG_FLOW, "Set illuminant\n");
+            if (ptr->data_length < 6 + 4)
+            {
+                span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX2 length - %d\n", ptr->data_length);
+                return FALSE;
+            }
+            s->illuminant_colour_temperature = set_illuminant_from_code(&s->logging, &s->lab, &data[6]);
+            break;
+        default:
+            span_log(&s->logging, SPAN_LOG_FLOW, "Got unexpected G3FAX%d length - %d\n", data[5], ptr->data_length);
+            return FALSE;
+        }
     }
 
-    /* Create input decompressor. */
-    decompressor.err = jpeg_std_error(&error_handler);
-    decompressor.client_data = (void *) &escape;
-    error_handler.error_exit = jpg_error_exit;
-    error_handler.output_message = jpg_error_exit;
-
-    jpeg_create_decompress(&decompressor);
-    jpeg_stdio_src(&decompressor, in);
-
-    /* Needed in the case of ITU Lab input */
-    for (m = 0;  m < 16;  m++)
-        jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
-
-    /* Rewind the file */
-    if (fseek(in, 0, SEEK_SET) != 0)
-        return FALSE;
-
-    /* Take the header */
-    jpeg_read_header(&decompressor, TRUE);
-
-    /* Now we can force the input colour space. For ITULab, we use YCbCr as a "don't touch" marker */
-    decompressor.out_color_space = JCS_YCbCr;
-
-    /* Sanity check and parameter check */
-    if (!is_itu_fax(logging, s, decompressor.marker_list))
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Is not an ITU FAX.\n");
-        return FALSE;
-    }
-
-    /* Create compressor */
-    compressor.err = jpeg_std_error(&error_handler);
-    compressor.client_data = (void *) &escape;
-    error_handler.error_exit = jpg_error_exit;
-    error_handler.output_message = jpg_error_exit;
-
-    jpeg_create_compress(&compressor);
-    jpeg_stdio_dest(&compressor, out);
-
-    /* Force the destination colour space */
-    compressor.in_color_space = JCS_RGB;
-    compressor.input_components = 3;
-
-    jpeg_set_defaults(&compressor);
-    /* Limit to baseline-JPEG values */
-    //jpeg_set_quality(&compressor, quality, TRUE);
-
-    /* Copy size, resolution, etc */
-    jpeg_copy_critical_parameters(&decompressor, &compressor);
-
-    /* We need to keep these */
-    compressor.density_unit = decompressor.density_unit;
-    compressor.X_density = decompressor.X_density;
-    compressor.Y_density = decompressor.Y_density;
-
-    jpeg_start_decompress(&decompressor);
-    jpeg_start_compress(&compressor, TRUE);
-
-    if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
-        return FALSE;
-
-    if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
-    {
-        free(scan_line_in);
-        return FALSE;
-    }
-
-    while (decompressor.output_scanline < decompressor.output_height)
-    {
-        jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
-        lab_to_srgb(s, scan_line_out, scan_line_in, decompressor.output_width);
-        jpeg_write_scanlines(&compressor, &scan_line_out, 1);
-    }
-    free(scan_line_in);
-    free(scan_line_out);
-    jpeg_finish_decompress(&decompressor);
-    jpeg_finish_compress(&compressor);
-    jpeg_destroy_decompress(&decompressor);
-    jpeg_destroy_compress(&compressor);
-    fclose(in);
-
-#if defined(HAVE_OPEN_MEMSTREAM)
-    fclose(out);
-    *dst = outptr;
-    *dstlen = outsize;
-#else
-    *dstlen = ftell(out);
-    *dst = malloc(*dstlen);
-    if (fseek(out, 0, SEEK_SET) != 0)
-    {
-        fclose(out);
-        return FALSE;
-    }
-    if (fread(*dst, 1, *dstlen, out) != *dstlen)
-    {
-        free(*dst);
-        fclose(out);
-        return FALSE;
-    }
-    fclose(out);
-#endif
-
-    return TRUE;
+    return ok;
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(int) t42_jpeg_to_itulab(logging_state_t *logging, lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen)
+static void set_itu_fax(t42_encode_state_t *s)
 {
-    struct jpeg_decompress_struct decompressor;
-    struct jpeg_compress_struct compressor;
-    FILE *in;
-    FILE *out;
-    int m;
-    JSAMPROW scan_line_in;
-    JSAMPROW scan_line_out;
-    escape_route_t escape;
-#if defined(HAVE_OPEN_MEMSTREAM)
-    char *outptr;
-    size_t outsize;
-#endif
+    uint8_t data[50];
+    int val[6];
 
-    escape.error_message[0] = '\0';
+    memcpy(data, "G3FAX\0", 6);
+    unpack_16(&data[6 + 0], 1994);
+    unpack_16(&data[6 + 2], s->spatial_resolution);
+    jpeg_write_marker(&s->compressor, (JPEG_APP0 + 1), data, 6 + 4);
 
-#if defined(HAVE_OPEN_MEMSTREAM)
-    if ((in = fmemopen(src, srclen, "r")) == NULL)
+    if (s->lab.offset_L != 0
+        ||
+        s->lab.range_L != 100
+        ||
+        s->lab.offset_a != 128
+        ||
+        s->lab.range_a != 170
+        ||
+        s->lab.offset_b != 96
+        ||
+        s->lab.range_b != 200)
     {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to fmemopen().\n");
-        return FALSE;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX1\n");
+        memcpy(data, "G3FAX\1", 6);
+        get_lab_gamut2(&s->lab, &val[0], &val[1], &val[2], &val[3], &val[4], &val[5]);
+        unpack_16(&data[6 + 0], val[0]);
+        unpack_16(&data[6 + 2], val[1]);
+        unpack_16(&data[6 + 4], val[2]);
+        unpack_16(&data[6 + 6], val[3]);
+        unpack_16(&data[6 + 8], val[4]);
+        unpack_16(&data[6 + 10], val[5]);
+        jpeg_write_marker(&s->compressor, (JPEG_APP0 + 1), data, 6 + 12);
     }
-    outsize = 0;
-    if ((out = open_memstream(&outptr, &outsize)) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n");
-        fclose(in);
-        return FALSE;
-    }
-    if (fseek(out, 0, SEEK_SET) != 0)
-    {
-        fclose(in);
-        fclose(out);
-        return FALSE;
-    }
-#else
-    if ((in = tmpfile()) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
-        return FALSE;
-    }
-    if (fwrite(src, 1, srclen, in) != srclen)
-    {
-        fclose(in);
-        return FALSE;
-    }
-    if (fseek(in, 0, SEEK_SET) != 0)
-    {
-        fclose(in);
-        return FALSE;
-    }
-    if ((out = tmpfile()) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
-        fclose(in);
-        return FALSE;
-    }
-#endif
-    scan_line_out = NULL;
 
-    if (setjmp(escape.escape))
+    if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0
+        ||
+        s->illuminant_colour_temperature > 0)
     {
-        if (escape.error_message[0])
-            span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message);
+        span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX2\n");
+        memcpy(data, "G3FAX\2", 6);
+        if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0)
+        {
+            memcpy(&data[6], s->illuminant_code, 4);
+        }
         else
-            span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n");
-        if (scan_line_out)
-            free(scan_line_out);
-        fclose(in);
-        fclose(out);
-        return FALSE;
+        {
+            memcpy(&data[6 + 0], "CT", 2);
+            unpack_16(&data[6 + 2], s->illuminant_colour_temperature);
+        }
+        jpeg_write_marker(&s->compressor, (JPEG_APP0 + 1), data, 6 + 4);
     }
-    /* Create input decompressor. */
-    decompressor.err = jpeg_std_error(&error_handler);
-    decompressor.client_data = (void *) &escape;
-    error_handler.error_exit = jpg_error_exit;
-    error_handler.output_message = jpg_error_exit;
-
-    jpeg_create_decompress(&decompressor);
-    jpeg_stdio_src(&decompressor, in);
-
-    /* Needed in the case of ITU Lab input */
-    for (m = 0;  m < 16;  m++)
-        jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
-
-    /* Rewind the file */
-    if (fseek(in, 0, SEEK_SET) != 0)
-        return FALSE;
-
-    /* Take the header */
-    jpeg_read_header(&decompressor, TRUE);
-
-    /* Now we can force the input colour space. For ITULab, we use YCbCr as a "don't touch" marker */
-    decompressor.out_color_space = JCS_RGB;
-
-    compressor.err = jpeg_std_error(&error_handler);
-    compressor.client_data = (void *) &escape;
-    error_handler.error_exit = jpg_error_exit;
-    error_handler.output_message = jpg_error_exit;
-
-    jpeg_create_compress(&compressor);
-    jpeg_stdio_dest(&compressor, out);
-
-    /* Force the destination colour space */
-    compressor.in_color_space = JCS_YCbCr;
-    compressor.input_components = 3;
-
-    jpeg_set_defaults(&compressor);
-    /* Limit to baseline-JPEG values */
-    //jpeg_set_quality(&compressor, quality, TRUE);
-
-    jpeg_copy_critical_parameters(&decompressor, &compressor);
-
-    /* We need to keep these */
-    compressor.density_unit = decompressor.density_unit;
-    compressor.X_density = decompressor.X_density;
-    compressor.Y_density = decompressor.Y_density;
-
-    jpeg_start_decompress(&decompressor);
-    jpeg_start_compress(&compressor, TRUE);
-
-    set_itu_fax(&compressor);
-
-    if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
-        return FALSE;
-
-    if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
-    {
-        free(scan_line_in);
-        return FALSE;
-    }
-
-    while (decompressor.output_scanline < decompressor.output_height)
-    {
-        jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
-        srgb_to_lab(s, scan_line_out, scan_line_in, decompressor.output_width);
-        jpeg_write_scanlines(&compressor, &scan_line_out, 1);
-    }
-
-    free(scan_line_in);
-    free(scan_line_out);
-    jpeg_finish_decompress(&decompressor);
-    jpeg_finish_compress(&compressor);
-    jpeg_destroy_decompress(&decompressor);
-    jpeg_destroy_compress(&compressor);
-    fclose(in);
-
-#if defined(HAVE_OPEN_MEMSTREAM)
-    fclose(out);
-    *dst = outptr;
-    *dstlen = outsize;
-#else
-    *dstlen = ftell(out);
-    *dst = malloc(*dstlen);
-    if (fseek(out, 0, SEEK_SET) != 0)
-    {
-        fclose(out);
-        return FALSE;
-    }
-    if (fread(*dst, 1, *dstlen, out) != *dstlen)
-    {
-        free(*dst);
-        fclose(out);
-        return FALSE;
-    }
-    fclose(out);
-#endif
-
-    return TRUE;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) t42_srgb_to_itulab(logging_state_t *logging, lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height)
-{
-    struct jpeg_compress_struct compressor;
-    FILE *out;
-    JSAMPROW scan_line_out;
-    JSAMPROW scan_line_in;
-    tsize_t pos;
-    escape_route_t escape;
-#if defined(HAVE_OPEN_MEMSTREAM)
-    char *outptr;
-    size_t outsize;
-#endif
-
-    escape.error_message[0] = '\0';
-
-#if defined(HAVE_OPEN_MEMSTREAM)
-    outsize = 0;
-    if ((out = open_memstream(&outptr, &outsize)) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n");
-        return FALSE;
-    }
-    if (fseek(out, 0, SEEK_SET) != 0)
-    {
-        fclose(out);
-        return FALSE;
-    }
-#else
-    if ((out = tmpfile()) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
-        return FALSE;
-    }
-#endif
-    scan_line_out = NULL;
-
-    if (setjmp(escape.escape))
-    {
-        if (escape.error_message[0])
-            span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message);
-        else
-            span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n");
-        if (scan_line_out)
-            free(scan_line_out);
-        fclose(out);
-        return FALSE;
-    }
-
-    compressor.err = jpeg_std_error(&error_handler);
-    compressor.client_data = (void *) &escape;
-    error_handler.error_exit = jpg_error_exit;
-    error_handler.output_message = jpg_error_exit;
-
-    jpeg_create_compress(&compressor);
-    jpeg_stdio_dest(&compressor, out);
-
-    /* Force the destination colour space */
-    compressor.in_color_space = JCS_YCbCr;
-    compressor.input_components = 3;
-
-    jpeg_set_defaults(&compressor);
-    /* Limit to baseline-JPEG values */
-    //jpeg_set_quality(&compressor, quality, TRUE);
-
-    /* Size, resolution, etc */
-    compressor.image_width = width;
-    compressor.image_height = height;
-
-    jpeg_start_compress(&compressor, TRUE);
-
-    set_itu_fax(&compressor);
-
-    if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
-        return FALSE;
-
-    for (pos = 0;  pos < srclen;  pos += compressor.image_width*compressor.num_components)
-    {
-        scan_line_in = (JSAMPROW) src + pos;
-        srgb_to_lab(s, scan_line_out, scan_line_in, compressor.image_width);
-        jpeg_write_scanlines(&compressor, &scan_line_out, 1);
-    }
-
-    free(scan_line_out);
-    jpeg_finish_compress(&compressor);
-    jpeg_destroy_compress(&compressor);
-
-#if defined(HAVE_OPEN_MEMSTREAM)
-    fclose(out);
-    *dst = outptr;
-    *dstlen = outsize;
-#else
-    *dstlen = ftell(out);
-    *dst = malloc(*dstlen);
-    if (fseek(out, 0, SEEK_SET) != 0)
-    {
-        fclose(out);
-        return FALSE;
-    }
-    if (fread(*dst, 1, *dstlen, out) != *dstlen)
-    {
-        free(*dst);
-        fclose(out);
-        return FALSE;
-    }
-    fclose(out);
-#endif
-
-    return TRUE;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) t42_itulab_to_itulab(logging_state_t *logging, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height)
-{
-    struct jpeg_compress_struct compressor;
-    FILE *out;
-    JSAMPROW scan_line_in;
-    tsize_t pos;
-    escape_route_t escape;
-#if defined(HAVE_OPEN_MEMSTREAM)
-    char *outptr;
-    size_t outsize;
-#endif
-
-    escape.error_message[0] = '\0';
-
-#if defined(HAVE_OPEN_MEMSTREAM)
-    outsize = 0;
-    if ((out = open_memstream(&outptr, &outsize)) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n");
-        return FALSE;
-    }
-    if (fseek(out, 0, SEEK_SET) != 0)
-    {
-        fclose(out);
-        return FALSE;
-    }
-#else
-    if ((out = tmpfile()) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
-        return FALSE;
-    }
-#endif
-
-    if (setjmp(escape.escape))
-    {
-        if (escape.error_message[0])
-            span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message);
-        else
-            span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n");
-        fclose(out);
-        return FALSE;
-    }
-
-    compressor.err = jpeg_std_error(&error_handler);
-    compressor.client_data = (void *) &escape;
-    error_handler.error_exit = jpg_error_exit;
-    error_handler.output_message = jpg_error_exit;
-
-    jpeg_create_compress(&compressor);
-    jpeg_stdio_dest(&compressor, out);
-
-    /* Force the destination colour space */
-    compressor.in_color_space = JCS_YCbCr;
-    compressor.input_components = 3;
-
-    jpeg_set_defaults(&compressor);
-    /* Limit to baseline-JPEG values */
-    //jpeg_set_quality(&compressor, quality, TRUE);
-
-    /* Size, resolution, etc */
-    compressor.image_width = width;
-    compressor.image_height = height;
-
-    jpeg_start_compress(&compressor, TRUE);
-
-    set_itu_fax(&compressor);
-
-    for (pos = 0;  pos < srclen;  pos += compressor.image_width*compressor.num_components)
-    {
-        scan_line_in = (JSAMPROW) src + pos;
-        jpeg_write_scanlines(&compressor, &scan_line_in, 1);
-    }
-
-    jpeg_finish_compress(&compressor);
-    jpeg_destroy_compress(&compressor);
-
-#if defined(HAVE_OPEN_MEMSTREAM)
-    fclose(out);
-    *dst = outptr;
-    *dstlen = outsize;
-#else
-    *dstlen = ftell(out);
-    *dst = malloc(*dstlen);
-    if (fseek(out, 0, SEEK_SET) != 0)
-    {
-        fclose(out);
-        return FALSE;
-    }
-    if (fread(*dst, 1, *dstlen, out) != *dstlen)
-    {
-        free(*dst);
-        fclose(out);
-        return FALSE;
-    }
-    fclose(out);
-#endif
-
-    return TRUE;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) t42_itulab_to_srgb(logging_state_t *logging, lab_params_t *s, tdata_t dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t *width, uint32_t *height)
-{
-    struct jpeg_decompress_struct decompressor;
-    JSAMPROW scan_line_out;
-    JSAMPROW scan_line_in;
-    tsize_t pos;
-    FILE *in;
-    int m;
-    escape_route_t escape;
-
-    escape.error_message[0] = '\0';
-
-#if defined(HAVE_OPEN_MEMSTREAM)
-    if ((in = fmemopen(src, srclen, "r")) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to fmemopen().\n");
-        return FALSE;
-    }
-#else
-    if ((in = tmpfile()) == NULL)
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
-        return FALSE;
-    }
-    if (fwrite(src, 1, srclen, in) != srclen)
-    {
-        fclose(in);
-        return FALSE;
-    }
-    if (fseek(in, 0, SEEK_SET) != 0)
-    {
-        fclose(in);
-        return FALSE;
-    }
-#endif
-    scan_line_out = NULL;
-
-    if (setjmp(escape.escape))
-    {
-        if (escape.error_message[0])
-            span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message);
-        else
-            span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n");
-printf("Error %s.\n", escape.error_message);
-        if (scan_line_out)
-            free(scan_line_out);
-        fclose(in);
-        return FALSE;
-    }
-    /* Create input decompressor. */
-    decompressor.err = jpeg_std_error(&error_handler);
-    decompressor.client_data = (void *) &escape;
-    error_handler.error_exit = jpg_error_exit;
-    error_handler.output_message = jpg_error_exit;
-
-    jpeg_create_decompress(&decompressor);
-    jpeg_stdio_src(&decompressor, in);
-
-    /* Needed in the case of ITU Lab input */
-    for (m = 0;  m < 16;  m++)
-        jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
-
-    /* Rewind the file */
-    if (fseek(in, 0, SEEK_SET) != 0)
-        return FALSE;
-    /* Take the header */
-    jpeg_read_header(&decompressor, FALSE);
-    /* Now we can force the input colour space. For ITULab, we use YCbCr as a "don't touch" marker */
-    decompressor.out_color_space = JCS_YCbCr;
-    /* Sanity check and parameter check */
-    if (!is_itu_fax(logging, s, decompressor.marker_list))
-    {
-        span_log(logging, SPAN_LOG_FLOW, "Is not an ITU FAX.\n");
-printf("Is not an ITU FAX 1.\n");
-        //return FALSE;
-    }
-    /* Copy size, resolution, etc */
-    *width = decompressor.image_width;
-    *height = decompressor.image_height;
-printf("Is %d x %d\n", decompressor.image_width, decompressor.image_height);
-
-    jpeg_start_decompress(&decompressor);
-
-printf("Is %d x %d x %d.\n", decompressor.output_width, decompressor.output_height, decompressor.num_components);
-    if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
-        return FALSE;
-
-    for (pos = 0;  decompressor.output_scanline < decompressor.output_height;  pos += decompressor.output_width*decompressor.num_components)
-    {
-        scan_line_out = (JSAMPROW) dst + pos;
-        jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
-        lab_to_srgb(s, scan_line_out, scan_line_in, decompressor.output_width);
-    }
-printf("Next %d\n", decompressor.output_scanline);
-
-    free(scan_line_in);
-    jpeg_finish_decompress(&decompressor);
-    jpeg_destroy_decompress(&decompressor);
-    fclose(in);
-
-    *dstlen = pos;
-
-printf("Next2 %d\n", decompressor.output_scanline);
-    return TRUE;
 }
 /*- End of function --------------------------------------------------------*/
 
 SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s,
                                           uint32_t l0,
-                                          int mx,
+                                          int quality,
                                           int options)
 {
+    s->quality = quality;
+    s->no_subsampling = (options & 1);
 }
 /*- End of function --------------------------------------------------------*/
 
 SPAN_DECLARE(int) t42_encode_set_image_width(t42_encode_state_t *s, uint32_t image_width)
 {
+    s->image_width = image_width;
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t length)
+SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t image_length)
 {
+    s->image_length = image_length;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_set_image_type(t42_encode_state_t *s, int image_type)
+{
+    s->image_type = image_type;
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
@@ -1201,25 +701,201 @@ SPAN_DECLARE(void) t42_encode_comment(t42_encode_state_t *s, const uint8_t comme
 
 SPAN_DECLARE(int) t42_encode_image_complete(t42_encode_state_t *s)
 {
+    //if (????)
+    //    return SIG_STATUS_END_OF_DATA;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Error handler for IJG library */
+static void jpg_encode_error_exit(j_common_ptr cinfo)
+{
+    t42_encode_state_t *s;
+
+    s = (t42_encode_state_t *) cinfo->client_data;
+    (*cinfo->err->format_message)(cinfo, s->error_message);
+    longjmp(s->escape, 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+/* This is the error catcher */
+static struct jpeg_error_mgr encode_error_handler =
+{
+    .error_exit = jpg_encode_error_exit,
+    .output_message = jpg_encode_error_exit
+};
+
+static int t42_srgb_to_itulab_jpeg(t42_encode_state_t *s)
+{
+    int i;
+
+    if (setjmp(s->escape))
+    {
+        if (s->error_message[0])
+            span_log(&s->logging, SPAN_LOG_FLOW, "%s\n", s->error_message);
+        else
+            span_log(&s->logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n");
+        if (s->scan_line_out)
+        {
+            free(s->scan_line_out);
+            s->scan_line_out = NULL;
+        }
+        if (s->out)
+        {
+            fclose(s->out);
+            s->out = NULL;
+        }
+        return -1;
+    }
+
+    s->compressor.err = jpeg_std_error(&encode_error_handler);
+    s->compressor.client_data = (void *) s;
+
+    jpeg_create_compress(&s->compressor);
+    jpeg_stdio_dest(&s->compressor, s->out);
+
+    /* Force the destination colour space */
+    if (s->image_type == T4_IMAGE_TYPE_COLOUR_8BIT)
+    {
+        s->samples_per_pixel = 3;
+        s->compressor.in_color_space = JCS_YCbCr;
+        s->compressor.input_components = s->samples_per_pixel;
+    }
+    else
+    {
+        s->samples_per_pixel = 1;
+        s->compressor.in_color_space = JCS_GRAYSCALE;
+        s->compressor.input_components = s->samples_per_pixel;
+    }
+
+    jpeg_set_defaults(&s->compressor);
+    /* Limit to baseline-JPEG values */
+    //jpeg_set_quality(&s->compressor, s->quality, TRUE);
+
+    if (s->no_subsampling)
+    {
+        /* Set 1:1:1 */
+        s->compressor.comp_info[0].h_samp_factor = 1;
+        s->compressor.comp_info[0].v_samp_factor = 1;
+    }
+    else
+    {
+        /* Set 4:1:1 */
+        s->compressor.comp_info[0].h_samp_factor = 2;
+        s->compressor.comp_info[0].v_samp_factor = 2;
+    }
+    s->compressor.comp_info[1].h_samp_factor = 1;
+    s->compressor.comp_info[1].v_samp_factor = 1;
+    s->compressor.comp_info[2].h_samp_factor = 1;
+    s->compressor.comp_info[2].v_samp_factor = 1;
+
+    /* Size, resolution, etc */
+    s->compressor.image_width = s->image_width;
+    s->compressor.image_height = s->image_length;
+
+    jpeg_start_compress(&s->compressor, TRUE);
+
+    set_itu_fax(s);
+
+    if ((s->scan_line_in = (JSAMPROW) malloc(s->samples_per_pixel*s->image_width)) == NULL)
+        return -1;
+
+    if (s->image_type == T4_IMAGE_TYPE_COLOUR_8BIT)
+    {
+        if ((s->scan_line_out = (JSAMPROW) malloc(s->samples_per_pixel*s->image_width)) == NULL)
+            return -1;
+
+        for (i = 0;  i < s->compressor.image_height;  i++)
+        {
+            s->row_read_handler(s->row_read_user_data, s->scan_line_in, s->samples_per_pixel*s->image_width);
+            srgb_to_lab(&s->lab, s->scan_line_out, s->scan_line_in, s->image_width);
+            jpeg_write_scanlines(&s->compressor, &s->scan_line_out, 1);
+        }
+    }
+    else
+    {
+        for (i = 0;  i < s->compressor.image_height;  i++)
+        {
+            s->row_read_handler(s->row_read_user_data, s->scan_line_in, s->image_width);
+            jpeg_write_scanlines(&s->compressor, &s->scan_line_in, 1);
+        }
+    }
+
+    if (s->scan_line_out)
+    {
+        free(s->scan_line_out);
+        s->scan_line_out = NULL;
+    }
+    jpeg_finish_compress(&s->compressor);
+    jpeg_destroy_compress(&s->compressor);
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    fclose(s->out);
+    s->buf_size =
+    s->compressed_image_size = s->outsize;
+#else
+    s->buf_size =
+    s->compressed_image_size = ftell(s->out);
+    if ((s->compressed_buf = malloc(s->compressed_image_size)) == NULL)
+        return -1;
+    if (fseek(s->out, 0, SEEK_SET) != 0)
+    {
+        fclose(s->out);
+        s->out = NULL;
+        free(s->compressed_buf);
+        s->compressed_buf = NULL;
+        return -1;
+    }
+    if (fread(s->compressed_buf, 1, s->compressed_image_size, s->out) != s->compressed_image_size)
+    {
+        fclose(s->out);
+        s->out = NULL;
+        free(s->compressed_buf);
+        s->compressed_buf = NULL;
+        return -1;
+    }
+    if (s->out)
+    {
+        fclose(s->out);
+        s->out = NULL;
+    }
+#endif
+
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
 
 SPAN_DECLARE(int) t42_encode_get(t42_encode_state_t *s, uint8_t buf[], size_t max_len)
 {
-    return 0;
+    int len;
+
+    if (s->compressed_image_size == 0)
+    {
+        if (t42_srgb_to_itulab_jpeg(s))
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert to ITULAB.\n");
+            return -1;
+        }
+    }
+    if (s->compressed_image_size >= s->compressed_image_ptr + max_len)
+        len = max_len;
+    else
+        len = s->compressed_image_size - s->compressed_image_ptr;
+    memcpy(buf, &s->compressed_buf[s->compressed_image_ptr], len);
+    s->compressed_image_ptr += len;
+    return len;
 }
 /*- End of function --------------------------------------------------------*/
 
 SPAN_DECLARE(uint32_t) t42_encode_get_image_width(t42_encode_state_t *s)
 {
-    return 0;
+    return s->image_width;
 }
 /*- End of function --------------------------------------------------------*/
 
 SPAN_DECLARE(uint32_t) t42_encode_get_image_length(t42_encode_state_t *s)
 {
-    return 0;
+    return s->image_length;
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -1233,6 +909,8 @@ SPAN_DECLARE(int) t42_encode_set_row_read_handler(t42_encode_state_t *s,
                                                   t4_row_read_handler_t handler,
                                                   void *user_data)
 {
+    s->row_read_handler = handler;
+    s->row_read_user_data = user_data;
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
@@ -1245,9 +923,52 @@ SPAN_DECLARE(logging_state_t *) t42_encode_get_logging_state(t42_encode_state_t
 
 SPAN_DECLARE(int) t42_encode_restart(t42_encode_state_t *s, uint32_t image_width, uint32_t image_length)
 {
-    //s->image_width = image_width;
-    //s->image_length = image_length;
+    s->image_width = image_width;
+    s->image_length = image_length;
+
+    if (s->itu_ycc)
+    {
+        /* ITU-YCC */
+        /* Illuminant D65 */
+        set_lab_illuminant(&s->lab, 95.047f, 100.000f, 108.883f);
+        set_lab_gamut(&s->lab, 0, 100, -127, 127, -127, 127, FALSE);
+    }
+    else
+    {
+        /* ITULAB */
+        /* Illuminant D50 */
+        set_lab_illuminant(&s->lab, 96.422f, 100.000f,  82.521f);
+        set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE);
+    }
     s->compressed_image_size = 0;
+    s->compressed_image_ptr = 0;
+
+    s->spatial_resolution = 200;
+
+    s->error_message[0] = '\0';
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    s->outsize = 0;
+    if ((s->out = open_memstream((char **) &s->compressed_buf, &s->outsize)) == NULL)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n");
+        return -1;
+    }
+    if (fseek(s->out, 0, SEEK_SET) != 0)
+    {
+        fclose(s->out);
+        s->out = NULL;
+        return -1;
+    }
+#else
+    if ((s->out = tmpfile()) == NULL)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
+        return -1;
+    }
+#endif
+    s->scan_line_out = NULL;
+
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
@@ -1267,8 +988,12 @@ SPAN_DECLARE(t42_encode_state_t *) t42_encode_init(t42_encode_state_t *s,
     span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
     span_log_set_protocol(&s->logging, "T.42");
 
+    s->quality = 90;
+    s->image_type = T4_IMAGE_TYPE_COLOUR_8BIT;
+
     s->row_read_handler = handler;
     s->row_read_user_data = user_data;
+
     t42_encode_restart(s, image_width, image_length);
 
     return s;
@@ -1291,8 +1016,202 @@ SPAN_DECLARE(int) t42_encode_free(t42_encode_state_t *s)
 }
 /*- End of function --------------------------------------------------------*/
 
+/* Error handler for IJG library */
+static void jpg_decode_error_exit(j_common_ptr cinfo)
+{
+    t42_decode_state_t *s;
+
+    s = (t42_decode_state_t *) cinfo->client_data;
+    (*cinfo->err->format_message)(cinfo, s->error_message);
+    longjmp(s->escape, 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+/* This is the error catcher */
+static struct jpeg_error_mgr decode_error_handler =
+{
+    .error_exit = jpg_decode_error_exit,
+    .output_message = jpg_decode_error_exit
+};
+
+static int t42_itulab_jpeg_to_srgb(t42_decode_state_t *s)
+{
+    int i;
+
+    if (s->compressed_buf == NULL)
+        return -1;
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    if ((s->in = fmemopen(s->compressed_buf, s->compressed_image_size, "r")) == NULL)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Failed to fmemopen().\n");
+        return -1;
+    }
+#else
+    if ((s->in = tmpfile()) == NULL)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n");
+        return -1;
+    }
+    if (fwrite(s->compressed_buf, 1, s->compressed_image_size, s->in) != s->compressed_image_size)
+    {
+        fclose(s->in);
+        s->in = NULL;
+        return -1;
+    }
+    if (fseek(s->in, 0, SEEK_SET) != 0)
+    {
+        fclose(s->in);
+        s->in = NULL;
+        return -1;
+    }
+#endif
+    s->scan_line_out = NULL;
+
+    if (setjmp(s->escape))
+    {
+        if (s->error_message[0])
+            span_log(&s->logging, SPAN_LOG_FLOW, "%s\n", s->error_message);
+        else
+            span_log(&s->logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n");
+        if (s->scan_line_out)
+        {
+            free(s->scan_line_out);
+            s->scan_line_out = NULL;
+        }
+        if (s->in)
+        {
+            fclose(s->in);
+            s->in = NULL;
+        }
+        return -1;
+    }
+    /* Create input decompressor. */
+    s->decompressor.err = jpeg_std_error(&decode_error_handler);
+    s->decompressor.client_data = (void *) s;
+
+    jpeg_create_decompress(&s->decompressor);
+    jpeg_stdio_src(&s->decompressor, s->in);
+
+    /* Get the FAX tags */
+    for (i = 0;  i < 16;  i++)
+        jpeg_save_markers(&s->decompressor, JPEG_APP0 + i, 0xFFFF);
+
+    /* Rewind the file */
+    if (fseek(s->in, 0, SEEK_SET) != 0)
+    {
+        fclose(s->in);
+        s->in = NULL;
+        return -1;
+    }
+
+    /* Take the header */
+    jpeg_read_header(&s->decompressor, FALSE);
+    /* Sanity check and parameter check */
+    if (!is_itu_fax(s, s->decompressor.marker_list))
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Is not an ITU FAX.\n");
+        return -1;
+    }
+    /* Copy size, resolution, etc */
+    s->image_width = s->decompressor.image_width;
+    s->image_length = s->decompressor.image_height;
+    s->samples_per_pixel = s->decompressor.num_components;
+
+    if (s->samples_per_pixel == 3)
+    {
+        /* Now we can force the input colour space. For ITULab, we use YCbCr as a "don't touch" marker */
+        s->decompressor.out_color_space = JCS_YCbCr;
+        span_log(&s->logging,
+                 SPAN_LOG_FLOW, 
+                 "Sampling %d %d %d %d %d %d\n",
+                 s->decompressor.comp_info[0].h_samp_factor,
+                 s->decompressor.comp_info[0].v_samp_factor,
+                 s->decompressor.comp_info[1].h_samp_factor,
+                 s->decompressor.comp_info[1].v_samp_factor,
+                 s->decompressor.comp_info[2].h_samp_factor,
+                 s->decompressor.comp_info[2].v_samp_factor); 
+    }
+    else
+    {
+        s->decompressor.out_color_space = JCS_GRAYSCALE;
+        span_log(&s->logging,
+                 SPAN_LOG_FLOW, 
+                 "Sampling %d %d\n",
+                 s->decompressor.comp_info[0].h_samp_factor,
+                 s->decompressor.comp_info[0].v_samp_factor); 
+    }
+
+    jpeg_start_decompress(&s->decompressor);
+
+    if ((s->scan_line_in = malloc(s->samples_per_pixel*s->image_width)) == NULL)
+        return -1;
+
+    if (s->samples_per_pixel == 3)
+    {
+        if ((s->scan_line_out = malloc(s->samples_per_pixel*s->image_width)) == NULL)
+            return -1;
+
+        while (s->decompressor.output_scanline < s->image_length)
+        {
+            jpeg_read_scanlines(&s->decompressor, &s->scan_line_in, 1);
+            lab_to_srgb(&s->lab, s->scan_line_out, s->scan_line_in, s->image_width);
+            s->row_write_handler(s->row_write_user_data, s->scan_line_out, s->samples_per_pixel*s->image_width);
+        }
+    }
+    else
+    {
+        while (s->decompressor.output_scanline < s->image_length)
+        {
+            jpeg_read_scanlines(&s->decompressor, &s->scan_line_in, 1);
+            s->row_write_handler(s->row_write_user_data, s->scan_line_in, s->image_width);
+        }
+    }
+
+    if (s->scan_line_in)
+    {
+        free(s->scan_line_in);
+        s->scan_line_in = NULL;
+    }
+    if (s->scan_line_out)
+    {
+        free(s->scan_line_out);
+        s->scan_line_out = NULL;
+    }
+    jpeg_finish_decompress(&s->decompressor);
+    jpeg_destroy_decompress(&s->decompressor);
+    fclose(s->in);
+    s->in = NULL;
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
 SPAN_DECLARE(void) t42_decode_rx_status(t42_decode_state_t *s, int status)
 {
+    span_log(&s->logging, SPAN_LOG_FLOW, "Signal status is %s (%d)\n", signal_status_to_str(status), status);
+    switch (status)
+    {
+    case SIG_STATUS_TRAINING_IN_PROGRESS:
+    case SIG_STATUS_TRAINING_FAILED:
+    case SIG_STATUS_TRAINING_SUCCEEDED:
+    case SIG_STATUS_CARRIER_UP:
+        /* Ignore these */
+        break;
+    case SIG_STATUS_CARRIER_DOWN:
+    case SIG_STATUS_END_OF_DATA:
+        /* Finalise the image */
+        if (!s->end_of_data)
+        {
+            if (t42_itulab_jpeg_to_srgb(s))
+                span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert from ITULAB.\n");
+            s->end_of_data = 1;
+        }
+        break;
+    default:
+        span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected rx status - %d!\n", status);
+        break;
+    }
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -1300,11 +1219,22 @@ SPAN_DECLARE(int) t42_decode_put(t42_decode_state_t *s, const uint8_t data[], si
 {
     uint8_t *buf;
 
+    if (len == 0)
+    {
+        if (!s->end_of_data)
+        {
+            if (t42_itulab_jpeg_to_srgb(s))
+                span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert from ITULAB.\n");
+            s->end_of_data = 1;
+        }
+        return T4_DECODE_OK;
+    }
+
     if (s->compressed_image_size + len > s->buf_size)
     {
-        if ((buf = (uint8_t *) realloc(s->compressed_buf, s->compressed_image_size + 1000)) == NULL)
+        if ((buf = (uint8_t *) realloc(s->compressed_buf, s->compressed_image_size + len + 10000)) == NULL)
             return -1;
-        s->buf_size = s->compressed_image_size + 1000;
+        s->buf_size = s->compressed_image_size + len + 10000;
         s->compressed_buf = buf;
     }
     memcpy(&s->compressed_buf[s->compressed_image_size], data, len);
@@ -1345,13 +1275,13 @@ SPAN_DECLARE(int) t42_decode_set_image_size_constraints(t42_decode_state_t *s,
 
 SPAN_DECLARE(uint32_t) t42_decode_get_image_width(t42_decode_state_t *s)
 {
-    return 0;
+    return s->image_width;
 }
 /*- End of function --------------------------------------------------------*/
 
 SPAN_DECLARE(uint32_t) t42_decode_get_image_length(t42_decode_state_t *s)
 {
-    return 0;
+    return s->image_length;
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -1369,7 +1299,26 @@ SPAN_DECLARE(logging_state_t *) t42_decode_get_logging_state(t42_decode_state_t
 
 SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s)
 {
+    if (s->itu_ycc)
+    {
+        /* ITU-YCC */
+        /* Illuminant D65 */
+        set_lab_illuminant(&s->lab, 95.047f, 100.000f, 108.883f);
+        set_lab_gamut(&s->lab, 0, 100, -127, 127, -127, 127, FALSE);
+    }
+    else
+    {
+        /* ITULAB */
+        /* Illuminant D50 */
+        set_lab_illuminant(&s->lab, 96.422f, 100.000f,  82.521f);
+        set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE);
+    }
+
+    s->end_of_data = 0;
     s->compressed_image_size = 0;
+
+    s->error_message[0] = '\0';
+
     return 0;
 }
 /*- End of function --------------------------------------------------------*/
@@ -1401,6 +1350,22 @@ SPAN_DECLARE(t42_decode_state_t *) t42_decode_init(t42_decode_state_t *s,
 
 SPAN_DECLARE(int) t42_decode_release(t42_decode_state_t *s)
 {
+    if (s->scan_line_in)
+    {
+        free(s->scan_line_in);
+        s->scan_line_in = NULL;
+    }
+    if (s->scan_line_out)
+    {
+        free(s->scan_line_out);
+        s->scan_line_out = NULL;
+    }
+    jpeg_destroy_decompress(&s->decompressor);
+    if (s->in)
+    {
+        fclose(s->in);
+        s->in = NULL;
+    }
     if (s->comment)
     {
         free(s->comment);
diff --git a/libs/spandsp/src/t42_t43_local.h b/libs/spandsp/src/t42_t43_local.h
index 6c0fedef9c..45230e57d3 100644
--- a/libs/spandsp/src/t42_t43_local.h
+++ b/libs/spandsp/src/t42_t43_local.h
@@ -33,7 +33,7 @@ extern "C"
 {
 #endif
 
-void set_illuminant_from_code(logging_state_t *logging, lab_params_t *s, const uint8_t code[4]);
+int set_illuminant_from_code(logging_state_t *logging, lab_params_t *s, const uint8_t code[4]);
 
 void set_gamut_from_code(logging_state_t *logging, lab_params_t *s, const uint8_t code[12]);
 
diff --git a/libs/spandsp/src/t43.c b/libs/spandsp/src/t43.c
index 2b123a8619..d81e8da25c 100644
--- a/libs/spandsp/src/t43.c
+++ b/libs/spandsp/src/t43.c
@@ -61,6 +61,7 @@
 #include "spandsp/private/t42.h"
 #include "spandsp/private/t43.h"
 
+#include "t43_gray_code_tables.h"
 #include "t42_t43_local.h"
 
 #if !defined(FALSE)
@@ -74,19 +75,19 @@ SPAN_DECLARE(const char *) t43_image_type_to_str(int type)
 {
     switch (type)
     {
-    case 0:
+    case T43_IMAGE_TYPE_RGB_BILEVEL:
         return "1 bit/colour image (RGB primaries)";
-    case 1:
+    case T43_IMAGE_TYPE_CMY_BILEVEL:
         return "1 bit/colour image (CMY primaries)";
-    case 2:
+    case T43_IMAGE_TYPE_CMYK_BILEVEL:
         return "1 bit/colour image (CMYK primaries)";
-    case 16:
+    case T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE:
         return "Palettized colour image (CIELAB 8 bits/component precision table)";
-    case 17:
+    case T43_IMAGE_TYPE_12BIT_COLOUR_PALETTE:
         return "Palettized colour image (CIELAB 12 bits/component precision table)";
-    case 32:
+    case T43_IMAGE_TYPE_GRAY:
         return "Gray-scale image (using L*)";
-    case 48:
+    case T43_IMAGE_TYPE_COLOUR:
         return "Continuous-tone colour image (CIELAB)";
     }
     return "???";
@@ -129,9 +130,11 @@ static __inline__ int unpack_32(uint8_t *s, uint32_t value)
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(int) t43_create_header(t43_decode_state_t *s, uint8_t data[], size_t len)
+static int t43_create_header(t43_encode_state_t *s, uint8_t data[], size_t len)
 {
     int pos;
+    int val[6];
+    int bytes_per_entry;
 
     pos = 0;
     unpack_16(data, 0xFFA8);
@@ -151,7 +154,7 @@ SPAN_DECLARE(int) t43_create_header(t43_decode_state_t *s, uint8_t data[], size_
     /* JBIG coding method (0) is the only possible value here */
     data[pos] = 0;
     pos += 1;
-    data[pos] = 42; //image_type;
+    data[pos] = s->image_type;
     pos += 1;
     data[pos] = s->bit_planes[0];
     pos += 1;
@@ -181,38 +184,47 @@ SPAN_DECLARE(int) t43_create_header(t43_decode_state_t *s, uint8_t data[], size_
         pos += 2;
         memcpy(&data[pos], "G3FAX\1", 6);
         pos += 6;
-        unpack_16(&data[pos + 0], s->lab.offset_L);
-        unpack_16(&data[pos + 2], s->lab.range_L);
-        unpack_16(&data[pos + 4], s->lab.offset_a);
-        unpack_16(&data[pos + 6], s->lab.range_a);
-        unpack_16(&data[pos + 8], s->lab.offset_b);
-        unpack_16(&data[pos + 10], s->lab.range_b);
+        get_lab_gamut2(&s->lab, &val[0], &val[1], &val[2], &val[3], &val[4], &val[5]);
+        unpack_16(&data[pos + 0], val[0]);
+        unpack_16(&data[pos + 2], val[1]);
+        unpack_16(&data[pos + 4], val[2]);
+        unpack_16(&data[pos + 6], val[3]);
+        unpack_16(&data[pos + 8], val[4]);
+        unpack_16(&data[pos + 10], val[5]);
         pos += 12;
     }
 
-    if (s->lab.x_n != 0.9638f  ||  s->lab.y_n != 1.0f  ||  s->lab.z_n != 0.8245f)
+    if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0
+        ||
+        s->illuminant_colour_temperature > 0)
     {
         span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX2\n");
         unpack_16(&data[pos], 0xFFE1);
         pos += 2;
-        unpack_16(&data[pos], 2 + 6 + 6);
+        unpack_16(&data[pos], 2 + 6 + 4);
         pos += 2;
         memcpy(&data[pos], "G3FAX\2", 6);
         pos += 6;
-        unpack_16(&data[pos], 0);
-        unpack_16(&data[pos + 2], 0);
-        unpack_16(&data[pos + 4], 0);
-        set_illuminant_from_code(&s->logging, &s->lab, &data[pos]);
-        pos += 6;
+        if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0)
+        {
+            memcpy(&data[pos], s->illuminant_code, 4);
+        }
+        else
+        {
+            memcpy(&data[pos], "CT", 2);
+            unpack_16(&data[pos + 2], s->illuminant_colour_temperature);
+        }
+        pos += 4;
     }
 
 #if 0
     if (s->colour_map)
     {
         span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX3\n");
+        bytes_per_entry = (table_id == 0)  ?  1  :  2;
         unpack_16(&data[pos], 0xFFE3);
         pos += 2;
-        unpack_32(&data[pos], ???);
+        unpack_32(&data[pos], 2 + 6 + 2 + 4 + 3*s->colour_map_entries*bytes_per_entry);
         pos += 4;
         memcpy(&data[pos], "G3FAX\3", 6);
         pos += 6;
@@ -221,7 +233,7 @@ SPAN_DECLARE(int) t43_create_header(t43_decode_state_t *s, uint8_t data[], size_
         unpack_32(&data[pos], s->colour_map_entries);
         pos += 4;
         srgb_to_lab(&s->lab, &data[pos], s->colour_map, s->colour_map_entries);
-        pos += 3*s->colour_map_entries;
+        pos += 3*s->colour_map_entries*bytes_per_entry;
     }
 #endif
 
@@ -257,6 +269,12 @@ SPAN_DECLARE(int) t43_encode_set_image_length(t43_encode_state_t *s, uint32_t im
 }
 /*- End of function --------------------------------------------------------*/
 
+SPAN_DECLARE(int) t43_encode_set_image_type(t43_encode_state_t *s, int image_type)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
 SPAN_DECLARE(void) t43_encode_abort(t43_encode_state_t *s)
 {
 }
@@ -343,6 +361,9 @@ SPAN_DECLARE(t43_encode_state_t *) t43_encode_init(t43_encode_state_t *s,
                     image_length,
                     handler,
                     user_data);
+
+    s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE;
+
     return s;
 }
 /*- End of function --------------------------------------------------------*/
@@ -390,9 +411,11 @@ SPAN_DECLARE(void) t43_decode_rx_status(t43_decode_state_t *s, int status)
 
 static void set_simple_colour_map(t43_decode_state_t *s, int code)
 {
+    int i;
+
     switch (code)
     {
-    case 0:
+    case T43_IMAGE_TYPE_RGB_BILEVEL:
         /* Table 3/T.43 1 bit/colour image (using RGB primaries) */
         memset(s->colour_map, 0, sizeof(s->colour_map));
         /* Black */
@@ -417,7 +440,7 @@ static void set_simple_colour_map(t43_decode_state_t *s, int code)
         s->colour_map[3*0xE0 + 2] = 0xF0;
         s->colour_map_entries = 256;
         break;
-    case 1:
+    case T43_IMAGE_TYPE_CMY_BILEVEL:
         /* Table 2/T.43 1 bit/colour image (using CMY primaries) */
         memset(s->colour_map, 0, sizeof(s->colour_map));
         /* White */
@@ -441,7 +464,7 @@ static void set_simple_colour_map(t43_decode_state_t *s, int code)
         /* Black */
         s->colour_map_entries = 256;
         break;
-    case 2:
+    case T43_IMAGE_TYPE_CMYK_BILEVEL:
         /* Table 1/T.43 1 bit/colour image (using CMYK primaries) */
         memset(s->colour_map, 0, sizeof(s->colour_map));
         /* White */
@@ -465,16 +488,26 @@ static void set_simple_colour_map(t43_decode_state_t *s, int code)
         /* Black */
         s->colour_map_entries = 256;
         break;
-    case 16:
+    case T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE:
         /* Palettized colour image (using CIELAB 8 bits/component precision table) */
+        for (i = 0;  i < 3*256;  i += 3)
+        {
+            s->colour_map[i + 0] = i;
+            s->colour_map[i + 1] = i;
+            s->colour_map[i + 2] = i;
+        }
+        s->colour_map_entries = 256;
         break;
-    case 17:
+    case T43_IMAGE_TYPE_12BIT_COLOUR_PALETTE:
         /* Palettized colour image (using CIELAB 12 bits/component precision table) */
         break;
-    case 32:
+    case T43_IMAGE_TYPE_GRAY:
         /* Gray-scale image (using L*) */
+        for (i = 0;  i < 256;  i++)
+            s->colour_map[i] = i;
+        s->colour_map_entries = 256;
         break;
-    case 48:
+    case T43_IMAGE_TYPE_COLOUR:
         /* Continuous-tone colour image (using CIELAB) */
         break;
     }
@@ -490,9 +523,6 @@ static int t43_analyse_header(t43_decode_state_t *s, const uint8_t data[], size_
     uint8_t col[3];
     int i;
 
-    /* Set defaults */
-    set_lab_illuminant(&s->lab, 0.9638f, 1.0f, 0.8245f);
-    set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE);
     pos = 0;
     if (pack_16(&data[pos]) != 0xFFA8)
         return 0;
@@ -520,48 +550,68 @@ static int t43_analyse_header(t43_decode_state_t *s, const uint8_t data[], size_
                 {
                 case 0:
                     span_log(&s->logging, SPAN_LOG_FLOW, "Got G3FAX0\n");
-                    if (seg >= 6 + 10)
+                    if (seg < 6 + 10)
+                    {
+                        span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX0 length - %d\n", seg);
+                    }
+                    else
                     {
                         val[0] = pack_16(&data[pos + 6 + 0]);
                         s->spatial_resolution = pack_16(&data[pos + 6 + 2]);
                         val[2] = data[pos + 6 + 4];
-                        val[3] = data[pos + 6 + 5];
+                        s->image_type = data[pos + 6 + 5];
                         s->bit_planes[0] = data[pos + 6 + 6];
                         s->bit_planes[1] = data[pos + 6 + 7];
                         s->bit_planes[2] = data[pos + 6 + 8];
                         s->bit_planes[3] = data[pos + 6 + 9];
+                        if (s->image_type == T43_IMAGE_TYPE_GRAY)
+                        {
+                            s->samples_per_pixel = 1;
+                        }
+                        else if (s->image_type == T43_IMAGE_TYPE_CMYK_BILEVEL)
+                        {
+                            s->samples_per_pixel = 4;
+                        }
+                        else
+                        {
+                            s->samples_per_pixel = 3;
+                        }
                         span_log(&s->logging,
                                  SPAN_LOG_FLOW,
-                                 "Version %d, resolution %.2fdpi, coding method %d, type %s (%d), bit planes %d,%d,%d,%d\n",
+                                 "Version %d, resolution %ddpi, coding method %d, type %s (%d), bit planes %d,%d,%d,%d\n",
                                  val[0],
-                                 s->spatial_resolution/100.0f,
+                                 s->spatial_resolution,
                                  val[2],
-                                 t43_image_type_to_str(val[3]),
-                                 val[3],
+                                 t43_image_type_to_str(s->image_type),
+                                 s->image_type,
                                  s->bit_planes[0],
                                  s->bit_planes[1],
                                  s->bit_planes[2],
                                  s->bit_planes[3]);
-                        set_simple_colour_map(s, val[3]);
-                    }
-                    else
-                    {
-                        span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX0 length - %d\n", seg);
+                        set_simple_colour_map(s, s->image_type);
                     }
                     break;
                 case 1:
                     span_log(&s->logging, SPAN_LOG_FLOW, "Set gamut\n");
-                    if (seg >= 6 + 12)
-                        set_gamut_from_code(&s->logging, &s->lab, &data[pos + 6]);
-                    else
+                    if (seg < 6 + 12)
+                    {
                         span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX1 length - %d\n", seg);
+                    }
+                    else
+                    {
+                        set_gamut_from_code(&s->logging, &s->lab, &data[pos + 6]);
+                    }
                     break;
                 case 2:
                     span_log(&s->logging, SPAN_LOG_FLOW, "Set illuminant\n");
-                    if (seg >= 6 + 4)
-                        set_illuminant_from_code(&s->logging, &s->lab, &data[pos + 6]);
-                    else
+                    if (seg < 6 + 4)
+                    {
                         span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX2 length - %d\n", seg);
+                    }
+                    else
+                    {
+                        s->illuminant_colour_temperature = set_illuminant_from_code(&s->logging, &s->lab, &data[pos + 6]);
+                    }
                     break;
                 default:
                     span_log(&s->logging, SPAN_LOG_FLOW, "Got unexpected G3FAX%d length - %d\n", data[pos + 5], seg);
@@ -588,7 +638,7 @@ static int t43_analyse_header(t43_decode_state_t *s, const uint8_t data[], size_
                     case 0:
                         /* 8 bit CIELAB */
                         s->colour_map_entries = pack_32(&data[pos + 8]);
-                        span_log(&s->logging, SPAN_LOG_FLOW, "  Entries %6d\n", s->colour_map_entries);
+                        span_log(&s->logging, SPAN_LOG_FLOW, "  Entries %6d (len %d)\n", s->colour_map_entries, seg);
                         if (seg >= 12 + s->colour_map_entries*3)
                         {
                             lab_to_srgb(&s->lab, s->colour_map, &data[pos + 12], s->colour_map_entries);
@@ -650,7 +700,7 @@ static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t le
 
     if (s->buf == NULL)
     {
-        image_size = 3*s->t85.xd*s->t85.yd;
+        image_size = s->samples_per_pixel*s->t85.xd*s->t85.yd;
         if ((s->buf = malloc(image_size)) == NULL)
             return -1;
         memset(s->buf, 0, image_size);
@@ -659,13 +709,25 @@ static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t le
     for (i = 0;  i < len;  i++)
     {
         mask = 0x80;
-        for (j = 0;  j < 24;  j += 3)
+        if (s->samples_per_pixel == 1)
         {
-            if ((buf[i] & mask))
-                s->buf[s->ptr + j] |= s->bit_plane_mask;
-            mask >>= 1;
+            for (j = 0;  j < 8;  j += s->samples_per_pixel)
+            {
+                if ((buf[i] & mask))
+                    s->buf[s->ptr + j] |= s->bit_plane_mask;
+                mask >>= 1;
+            }
         }
-        s->ptr += 3*8;
+        else
+        {
+            for (j = 0;  j < s->samples_per_pixel*8;  j += s->samples_per_pixel)
+            {
+                if ((buf[i] & mask))
+                    s->buf[s->ptr + j] |= s->bit_plane_mask;
+                mask >>= 1;
+            }
+        }
+        s->ptr += s->samples_per_pixel*8;
     }
     s->row++;
     return 0;
@@ -724,17 +786,23 @@ SPAN_DECLARE(int) t43_decode_put(t43_decode_state_t *s, const uint8_t data[], si
         t85_decode_new_plane(&s->t85);
     }
     /* Apply the colour map, and produce the RGB data from the collected bit-planes */
-    for (j = 0;  j < total_len;  j += 3)
+    if (s->samples_per_pixel == 1)
     {
-        i = s->buf[j];
-        s->buf[j] = s->colour_map[3*i];
-        s->buf[j + 1] = s->colour_map[3*i + 1];
-        s->buf[j + 2] = s->colour_map[3*i + 2];
+        for (j = 0;  j < total_len;  j += s->samples_per_pixel)
+            s->buf[j] = s->colour_map[s->buf[j]];
+    }
+    else
+    {
+        for (j = 0;  j < total_len;  j += s->samples_per_pixel)
+        {
+            i = s->buf[j];
+            s->buf[j] = s->colour_map[3*i];
+            s->buf[j + 1] = s->colour_map[3*i + 1];
+            s->buf[j + 2] = s->colour_map[3*i + 2];
+        }
     }
     for (j = 0;  j < s->t85.yd;  j++)
-    {
-        s->row_write_handler(s->row_write_user_data, &s->buf[j*3*s->t85.xd], 3*s->t85.xd);
-    }
+        s->row_write_handler(s->row_write_user_data, &s->buf[j*s->samples_per_pixel*s->t85.xd], s->samples_per_pixel*s->t85.xd);
     return result;
 }
 /*- End of function --------------------------------------------------------*/
@@ -794,6 +862,17 @@ SPAN_DECLARE(logging_state_t *) t43_decode_get_logging_state(t43_decode_state_t
 
 SPAN_DECLARE(int) t43_decode_restart(t43_decode_state_t *s)
 {
+    /* ITULAB */
+    /* Illuminant D50 */
+    set_lab_illuminant(&s->lab, 96.422f, 100.000f,  82.521f);
+    set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE);
+
+    s->t85.min_bit_planes = 1;
+    s->t85.max_bit_planes = 8;
+    s->bit_plane_mask = 0x80;
+    s->current_bit_plane = -1;
+    s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE;
+
     return t85_decode_restart(&s->t85);
 }
 /*- End of function --------------------------------------------------------*/
@@ -816,10 +895,16 @@ SPAN_DECLARE(t43_decode_state_t *) t43_decode_init(t43_decode_state_t *s,
 
     t85_decode_init(&s->t85, t85_row_write_handler, s);
 
+    /* ITULAB */
+    /* Illuminant D50 */
+    set_lab_illuminant(&s->lab, 96.422f, 100.000f,  82.521f);
+    set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE);
+
     s->t85.min_bit_planes = 1;
     s->t85.max_bit_planes = 8;
     s->bit_plane_mask = 0x80;
     s->current_bit_plane = -1;
+    s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE;
 
     return s;
 }
diff --git a/libs/spandsp/src/t4_rx.c b/libs/spandsp/src/t4_rx.c
index 765f427cea..7723e14eb2 100644
--- a/libs/spandsp/src/t4_rx.c
+++ b/libs/spandsp/src/t4_rx.c
@@ -110,9 +110,9 @@ SPAN_DECLARE(const char *) t4_compression_to_str(int compression)
     case T4_COMPRESSION_T88:
         return "T.88";
     case T4_COMPRESSION_T42_T81:
-        return "T.81";
+        return "T.81+T.42";
     case T4_COMPRESSION_SYCC_T81:
-        return "sYCC T.81";
+        return "T.81+sYCC";
     case T4_COMPRESSION_T43:
         return "T.43";
     case T4_COMPRESSION_T45:
@@ -130,14 +130,20 @@ SPAN_DECLARE(const char *) t4_image_type_to_str(int type)
         return "bi-level";
     case T4_IMAGE_TYPE_COLOUR_BILEVEL:
         return "bi-level colour";
+    case T4_IMAGE_TYPE_4COLOUR_BILEVEL:
+        return "CMYK bi-level colour";
     case T4_IMAGE_TYPE_GRAY_8BIT:
         return "8-bit gray scale";
     case T4_IMAGE_TYPE_GRAY_12BIT:
         return "12-bit gray scale";
     case T4_IMAGE_TYPE_COLOUR_8BIT:
         return "8-bit colour";
+    case T4_IMAGE_TYPE_4COLOUR_8BIT:
+        return "CMYK 8-bit colour";
     case T4_IMAGE_TYPE_COLOUR_12BIT:
         return "12-bit colour";
+    case T4_IMAGE_TYPE_4COLOUR_12BIT:
+        return "CMYK 12-bit colour";
     }
     return "???";
 }
@@ -751,9 +757,9 @@ static void select_tiff_compression(t4_rx_state_t *s, int output_image_type)
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
+SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression)
 {
-    switch (encoding)
+    switch (compression)
     {
     case T4_COMPRESSION_T4_1D:
     case T4_COMPRESSION_T4_2D:
@@ -765,12 +771,12 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
         case T4_COMPRESSION_T6:
             break;
         default:
-            t4_t6_decode_init(&s->decoder.t4_t6, encoding, s->metadata.image_width, s->row_handler, s->row_handler_user_data);
+            t4_t6_decode_init(&s->decoder.t4_t6, compression, s->metadata.image_width, s->row_handler, s->row_handler_user_data);
             break;
         }
-        s->metadata.compression = encoding;
+        s->metadata.compression = compression;
         select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL);
-        return t4_t6_decode_set_encoding(&s->decoder.t4_t6, encoding);
+        return t4_t6_decode_set_encoding(&s->decoder.t4_t6, compression);
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
         switch (s->metadata.compression)
@@ -787,7 +793,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
             break;
         }
         select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL);
-        s->metadata.compression = encoding;
+        s->metadata.compression = compression;
         return 0;
 #if defined(SPANDSP_SUPPORT_T88)
     case T4_COMPRESSION_T88:
@@ -799,7 +805,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
             break;
         }
         select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL);
-        s->metadata.compression = encoding;
+        s->metadata.compression = compression;
         return 0;
 #endif
     case T4_COMPRESSION_T42_T81:
@@ -817,7 +823,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
             t42_decode_set_image_size_constraints(&s->decoder.t42, T4_WIDTH_1200_A3, 0);
             break;
         }
-        s->metadata.compression = encoding;
+        s->metadata.compression = compression;
         select_tiff_compression(s, T4_IMAGE_TYPE_COLOUR_8BIT);
         return 0;
 #if defined(SPANDSP_SUPPORT_T43)
@@ -834,7 +840,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
             t43_decode_set_image_size_constraints(&s->decoder.t43, T4_WIDTH_1200_A3, 0);
             break;
         }
-        s->metadata.compression = encoding;
+        s->metadata.compression = compression;
         select_tiff_compression(s, T4_IMAGE_TYPE_COLOUR_8BIT);
         return 0;
 #endif
@@ -847,7 +853,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
         default:
             break;
         }
-        s->metadata.compression = encoding;
+        s->metadata.compression = compression;
         select_tiff_compression(s, T4_IMAGE_TYPE_COLOUR_8BIT);
         return 0;
 #endif
diff --git a/libs/spandsp/src/t4_t6_encode.c b/libs/spandsp/src/t4_t6_encode.c
index 00d3ce1d93..ddb8b33a06 100644
--- a/libs/spandsp/src/t4_t6_encode.c
+++ b/libs/spandsp/src/t4_t6_encode.c
@@ -57,8 +57,8 @@
 #include "config.h"
 #endif
 
-#include <stdlib.h>
 #include <inttypes.h>
+#include <stdlib.h>
 #include <limits.h>
 #include <stdio.h>
 #include <fcntl.h>
@@ -983,13 +983,15 @@ SPAN_DECLARE(int) t4_t6_encode_set_encoding(t4_t6_encode_state_t *s, int encodin
     switch (encoding)
     {
     case T4_COMPRESSION_T6:
+        s->min_bits_per_row = 0;
+        /* Fall through */
     case T4_COMPRESSION_T4_2D:
     case T4_COMPRESSION_T4_1D:
         s->encoding = encoding;
         /* Set this to the default value for the lowest resolution in the T.4 spec. */
         s->max_rows_to_next_1d_row = 2;
         s->rows_to_next_1d_row = s->max_rows_to_next_1d_row - 1;
-        s->row_is_2d = FALSE;
+        s->row_is_2d = (s->encoding == T4_COMPRESSION_T6);
         return 0;
     }
     return -1;
@@ -998,7 +1000,16 @@ SPAN_DECLARE(int) t4_t6_encode_set_encoding(t4_t6_encode_state_t *s, int encodin
 
 SPAN_DECLARE(void) t4_t6_encode_set_min_bits_per_row(t4_t6_encode_state_t *s, int bits)
 {
-    s->min_bits_per_row = bits;
+    switch (s->encoding)
+    {
+    case T4_COMPRESSION_T6:
+        s->min_bits_per_row = 0;
+        break;
+    case T4_COMPRESSION_T4_2D:
+    case T4_COMPRESSION_T4_1D:
+        s->min_bits_per_row = bits;
+        break;
+    }
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -1056,9 +1067,12 @@ SPAN_DECLARE(void) t4_t6_encode_set_max_2d_rows_per_1d_row(t4_t6_encode_state_t
     } y_res_table[] =
     {
         {T4_Y_RESOLUTION_STANDARD, 2},
+        {T4_Y_RESOLUTION_100, 2},
         {T4_Y_RESOLUTION_FINE, 4},
+        {T4_Y_RESOLUTION_200, 4},
         {T4_Y_RESOLUTION_300, 6},
         {T4_Y_RESOLUTION_SUPERFINE, 8},
+        {T4_Y_RESOLUTION_400, 8},
         {T4_Y_RESOLUTION_600, 12},
         {T4_Y_RESOLUTION_800, 16},
         {T4_Y_RESOLUTION_1200, 24},
@@ -1094,7 +1108,7 @@ SPAN_DECLARE(logging_state_t *) t4_t6_encode_get_logging_state(t4_t6_encode_stat
 }
 /*- End of function --------------------------------------------------------*/
 
-SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width)
+SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width, int image_length)
 {
     /* Allow for pages being of different width. */
     t4_t6_encode_set_image_width(s, image_width);
@@ -1125,6 +1139,7 @@ SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width)
 SPAN_DECLARE(t4_t6_encode_state_t *) t4_t6_encode_init(t4_t6_encode_state_t *s,
                                                        int encoding,
                                                        int image_width,
+                                                       int image_length,
                                                        t4_row_read_handler_t handler,
                                                        void *user_data)
 {
@@ -1142,7 +1157,7 @@ SPAN_DECLARE(t4_t6_encode_state_t *) t4_t6_encode_init(t4_t6_encode_state_t *s,
     s->row_read_user_data = user_data;
 
     s->max_rows_to_next_1d_row = 2;
-    t4_t6_encode_restart(s, image_width);
+    t4_t6_encode_restart(s, image_width, image_length);
 
     return s;
 }
diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c
index 02fa125dc6..90b20aeacc 100644
--- a/libs/spandsp/src/t4_tx.c
+++ b/libs/spandsp/src/t4_tx.c
@@ -29,8 +29,8 @@
 #include "config.h"
 #endif
 
-#include <stdlib.h>
 #include <inttypes.h>
+#include <stdlib.h>
 #include <limits.h>
 #include <stdio.h>
 #include <fcntl.h>
@@ -91,6 +91,7 @@ typedef struct
     uint8_t *buf;
     int ptr;
     int row;
+    int size;
     int bit_mask;
 } packer_t;
 
@@ -100,7 +101,7 @@ typedef struct
     int code;
 } res_table_t;
 
-static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length);
+static void t4_tx_set_image_length(t4_tx_state_t *s, uint32_t image_length);
 
 static const res_table_t x_res_table[] =
 {
@@ -1102,7 +1103,7 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding)
             case T4_COMPRESSION_T6:
                 break;
             default:
-                t4_t6_encode_init(&s->encoder.t4_t6, encoding, s->metadata.image_width, s->row_handler, s->row_handler_user_data);
+                t4_t6_encode_init(&s->encoder.t4_t6, encoding, s->metadata.image_width, s->metadata.image_length, s->row_handler, s->row_handler_user_data);
                 t4_t6_encode_set_max_2d_rows_per_1d_row(&s->encoder.t4_t6, -s->metadata.y_resolution);
                 break;
             }
@@ -1227,7 +1228,7 @@ SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width)
 }
 /*- End of function --------------------------------------------------------*/
 
-static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length)
+static void t4_tx_set_image_length(t4_tx_state_t *s, uint32_t image_length)
 {
     s->metadata.image_length = image_length;
     switch (s->metadata.compression)
@@ -1495,7 +1496,7 @@ SPAN_DECLARE(int) t4_tx_start_page(t4_tx_state_t *s)
     case T4_COMPRESSION_T4_1D:
     case T4_COMPRESSION_T4_2D:
     case T4_COMPRESSION_T6:
-        t4_t6_encode_restart(&s->encoder.t4_t6, s->metadata.image_width);
+        t4_t6_encode_restart(&s->encoder.t4_t6, s->metadata.image_width, s->metadata.image_length);
         break;
     case T4_COMPRESSION_T85:
     case T4_COMPRESSION_T85_L0:
diff --git a/libs/spandsp/tests/fax_tests.c b/libs/spandsp/tests/fax_tests.c
index 5af1675361..49958571a9 100644
--- a/libs/spandsp/tests/fax_tests.c
+++ b/libs/spandsp/tests/fax_tests.c
@@ -845,6 +845,7 @@ int main(int argc, char *argv[])
                                     | T4_SUPPORT_LENGTH_US_LETTER
                                     | T4_SUPPORT_LENGTH_US_LEGAL
                                     | T4_SUPPORT_LENGTH_UNLIMITED);
+#if 0
         t30_set_supported_bilevel_resolutions(t30_state[i],
                                               T4_SUPPORT_RESOLUTION_R8_STANDARD
                                             | T4_SUPPORT_RESOLUTION_R8_FINE
@@ -860,7 +861,36 @@ int main(int argc, char *argv[])
                                             | T4_SUPPORT_RESOLUTION_600_600
                                             | T4_SUPPORT_RESOLUTION_600_1200
                                             | T4_SUPPORT_RESOLUTION_1200_1200);
+#elif 0
+        t30_set_supported_bilevel_resolutions(t30_state[i],
+                                              T4_SUPPORT_RESOLUTION_R8_STANDARD
+                                            | T4_SUPPORT_RESOLUTION_R8_FINE
+                                            | T4_SUPPORT_RESOLUTION_R8_SUPERFINE
+                                            | T4_SUPPORT_RESOLUTION_R16_SUPERFINE);
+#else
+        t30_set_supported_bilevel_resolutions(t30_state[i],
+                                              T4_SUPPORT_RESOLUTION_200_100
+                                            | T4_SUPPORT_RESOLUTION_200_200
+                                            | T4_SUPPORT_RESOLUTION_200_400
+                                            | T4_SUPPORT_RESOLUTION_300_300
+                                            | T4_SUPPORT_RESOLUTION_300_600
+                                            | T4_SUPPORT_RESOLUTION_400_400
+                                            | T4_SUPPORT_RESOLUTION_400_800
+                                            | T4_SUPPORT_RESOLUTION_600_600
+                                            | T4_SUPPORT_RESOLUTION_600_1200
+                                            | T4_SUPPORT_RESOLUTION_1200_1200);
+#endif
+#if 1
         t30_set_supported_colour_resolutions(t30_state[i], 0);
+#else
+        t30_set_supported_colour_resolutions(t30_state[i],
+                                             T4_SUPPORT_RESOLUTION_100_100
+                                           | T4_SUPPORT_RESOLUTION_200_200
+                                           | T4_SUPPORT_RESOLUTION_300_300
+                                           | T4_SUPPORT_RESOLUTION_400_400
+                                           | T4_SUPPORT_RESOLUTION_600_600
+                                           | T4_SUPPORT_RESOLUTION_1200_1200);
+#endif
         t30_set_supported_output_compressions(t30_state[i], T4_SUPPORT_COMPRESSION_T4_2D);
         t30_set_ecm_capability(t30_state[i], use_ecm);
         t30_set_supported_compressions(t30_state[i],
diff --git a/libs/spandsp/tests/t4_t6_tests.c b/libs/spandsp/tests/t4_t6_tests.c
index bb67643422..c735557fb7 100644
--- a/libs/spandsp/tests/t4_t6_tests.c
+++ b/libs/spandsp/tests/t4_t6_tests.c
@@ -331,7 +331,7 @@ int main(int argc, char *argv[])
 #if 1
     printf("Testing image_function->compress->decompress->image_function\n");
     /* Send end gets image from a function */
-    if ((send_state = t4_t6_encode_init(NULL, compression, 1728, row_read_handler, NULL)) == NULL)
+    if ((send_state = t4_t6_encode_init(NULL, compression, 1728, -1, row_read_handler, NULL)) == NULL)
     {
         printf("Failed to init T.4/T.6 encoder\n");
         exit(2);
@@ -364,7 +364,7 @@ int main(int argc, char *argv[])
         t4_t6_encode_set_encoding(send_state, compression);
         t4_t6_decode_set_encoding(receive_state, compression);
 
-        if (t4_t6_encode_restart(send_state, 1728))
+        if (t4_t6_encode_restart(send_state, 1728, -1))
             break;
         if (t4_t6_decode_restart(receive_state, 1728))
             break;