790 lines
26 KiB
C
Raw Normal View History

/*
* 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/t81_t82_arith_coding.h"
#include "spandsp/t85.h"
#if defined(SPANDSP_SUPPORT_T42)
#include "spandsp/t42.h"
#endif
#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"
#if defined(SPANDSP_SUPPORT_T42)
#include "spandsp/private/t42.h"
#endif
#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/t4_rx.h"
#include "spandsp/private/t4_tx.h"
/*! The number of centimetres in one inch */
#define CM_PER_INCH 2.54f
SPAN_DECLARE(const char *) t4_encoding_to_str(int encoding)
{
switch (encoding)
{
case T4_COMPRESSION_NONE:
return "None";
case T4_COMPRESSION_ITU_T4_1D:
return "T.4 1-D";
case T4_COMPRESSION_ITU_T4_2D:
return "T.4 2-D";
case T4_COMPRESSION_ITU_T6:
return "T.6";
case T4_COMPRESSION_ITU_T42:
return "T.42";
case T4_COMPRESSION_ITU_SYCC_T42:
return "sYCC T.42";
case T4_COMPRESSION_ITU_T43:
return "T.43";
case T4_COMPRESSION_ITU_T45:
return "T.45";
case T4_COMPRESSION_ITU_T85:
return "T.85";
case T4_COMPRESSION_ITU_T85_L0:
return "T.85(L0)";
}
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;
t = &s->tiff;
/* Prepare the directory entry fully before writing the image, or libtiff complains */
switch (t->output_encoding)
{
case T4_COMPRESSION_ITU_T6:
output_compression = COMPRESSION_CCITT_T6;
output_t4_options = 0;
break;
case T4_COMPRESSION_ITU_T4_2D:
output_compression = COMPRESSION_CCITT_T4;
output_t4_options = GROUP3OPT_FILLBITS | GROUP3OPT_2DENCODING;
break;
default:
output_compression = COMPRESSION_CCITT_T4;
output_t4_options = GROUP3OPT_FILLBITS;
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;
}
#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");
/* TIFFSetField(t->tiff_file, TIFFTAG_MODENUMBER, 0); */
#endif
TIFFSetField(t->tiff_file, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(t->tiff_file, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(t->tiff_file, TIFFTAG_SAMPLESPERPIXEL, 1);
if (output_compression == COMPRESSION_CCITT_T4
||
output_compression == COMPRESSION_CCITT_T6)
{
TIFFSetField(t->tiff_file, TIFFTAG_ROWSPERSTRIP, -1L);
}
else
{
TIFFSetField(t->tiff_file,
TIFFTAG_ROWSPERSTRIP,
TIFFDefaultStripSize(t->tiff_file, 0));
}
TIFFSetField(t->tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
TIFFSetField(t->tiff_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
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;
switch (s->line_encoding)
{
case T4_COMPRESSION_ITU_T4_1D:
case T4_COMPRESSION_ITU_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_ITU_T6:
TIFFSetField(t->tiff_file, TIFFTAG_IMAGELENGTH, t4_t6_decode_get_image_length(&s->decoder.t4_t6));
break;
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
TIFFSetField(t->tiff_file, TIFFTAG_IMAGELENGTH, t42_decode_get_image_length(&s->decoder.t42));
break;
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
TIFFSetField(t->tiff_file, TIFFTAG_IMAGELENGTH, t43_decode_get_image_length(&s->decoder.t43));
break;
#endif
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_T85_L0:
TIFFSetField(t->tiff_file, TIFFTAG_IMAGELENGTH, t85_decode_get_image_length(&s->decoder.t85));
break;
}
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 write_tiff_image(t4_rx_state_t *s)
{
if (s->tiff.image_buffer == NULL || s->tiff.image_size <= 0)
return -1;
/* Set up the TIFF directory info... */
set_tiff_directory_info(s);
/* ...and then write the image... */
if (TIFFWriteEncodedStrip(s->tiff.tiff_file, 0, s->tiff.image_buffer, s->tiff.image_size) < 0)
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", s->tiff.file);
/* ...then the directory entry, and libtiff is happy. */
TIFFWriteDirectory(s->tiff.tiff_file);
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++)
{
TIFFSetDirectory(t->tiff_file, (tdir_t) i);
TIFFSetField(t->tiff_file, TIFFTAG_PAGENUMBER, i, s->current_page);
TIFFWriteDirectory(t->tiff_file);
}
}
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)
{
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_byte(t4_rx_state_t *s, uint8_t byte)
{
s->line_image_size += 8;
switch (s->line_encoding)
{
case T4_COMPRESSION_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_T6:
return t4_t6_decode_put_byte(&s->decoder.t4_t6, byte);
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
return t42_decode_put_byte(&s->decoder.t42, byte);
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
return t43_decode_put_byte(&s->decoder.t43, byte);
#endif
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_T85_L0:
return t85_decode_put_byte(&s->decoder.t85, byte);
}
return TRUE;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_rx_put_chunk(t4_rx_state_t *s, const uint8_t buf[], int len)
{
s->line_image_size += 8*len;
switch (s->line_encoding)
{
case T4_COMPRESSION_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_T6:
return t4_t6_decode_put_chunk(&s->decoder.t4_t6, buf, len);
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
return t42_decode_put_chunk(&s->decoder.t42, buf, len);
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
return t43_decode_put_chunk(&s->decoder.t43, buf, len);
#endif
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_T85_L0:
return t85_decode_put_chunk(&s->decoder.t85, buf, len);
}
return TRUE;
}
/*- 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_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_T6:
switch (s->line_encoding)
{
case T4_COMPRESSION_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_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);
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
switch (s->line_encoding)
{
case T4_COMPRESSION_ITU_T42:
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;
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
switch (s->line_encoding)
{
case T4_COMPRESSION_ITU_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_ITU_T85:
case T4_COMPRESSION_ITU_T85_L0:
switch (s->line_encoding)
{
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_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_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_T6:
return t4_t6_decode_set_row_write_handler(&s->decoder.t4_t6, handler, user_data);
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
return t42_decode_set_row_write_handler(&s->decoder.t42, handler, user_data);
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
return t43_decode_set_row_write_handler(&s->decoder.t43, handler, user_data);
#endif
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_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->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_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_T6:
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->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;
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
t->width = t42_decode_get_image_width(&s->decoder.t42);
t->length = t42_decode_get_image_length(&s->decoder.t42);
t->line_image_size = t42_decode_get_compressed_image_size(&s->decoder.t42)/8;
break;
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
t->width = t43_decode_get_image_width(&s->decoder.t43);
t->length = t43_decode_get_image_length(&s->decoder.t43);
t->line_image_size = t43_decode_get_compressed_image_size(&s->decoder.t43)/8;
break;
#endif
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_T85_L0:
t->width = t85_decode_get_image_width(&s->decoder.t85);
t->length = t85_decode_get_image_length(&s->decoder.t85);
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_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_T6:
t4_t6_decode_restart(&s->decoder.t4_t6, s->image_width);
break;
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
t42_decode_restart(&s->decoder.t42);
break;
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
t43_decode_restart(&s->decoder.t43);
break;
#endif
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_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_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_T6:
t4_t6_decode_put_byte(&s->decoder.t4_t6, SIG_STATUS_END_OF_DATA);
length = t4_t6_decode_get_image_length(&s->decoder.t4_t6);
break;
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
t42_decode_put_byte(&s->decoder.t42, SIG_STATUS_END_OF_DATA);
length = t42_decode_get_image_length(&s->decoder.t42);
break;
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
t43_decode_put_byte(&s->decoder.t43, SIG_STATUS_END_OF_DATA);
length = t43_decode_get_image_length(&s->decoder.t43);
break;
#endif
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_T85_L0:
t85_decode_put_byte(&s->decoder.t85, SIG_STATUS_END_OF_DATA);
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(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_ITU_T4_1D:
case T4_COMPRESSION_ITU_T4_2D:
case T4_COMPRESSION_ITU_T6:
return t4_t6_decode_release(&s->decoder.t4_t6);
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
return t42_decode_release(&s->decoder.t42);
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
return t43_decode_release(&s->decoder.t43);
#endif
case T4_COMPRESSION_ITU_T85:
case T4_COMPRESSION_ITU_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 ------------------------------------------------------------*/