freeswitch/libs/spandsp/src/t4_rx.c

966 lines
32 KiB
C

/*
* SpanDSP - a series of DSP components for telephony
*
* t4_rx.c - ITU T.4 FAX image receive processing
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2003, 2007, 2010 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <memory.h>
#include <string.h>
#if defined(HAVE_TGMATH_H)
#include <tgmath.h>
#endif
#if defined(HAVE_MATH_H)
#include <math.h>
#endif
#include "floating_fudge.h"
#include <tiffio.h>
#include "spandsp/telephony.h"
#include "spandsp/logging.h"
#include "spandsp/bit_operations.h"
#include "spandsp/async.h"
#include "spandsp/timezone.h"
#include "spandsp/t4_rx.h"
#include "spandsp/t4_tx.h"
#include "spandsp/image_translate.h"
#include "spandsp/t81_t82_arith_coding.h"
#include "spandsp/t85.h"
#include "spandsp/t42.h"
#if defined(SPANDSP_SUPPORT_T43)
#include "spandsp/t43.h"
#endif
#include "spandsp/t4_t6_decode.h"
#include "spandsp/t4_t6_encode.h"
#include "spandsp/version.h"
#include "spandsp/private/logging.h"
#include "spandsp/private/t81_t82_arith_coding.h"
#include "spandsp/private/t85.h"
#include "spandsp/private/t42.h"
#if defined(SPANDSP_SUPPORT_T43)
#include "spandsp/private/t43.h"
#endif
#include "spandsp/private/t4_t6_decode.h"
#include "spandsp/private/t4_t6_encode.h"
#include "spandsp/private/image_translate.h"
#include "spandsp/private/t4_rx.h"
#include "spandsp/private/t4_tx.h"
/*! The number of centimetres in one inch */
#define CM_PER_INCH 2.54f
typedef struct
{
uint8_t *buf;
int ptr;
} packer_t;
#if defined(SPANDSP_SUPPORT_TIFF_FX)
extern TIFFFieldArray tiff_fx_field_array;
#endif
SPAN_DECLARE(const char *) t4_encoding_to_str(int encoding)
{
switch (encoding)
{
case T4_COMPRESSION_NONE:
return "None";
case T4_COMPRESSION_T4_1D:
return "T.4 1-D";
case T4_COMPRESSION_T4_2D:
return "T.4 2-D";
case T4_COMPRESSION_T6:
return "T.6";
case T4_COMPRESSION_T85:
return "T.85";
case T4_COMPRESSION_T85_L0:
return "T.85(L0)";
case T4_COMPRESSION_T42_T81:
return "T.81";
case T4_COMPRESSION_SYCC_T81:
return "sYCC T.81";
case T4_COMPRESSION_T43:
return "T.43";
case T4_COMPRESSION_T45:
return "T.45";
}
return "???";
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(const char *) t4_image_type_to_str(int type)
{
switch (type)
{
case T4_IMAGE_TYPE_BILEVEL:
return "bi-level";
case T4_IMAGE_TYPE_COLOUR_BILEVEL:
return "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_COLOUR_12BIT:
return "12-bit colour";
}
return "???";
}
/*- End of function --------------------------------------------------------*/
static int set_tiff_directory_info(t4_rx_state_t *s)
{
time_t now;
struct tm *tm;
char buf[256 + 1];
uint16_t resunit;
float x_resolution;
float y_resolution;
t4_rx_tiff_state_t *t;
int32_t output_compression;
int32_t output_t4_options;
int bits_per_sample;
int samples_per_pixel;
int photometric;
t = &s->tiff;
/* Prepare the directory entry fully before writing the image, or libtiff complains */
bits_per_sample = 1;
samples_per_pixel = 1;
photometric = PHOTOMETRIC_MINISWHITE;
switch (t->output_encoding)
{
case T4_COMPRESSION_T4_1D:
default:
output_compression = COMPRESSION_CCITT_T4;
output_t4_options = GROUP3OPT_FILLBITS;
break;
case T4_COMPRESSION_T4_2D:
output_compression = COMPRESSION_CCITT_T4;
output_t4_options = GROUP3OPT_FILLBITS | GROUP3OPT_2DENCODING;
break;
case T4_COMPRESSION_T6:
output_compression = COMPRESSION_CCITT_T6;
break;
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_T42_T81:
output_compression = COMPRESSION_JPEG;
bits_per_sample = 8;
samples_per_pixel = 3;
photometric = PHOTOMETRIC_ITULAB;
break;
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
output_compression = COMPRESSION_T43;
bits_per_sample = 8;
samples_per_pixel = 3;
photometric = PHOTOMETRIC_ITULAB;
break;
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
output_compression = COMPRESSION_T85;
break;
}
TIFFSetField(t->tiff_file, TIFFTAG_COMPRESSION, output_compression);
switch (output_compression)
{
case COMPRESSION_CCITT_T4:
TIFFSetField(t->tiff_file, TIFFTAG_T4OPTIONS, output_t4_options);
TIFFSetField(t->tiff_file, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
break;
case COMPRESSION_CCITT_T6:
TIFFSetField(t->tiff_file, TIFFTAG_T6OPTIONS, 0);
TIFFSetField(t->tiff_file, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
break;
case COMPRESSION_JPEG:
TIFFSetField(t->tiff_file, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
break;
#if defined(SPANDSP_SUPPORT_T43)
case COMPRESSION_T43:
TIFFSetField(t->tiff_file, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
break;
#endif
case COMPRESSION_T85:
TIFFSetField(t->tiff_file, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
break;
}
TIFFSetField(t->tiff_file, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(t->tiff_file, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
TIFFSetField(t->tiff_file, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel);
TIFFSetField(t->tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, photometric);
TIFFSetField(t->tiff_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
/* TIFFTAG_STRIPBYTECOUNTS and TIFFTAG_STRIPOFFSETS are added automatically */
x_resolution = s->metadata.x_resolution/100.0f;
y_resolution = s->metadata.y_resolution/100.0f;
/* Metric seems the sane thing to use in the 21st century, but a lot of lousy software
gets FAX resolutions wrong, and more get it wrong using metric than using inches. */
#if 0
TIFFSetField(t->tiff_file, TIFFTAG_XRESOLUTION, x_resolution);
TIFFSetField(t->tiff_file, TIFFTAG_YRESOLUTION, y_resolution);
resunit = RESUNIT_CENTIMETER;
TIFFSetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, resunit);
#else
TIFFSetField(t->tiff_file, TIFFTAG_XRESOLUTION, floorf(x_resolution*CM_PER_INCH + 0.5f));
TIFFSetField(t->tiff_file, TIFFTAG_YRESOLUTION, floorf(y_resolution*CM_PER_INCH + 0.5f));
resunit = RESUNIT_INCH;
TIFFSetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, resunit);
#endif
TIFFSetField(t->tiff_file, TIFFTAG_SOFTWARE, "Spandsp " SPANDSP_RELEASE_DATETIME_STRING);
if (gethostname(buf, sizeof(buf)) == 0)
TIFFSetField(t->tiff_file, TIFFTAG_HOSTCOMPUTER, buf);
#if defined(TIFFTAG_FAXDCS)
if (s->metadata.dcs)
TIFFSetField(t->tiff_file, TIFFTAG_FAXDCS, s->metadata.dcs);
#endif
if (s->metadata.sub_address)
TIFFSetField(t->tiff_file, TIFFTAG_FAXSUBADDRESS, s->metadata.sub_address);
if (s->metadata.far_ident)
TIFFSetField(t->tiff_file, TIFFTAG_IMAGEDESCRIPTION, s->metadata.far_ident);
if (s->metadata.vendor)
TIFFSetField(t->tiff_file, TIFFTAG_MAKE, s->metadata.vendor);
if (s->metadata.model)
TIFFSetField(t->tiff_file, TIFFTAG_MODEL, s->metadata.model);
time(&now);
tm = localtime(&now);
sprintf(buf,
"%4d/%02d/%02d %02d:%02d:%02d",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
TIFFSetField(t->tiff_file, TIFFTAG_DATETIME, buf);
TIFFSetField(t->tiff_file, TIFFTAG_FAXRECVTIME, now - s->tiff.page_start_time);
TIFFSetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, s->image_width);
/* Set the total pages to 1. For any one page document we will get this
right. For multi-page documents we will need to come back and fill in
the right answer when we know it. */
TIFFSetField(t->tiff_file, TIFFTAG_PAGENUMBER, s->current_page, 1);
/* TIFF page numbers start from zero, so the number of pages in the file
is always one greater than the highest page number in the file. */
s->tiff.pages_in_file = s->current_page + 1;
s->image_length = 0;
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
/* We only get bad row info from pages received in non-ECM mode. */
if (output_compression == COMPRESSION_CCITT_T4)
{
if (s->decoder.t4_t6.bad_rows)
{
TIFFSetField(t->tiff_file, TIFFTAG_BADFAXLINES, s->decoder.t4_t6.bad_rows);
TIFFSetField(t->tiff_file, TIFFTAG_CONSECUTIVEBADFAXLINES, s->decoder.t4_t6.longest_bad_row_run);
TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_REGENERATED);
}
else
{
TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_CLEAN);
}
}
/* Fall through */
case T4_COMPRESSION_T6:
s->image_length = t4_t6_decode_get_image_length(&s->decoder.t4_t6);
break;
case T4_COMPRESSION_T42_T81:
s->image_length = t42_decode_get_image_length(&s->decoder.t42);
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
s->image_length = t43_decode_get_image_length(&s->decoder.t43);
break;
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
s->image_length = t85_decode_get_image_length(&s->decoder.t85);
break;
}
TIFFSetField(t->tiff_file, TIFFTAG_IMAGELENGTH, s->image_length);
TIFFSetField(t->tiff_file, TIFFTAG_ROWSPERSTRIP, s->image_length);
#if defined(SPANDSP_SUPPORT_TIFF_FX)
TIFFSetField(t->tiff_file, TIFFTAG_PROFILETYPE, PROFILETYPE_G3_FAX);
TIFFSetField(t->tiff_file, TIFFTAG_FAXPROFILE, FAXPROFILE_S);
TIFFSetField(t->tiff_file, TIFFTAG_CODINGMETHODS, CODINGMETHODS_T4_1D | CODINGMETHODS_T4_2D | CODINGMETHODS_T6);
TIFFSetField(t->tiff_file, TIFFTAG_VERSIONYEAR, "1998");
if (s->current_page == 0)
{
/* Create a placeholder for the global parameters IFD, to be filled in later */
TIFFSetField(t->tiff_file, TIFFTAG_GLOBALPARAMETERSIFD, 0);
}
#endif
return 0;
}
/*- End of function --------------------------------------------------------*/
static int open_tiff_output_file(t4_rx_state_t *s, const char *file)
{
if ((s->tiff.tiff_file = TIFFOpen(file, "w")) == NULL)
return -1;
return 0;
}
/*- End of function --------------------------------------------------------*/
static int row_read_handler(void *user_data, uint8_t row[], size_t len)
{
packer_t *s;
s = (packer_t *) user_data;
memcpy(row, &s->buf[s->ptr], len);
s->ptr += len;
return len;
}
/*- End of function --------------------------------------------------------*/
static int write_tiff_image(t4_rx_state_t *s)
{
t4_rx_tiff_state_t *t;
uint8_t *buf;
uint8_t *buf2;
int buf_len;
int len;
int len2;
t85_encode_state_t t85;
#if defined(SPANDSP_SUPPORT_T43)
t43_encode_state_t t43;
#endif
packer_t packer;
#if defined(SPANDSP_SUPPORT_TIFF_FX)
uint64_t offset;
#endif
t = &s->tiff;
if (t->image_buffer == NULL || t->image_size <= 0)
return -1;
/* Set up the TIFF directory info... */
set_tiff_directory_info(s);
/* ...Put the directory in the file before the image data, to get them in the order specified
for TIFF/F files... */
if (!TIFFCheckpointDirectory(t->tiff_file))
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to checkpoint directory for page %d.\n", t->file, s->current_page);
/* ...and write out the image... */
switch (t->output_encoding)
{
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
span_log(&s->logging, SPAN_LOG_WARNING, "%s: TODO need T.85 compression.\n", t->file);
buf_len = 0;
buf = NULL;
packer.buf = t->image_buffer;
packer.ptr = 0;
t85_encode_init(&t85, s->image_width, s->image_length, row_read_handler, &packer);
//if (t->output_encoding == T4_COMPRESSION_T85_L0)
// t85_encode_set_options(&t85, 256, -1, -1);
len2 = 0;
do
{
if (buf_len < len2 + 50000)
{
buf_len += 50000;
if ((buf2 = realloc(buf, buf_len)) == NULL)
{
if (buf)
free(buf);
return -1;
}
buf = buf2;
}
len = t85_encode_get(&t85, &buf[len2], 50000);
len2 += len;
}
while (len > 0);
if (TIFFWriteRawStrip(t->tiff_file, 0, buf, len2) < 0)
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file);
t85_encode_release(&t85);
free(buf);
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
span_log(&s->logging, SPAN_LOG_WARNING, "%s: TODO need T.43 compression.\n", t->file);
buf_len = 0;
buf = NULL;
packer.buf = t->image_buffer;
packer.ptr = 0;
t43_encode_init(&t43, s->image_width, s->image_length, row_read_handler, &packer);
len2 = 0;
do
{
if (buf_len < len2 + 50000)
{
buf_len += 50000;
if ((buf2 = realloc(buf, buf_len)) == NULL)
{
if (buf)
free(buf);
return -1;
}
buf = buf2;
}
len = t43_encode_get(&t43, &buf[len2], 50000);
len2 += len;
}
while (len > 0);
if (TIFFWriteRawStrip(t->tiff_file, 0, buf, len2) < 0)
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file);
t43_encode_release(&t43);
free(buf);
break;
#endif
default:
/* Let libtiff do the compression */
if (TIFFWriteEncodedStrip(t->tiff_file, 0, t->image_buffer, t->image_size) < 0)
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file);
break;
}
/* ...then finalise the directory entry, and libtiff is happy. */
if (!TIFFWriteDirectory(t->tiff_file))
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to write directory for page %d.\n", t->file, s->current_page);
#if defined(SPANDSP_SUPPORT_TIFF_FX)
if (s->current_page == 0)
{
if (!TIFFCreateCustomDirectory(t->tiff_file, &tiff_fx_field_array))
{
TIFFSetField(t->tiff_file, TIFFTAG_FAXPROFILE, PROFILETYPE_G3_FAX);
TIFFSetField(t->tiff_file, TIFFTAG_PROFILETYPE, FAXPROFILE_F);
TIFFSetField(t->tiff_file, TIFFTAG_CODINGMETHODS, CODINGMETHODS_T4_1D | CODINGMETHODS_T4_2D | CODINGMETHODS_T6);
TIFFSetField(t->tiff_file, TIFFTAG_VERSIONYEAR, "1998");
TIFFSetField(t->tiff_file, TIFFTAG_MODENUMBER, 3);
offset = 0;
if (!TIFFWriteCustomDirectory(t->tiff_file, &offset))
printf("Failed to write custom directory.\n");
/* Now go back and patch in the pointer to the new IFD */
if (!TIFFSetDirectory(t->tiff_file, s->current_page))
printf("Failed to set directory.\n");
if (!TIFFSetField(t->tiff_file, TIFFTAG_GLOBALPARAMETERSIFD, offset))
printf("Failed to set field.\n");
if (!TIFFWriteDirectory(t->tiff_file))
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to write directory for page %d.\n", t->file, s->current_page);
}
}
#endif
return 0;
}
/*- End of function --------------------------------------------------------*/
static int close_tiff_output_file(t4_rx_state_t *s)
{
int i;
t4_rx_tiff_state_t *t;
t = &s->tiff;
/* Perform any operations needed to tidy up a written TIFF file before
closure. */
if (s->current_page > 1)
{
/* We need to edit the TIFF directories. Until now we did not know
the total page count, so the TIFF file currently says one. Now we
need to set the correct total page count associated with each page. */
for (i = 0; i < s->current_page; i++)
{
if (!TIFFSetDirectory(t->tiff_file, (tdir_t) i))
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to set directory to page %d.\n", s->tiff.file, i);
TIFFSetField(t->tiff_file, TIFFTAG_PAGENUMBER, i, s->current_page);
if (!TIFFWriteDirectory(t->tiff_file))
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to write directory for page %d.\n", s->tiff.file, i);
}
}
TIFFClose(t->tiff_file);
t->tiff_file = NULL;
if (s->tiff.file)
{
/* Try not to leave a file behind, if we didn't receive any pages to
put in it. */
if (s->current_page == 0)
remove(s->tiff.file);
free((char *) s->tiff.file);
}
s->tiff.file = NULL;
return 0;
}
/*- End of function --------------------------------------------------------*/
static void tiff_rx_release(t4_rx_state_t *s)
{
if (s->tiff.tiff_file)
close_tiff_output_file(s);
if (s->tiff.image_buffer)
{
free(s->tiff.image_buffer);
s->tiff.image_buffer = NULL;
s->tiff.image_size = 0;
s->tiff.image_buffer_size = 0;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_put_bit(t4_rx_state_t *s, int bit)
{
/* We only put bit by bit for T.4-1D and T.4-2D */
s->line_image_size += 1;
return t4_t6_decode_put_bit(&s->decoder.t4_t6, bit);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_put(t4_rx_state_t *s, const uint8_t buf[], size_t len)
{
s->line_image_size += 8*len;
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
return t4_t6_decode_put(&s->decoder.t4_t6, buf, len);
case T4_COMPRESSION_T42_T81:
return t42_decode_put(&s->decoder.t42, buf, len);
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
return t43_decode_put(&s->decoder.t43, buf, len);
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
return t85_decode_put(&s->decoder.t85, buf, len);
}
return T4_DECODE_OK;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_set_y_resolution(t4_rx_state_t *s, int resolution)
{
s->metadata.y_resolution = resolution;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_set_x_resolution(t4_rx_state_t *s, int resolution)
{
s->metadata.x_resolution = resolution;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_set_dcs(t4_rx_state_t *s, const char *dcs)
{
s->metadata.dcs = (dcs && dcs[0]) ? dcs : NULL;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_set_sub_address(t4_rx_state_t *s, const char *sub_address)
{
s->metadata.sub_address = (sub_address && sub_address[0]) ? sub_address : NULL;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_set_far_ident(t4_rx_state_t *s, const char *ident)
{
s->metadata.far_ident = (ident && ident[0]) ? ident : NULL;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_set_vendor(t4_rx_state_t *s, const char *vendor)
{
s->metadata.vendor = vendor;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_set_model(t4_rx_state_t *s, const char *model)
{
s->metadata.model = model;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding)
{
switch (encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
break;
default:
t4_t6_decode_init(&s->decoder.t4_t6, encoding, s->image_width, s->row_handler, s->row_handler_user_data);
break;
}
s->line_encoding = encoding;
return t4_t6_decode_set_encoding(&s->decoder.t4_t6, encoding);
case T4_COMPRESSION_T42_T81:
switch (s->line_encoding)
{
case T4_COMPRESSION_T42_T81:
break;
default:
t42_decode_init(&s->decoder.t42, s->row_handler, s->row_handler_user_data);
/* Constrain received images to the maximum width of any FAX. This will
avoid one potential cause of trouble, where a bad received image has
a gigantic dimension that sucks our memory dry. */
t42_decode_set_image_size_constraints(&s->decoder.t42, T4_WIDTH_1200_A3, 0);
break;
}
s->line_encoding = encoding;
return 0;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
switch (s->line_encoding)
{
case T4_COMPRESSION_T43:
break;
default:
t43_decode_init(&s->decoder.t43, s->row_handler, s->row_handler_user_data);
/* Constrain received images to the maximum width of any FAX. This will
avoid one potential cause of trouble, where a bad received image has
a gigantic dimension that sucks our memory dry. */
t43_decode_set_image_size_constraints(&s->decoder.t43, T4_WIDTH_1200_A3, 0);
break;
}
s->line_encoding = encoding;
return 0;
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
switch (s->line_encoding)
{
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
break;
default:
t85_decode_init(&s->decoder.t85, s->row_handler, s->row_handler_user_data);
/* Constrain received images to the maximum width of any FAX. This will
avoid one potential cause of trouble, where a bad received image has
a gigantic dimension that sucks our memory dry. */
t85_decode_set_image_size_constraints(&s->decoder.t85, T4_WIDTH_1200_A3, 0);
break;
}
s->line_encoding = encoding;
return 0;
}
return -1;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_set_image_width(t4_rx_state_t *s, int width)
{
s->image_width = width;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_set_row_write_handler(t4_rx_state_t *s, t4_row_write_handler_t handler, void *user_data)
{
s->row_handler = handler;
s->row_handler_user_data = user_data;
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
return t4_t6_decode_set_row_write_handler(&s->decoder.t4_t6, handler, user_data);
case T4_COMPRESSION_T42_T81:
return t42_decode_set_row_write_handler(&s->decoder.t42, handler, user_data);
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
return t43_decode_set_row_write_handler(&s->decoder.t43, handler, user_data);
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
return t85_decode_set_row_write_handler(&s->decoder.t85, handler, user_data);
}
return -1;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t)
{
memset(t, 0, sizeof(*t));
t->pages_transferred = s->current_page;
t->pages_in_file = s->tiff.pages_in_file;
t->image_x_resolution = s->metadata.x_resolution;
t->image_y_resolution = s->metadata.y_resolution;
t->x_resolution = s->metadata.x_resolution;
t->y_resolution = s->metadata.y_resolution;
t->encoding = s->line_encoding;
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
t->type = T4_IMAGE_TYPE_BILEVEL;
t->width = t4_t6_decode_get_image_width(&s->decoder.t4_t6);
t->length = t4_t6_decode_get_image_length(&s->decoder.t4_t6);
t->image_type = t->type;
t->image_width = t->width;
t->image_length = t->length;
t->line_image_size = t4_t6_decode_get_compressed_image_size(&s->decoder.t4_t6)/8;
t->bad_rows = s->decoder.t4_t6.bad_rows;
t->longest_bad_row_run = s->decoder.t4_t6.longest_bad_row_run;
break;
case T4_COMPRESSION_T42_T81:
t->type = 0;
t->width = t42_decode_get_image_width(&s->decoder.t42);
t->length = t42_decode_get_image_length(&s->decoder.t42);
t->image_type = t->type;
t->image_width = t->width;
t->image_length = t->length;
t->line_image_size = t42_decode_get_compressed_image_size(&s->decoder.t42)/8;
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
t->type = 0;
t->width = t43_decode_get_image_width(&s->decoder.t43);
t->length = t43_decode_get_image_length(&s->decoder.t43);
t->image_type = t->type;
t->image_width = t->width;
t->image_length = t->length;
t->line_image_size = t43_decode_get_compressed_image_size(&s->decoder.t43)/8;
break;
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
t->type = T4_IMAGE_TYPE_BILEVEL;
t->width = t85_decode_get_image_width(&s->decoder.t85);
t->length = t85_decode_get_image_length(&s->decoder.t85);
t->image_type = t->type;
t->image_width = t->width;
t->image_length = t->length;
t->line_image_size = t85_decode_get_compressed_image_size(&s->decoder.t85)/8;
break;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_start_page(t4_rx_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Start rx page %d - compression %s\n", s->current_page, t4_encoding_to_str(s->line_encoding));
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
t4_t6_decode_restart(&s->decoder.t4_t6, s->image_width);
break;
case T4_COMPRESSION_T42_T81:
t42_decode_restart(&s->decoder.t42);
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
t43_decode_restart(&s->decoder.t43);
break;
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
t85_decode_restart(&s->decoder.t85);
break;
}
s->line_image_size = 0;
s->tiff.image_size = 0;
time (&s->tiff.page_start_time);
return 0;
}
/*- End of function --------------------------------------------------------*/
static int tiff_row_write_handler(void *user_data, const uint8_t buf[], size_t len)
{
t4_rx_state_t *s;
uint8_t *t;
s = (t4_rx_state_t *) user_data;
if (buf && len > 0)
{
if (s->tiff.image_size + len >= s->tiff.image_buffer_size)
{
if ((t = realloc(s->tiff.image_buffer, s->tiff.image_buffer_size + 100*len)) == NULL)
return -1;
s->tiff.image_buffer_size += 100*len;
s->tiff.image_buffer = t;
}
memcpy(&s->tiff.image_buffer[s->tiff.image_size], buf, len);
s->tiff.image_size += len;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_end_page(t4_rx_state_t *s)
{
int length;
length = 0;
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
t4_t6_decode_put(&s->decoder.t4_t6, NULL, 0);
length = t4_t6_decode_get_image_length(&s->decoder.t4_t6);
break;
case T4_COMPRESSION_T42_T81:
t42_decode_put(&s->decoder.t42, NULL, 0);
length = t42_decode_get_image_length(&s->decoder.t42);
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
t43_decode_put(&s->decoder.t43, NULL, 0);
length = t43_decode_get_image_length(&s->decoder.t43);
break;
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
t85_decode_put(&s->decoder.t85, NULL, 0);
length = t85_decode_get_image_length(&s->decoder.t85);
break;
}
if (length == 0)
return -1;
if (s->tiff.tiff_file)
{
if (write_tiff_image(s) == 0)
s->current_page++;
s->tiff.image_size = 0;
}
else
{
s->current_page++;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(logging_state_t *) t4_rx_get_logging_state(t4_rx_state_t *s)
{
return &s->logging;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(t4_rx_state_t *) t4_rx_init(t4_rx_state_t *s, const char *file, int output_encoding)
{
int allocated;
allocated = FALSE;
if (s == NULL)
{
if ((s = (t4_rx_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
allocated = TRUE;
}
#if defined(SPANDSP_SUPPORT_TIFF_FX)
TIFF_FX_init();
#endif
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "T.4");
span_log(&s->logging, SPAN_LOG_FLOW, "Start rx document\n");
/* Only provide for one form of coding throughout the file, even though the
coding on the wire could change between pages. */
s->tiff.output_encoding = output_encoding;
/* Set some default values */
s->metadata.x_resolution = T4_X_RESOLUTION_R8;
s->metadata.y_resolution = T4_Y_RESOLUTION_FINE;
s->current_page = 0;
/* Default handler */
s->row_handler = tiff_row_write_handler;
s->row_handler_user_data = s;
if (file)
{
s->tiff.pages_in_file = 0;
if (open_tiff_output_file(s, file) < 0)
{
if (allocated)
free(s);
return NULL;
}
/* Save the file name for logging reports. */
s->tiff.file = strdup(file);
}
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_release(t4_rx_state_t *s)
{
if (s->tiff.file)
tiff_rx_release(s);
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
return t4_t6_decode_release(&s->decoder.t4_t6);
case T4_COMPRESSION_T42_T81:
return t42_decode_release(&s->decoder.t42);
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
return t43_decode_release(&s->decoder.t43);
#endif
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
return t85_decode_release(&s->decoder.t85);
}
return -1;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_free(t4_rx_state_t *s)
{
int ret;
ret = t4_rx_release(s);
free(s);
return ret;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/