freeswitch/libs/spandsp/src/t4_tx.c

1665 lines
57 KiB
C

/*
* SpanDSP - a series of DSP components for telephony
*
* t4_tx.c - ITU T.4 FAX image transmit 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/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"
#include "faxfont.h"
#if defined(SPANDSP_SUPPORT_TIFF_FX)
#include <tif_dir.h>
#endif
/*! The number of centimetres in one inch */
#define CM_PER_INCH 2.54f
typedef struct
{
uint8_t *buf;
int ptr;
int row;
int bit_mask;
} packer_t;
typedef struct
{
float resolution;
int code;
} res_table_t;
static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length);
static const res_table_t x_res_table[] =
{
{ 100.0f/CM_PER_INCH, T4_X_RESOLUTION_100},
{ 102.0f/CM_PER_INCH, T4_X_RESOLUTION_R4},
{ 200.0f/CM_PER_INCH, T4_X_RESOLUTION_200},
{ 204.0f/CM_PER_INCH, T4_X_RESOLUTION_R8},
{ 300.0f/CM_PER_INCH, T4_X_RESOLUTION_300},
{ 400.0f/CM_PER_INCH, T4_X_RESOLUTION_400},
{ 408.0f/CM_PER_INCH, T4_X_RESOLUTION_R16},
{ 600.0f/CM_PER_INCH, T4_X_RESOLUTION_600},
{ 800.0f/CM_PER_INCH, T4_X_RESOLUTION_800},
{1200.0f/CM_PER_INCH, T4_X_RESOLUTION_1200},
{ -1.00f, -1}
};
static const res_table_t y_res_table[] =
{
{ 38.50f, T4_Y_RESOLUTION_STANDARD},
{ 100.0f/CM_PER_INCH, T4_Y_RESOLUTION_100},
{ 77.00f, T4_Y_RESOLUTION_FINE},
{ 200.0f/CM_PER_INCH, T4_Y_RESOLUTION_200},
{ 300.0f/CM_PER_INCH, T4_Y_RESOLUTION_300},
{ 154.00f, T4_Y_RESOLUTION_SUPERFINE},
{ 400.0f/CM_PER_INCH, T4_Y_RESOLUTION_400},
{ 600.0f/CM_PER_INCH, T4_Y_RESOLUTION_600},
{ 800.0f/CM_PER_INCH, T4_Y_RESOLUTION_800},
{1200.0f/CM_PER_INCH, T4_Y_RESOLUTION_1200},
{ -1.00f, -1}
};
static const int resolution_map[10][10] =
{
{ 0, 0, 0, T4_RESOLUTION_R8_STANDARD, 0, 0, 0, 0, 0, 0},
{T4_RESOLUTION_100_100, 0, T4_RESOLUTION_200_100, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, T4_RESOLUTION_R8_FINE, 0, 0, 0, 0, 0, 0},
{ 0, 0, T4_RESOLUTION_200_200, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, T4_RESOLUTION_300_300, 0, 0, 0, 0, 0},
{ 0, 0, 0, T4_RESOLUTION_R8_SUPERFINE, 0, 0, T4_RESOLUTION_R16_SUPERFINE, 0, 0, 0},
{ 0, 0, T4_RESOLUTION_200_400, 0, 0, T4_RESOLUTION_400_400, 0, 0, 0, 0},
{ 0, 0, 0, 0, T4_RESOLUTION_300_600, 0, 0, T4_RESOLUTION_600_600, 0, 0},
{ 0, 0, 0, 0, 0, T4_RESOLUTION_400_800, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, T4_RESOLUTION_600_1200, 0, T4_RESOLUTION_1200_1200},
};
#if defined(SPANDSP_SUPPORT_TIFF_FX)
/* TIFF-FX related extensions to the tag set supported by libtiff */
static const TIFFFieldInfo tiff_fx_tiff_field_info[] =
{
{TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, FIELD_CUSTOM, FALSE, FALSE, (char *) "Indexed"},
{TIFFTAG_GLOBALPARAMETERSIFD, 1, 1, TIFF_IFD8, FIELD_CUSTOM, FALSE, FALSE, (char *) "GlobalParametersIFD"},
{TIFFTAG_PROFILETYPE, 1, 1, TIFF_LONG, FIELD_CUSTOM, FALSE, FALSE, (char *) "ProfileType"},
{TIFFTAG_FAXPROFILE, 1, 1, TIFF_BYTE, FIELD_CUSTOM, FALSE, FALSE, (char *) "FaxProfile"},
{TIFFTAG_CODINGMETHODS, 1, 1, TIFF_LONG, FIELD_CUSTOM, FALSE, FALSE, (char *) "CodingMethods"},
{TIFFTAG_VERSIONYEAR, 4, 4, TIFF_BYTE, FIELD_CUSTOM, FALSE, FALSE, (char *) "VersionYear"},
{TIFFTAG_MODENUMBER, 1, 1, TIFF_BYTE, FIELD_CUSTOM, FALSE, FALSE, (char *) "ModeNumber"},
{TIFFTAG_DECODE, TIFF_VARIABLE, TIFF_VARIABLE, TIFF_SRATIONAL, FIELD_CUSTOM, FALSE, TRUE, (char *) "Decode"},
{TIFFTAG_IMAGEBASECOLOR, TIFF_VARIABLE, TIFF_VARIABLE, TIFF_SHORT, FIELD_CUSTOM, FALSE, TRUE, (char *) "ImageBaseColor"},
{TIFFTAG_T82OPTIONS, 1, 1, TIFF_LONG, FIELD_CUSTOM, FALSE, FALSE, (char *) "T82Options"},
{TIFFTAG_STRIPROWCOUNTS, TIFF_VARIABLE, TIFF_VARIABLE, TIFF_LONG, FIELD_CUSTOM, FALSE, TRUE, (char *) "StripRowCounts"},
{TIFFTAG_IMAGELAYER, 2, 2, TIFF_LONG, FIELD_CUSTOM, FALSE, FALSE, (char *) "ImageLayer"},
};
#if 1
static TIFFField tiff_fx_tiff_fields[] =
{
{ TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "Indexed" },
{ TIFFTAG_GLOBALPARAMETERSIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, (char *) "GlobalParametersIFD", NULL },
{ TIFFTAG_PROFILETYPE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "ProfileType", NULL },
{ TIFFTAG_FAXPROFILE, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "FaxProfile", NULL },
{ TIFFTAG_CODINGMETHODS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "CodingMethods", NULL },
{ TIFFTAG_VERSIONYEAR, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "VersionYear", NULL },
{ TIFFTAG_MODENUMBER, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "ModeNumber", NULL },
{ TIFFTAG_DECODE, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, (char *) "Decode", NULL },
{ TIFFTAG_IMAGEBASECOLOR, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, (char *) "ImageBaseColor", NULL },
{ TIFFTAG_T82OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "T82Options", NULL },
{ TIFFTAG_STRIPROWCOUNTS, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, (char *) "StripRowCounts", NULL },
{ TIFFTAG_IMAGELAYER, 2, 2, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "ImageLayer", NULL },
};
TIFFFieldArray tiff_fx_field_array = { tfiatOther, 0, 12, tiff_fx_tiff_fields };
#endif
static TIFFExtendProc _ParentExtender = NULL;
static void TIFFFXDefaultDirectory(TIFF *tif)
{
/* Install the extended tag field info */
TIFFMergeFieldInfo(tif, tiff_fx_tiff_field_info, 12);
/* Since we may have overriddden another directory method, we call it now to
allow it to set up the rest of its own methods. */
if (_ParentExtender)
(*_ParentExtender)(tif);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) TIFF_FX_init(void)
{
static int first_time = TRUE;
if (!first_time)
return;
first_time = FALSE;
/* Grab the inherited method and install */
_ParentExtender = TIFFSetTagExtender(TIFFFXDefaultDirectory);
}
/*- End of function --------------------------------------------------------*/
#endif
static int match_resolution(int res_unit, float actual, const res_table_t table[])
{
int i;
int best_entry;
float best_ratio;
float ratio;
if (actual == 0.0f)
return -1;
if (res_unit == RESUNIT_INCH)
actual /= CM_PER_INCH;
best_ratio = 0.0f;
best_entry = -1;
for (i = 0; table[i].code > 0; i++)
{
if (actual > table[i].resolution)
ratio = table[i].resolution/actual;
else
ratio = actual/table[i].resolution;
if (ratio > best_ratio)
{
best_entry = i;
best_ratio = ratio;
}
}
if (best_ratio < 0.95f)
return -1;
return best_entry;
}
/*- End of function --------------------------------------------------------*/
#if 0 //defined(SPANDSP_SUPPORT_TIFF_FX)
static int read_colour_map(t4_tx_state_t *s, int bits_per_sample)
{
int i;
uint16_t *map_L;
uint16_t *map_a;
uint16_t *map_b;
uint16_t *map_z;
map_L = NULL;
map_a = NULL;
map_b = NULL;
map_z = NULL;
if (!TIFFGetField(s->tiff.tiff_file, TIFFTAG_COLORMAP, &map_L, &map_a, &map_b, &map_z))
return -1;
/* TODO: This only allows for 8 bit deep maps */
span_log(&s->logging, SPAN_LOG_FLOW, "Got a colour map\n");
s->colour_map_entries = 1 << bits_per_sample;
if ((s->colour_map = realloc(s->colour_map, 3*s->colour_map_entries)) == NULL)
return -1;
#if 0
/* Sweep the colormap in the proper order */
for (i = 0; i < s->colour_map_entries; i++)
{
s->colour_map[3*i + 0] = (map_L[i] >> 8) & 0xFF;
s->colour_map[3*i + 1] = (map_a[i] >> 8) & 0xFF;
s->colour_map[3*i + 2] = (map_b[i] >> 8) & 0xFF;
span_log(&s->logging, SPAN_LOG_FLOW, "Map %3d - %5d %5d %5d\n", i, s->colour_map[3*i], s->colour_map[3*i + 1], s->colour_map[3*i + 2]);
}
#else
/* Sweep the colormap in the order that seems to work for l04x_02x.tif */
for (i = 0; i < s->colour_map_entries; i++)
{
s->colour_map[0*s->colour_map_entries + i] = (map_L[i] >> 8) & 0xFF;
s->colour_map[1*s->colour_map_entries + i] = (map_a[i] >> 8) & 0xFF;
s->colour_map[2*s->colour_map_entries + i] = (map_b[i] >> 8) & 0xFF;
}
#endif
lab_to_srgb(&s->lab_params, s->colour_map, s->colour_map, 256);
for (i = 0; i < s->colour_map_entries; i++)
span_log(&s->logging, SPAN_LOG_FLOW, "Map %3d - %5d %5d %5d\n", i, s->colour_map[3*i], s->colour_map[3*i + 1], s->colour_map[3*i + 2]);
return 0;
}
/*- End of function --------------------------------------------------------*/
#endif
static int get_tiff_directory_info(t4_tx_state_t *s)
{
#if defined(SPANDSP_SUPPORT_TIFF_FX)
static const char *tiff_fx_fax_profiles[] =
{
"???",
"profile S",
"profile F",
"profile J",
"profile C",
"profile L",
"profile M"
};
char *u;
char uu[10];
uint64_t diroff;
uint8_t parm8;
#endif
uint32_t parm32;
int best_x_entry;
int best_y_entry;
float x_resolution;
float y_resolution;
t4_tx_tiff_state_t *t;
uint16_t bits_per_sample;
uint16_t samples_per_pixel;
uint16_t res_unit;
t = &s->tiff;
bits_per_sample = 1;
TIFFGetField(t->tiff_file, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
samples_per_pixel = 1;
TIFFGetField(t->tiff_file, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
if (samples_per_pixel == 1 && bits_per_sample == 1)
t->image_type = T4_IMAGE_TYPE_BILEVEL;
else if (samples_per_pixel == 3 && bits_per_sample == 1)
t->image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL;
else if (samples_per_pixel == 1 && bits_per_sample == 8)
t->image_type = T4_IMAGE_TYPE_GRAY_8BIT;
else if (samples_per_pixel == 1 && bits_per_sample > 8)
t->image_type = T4_IMAGE_TYPE_GRAY_12BIT;
else if (samples_per_pixel == 3 && bits_per_sample == 8)
t->image_type = T4_IMAGE_TYPE_COLOUR_8BIT;
else if (samples_per_pixel == 3 && bits_per_sample > 8)
t->image_type = T4_IMAGE_TYPE_COLOUR_12BIT;
else
return -1;
#if 0
/* Limit ourselves to plain black and white pages */
if (t->image_type != T4_IMAGE_TYPE_BILEVEL)
return -1;
#endif
parm32 = 0;
TIFFGetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, &parm32);
t->image_width =
s->image_width = parm32;
parm32 = 0;
TIFFGetField(t->tiff_file, TIFFTAG_IMAGELENGTH, &parm32);
t->image_length =
s->image_length = parm32;
x_resolution = 0.0f;
TIFFGetField(t->tiff_file, TIFFTAG_XRESOLUTION, &x_resolution);
y_resolution = 0.0f;
TIFFGetField(t->tiff_file, TIFFTAG_YRESOLUTION, &y_resolution);
res_unit = RESUNIT_INCH;
TIFFGetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, &res_unit);
t->photo_metric = PHOTOMETRIC_MINISWHITE;
TIFFGetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, &t->photo_metric);
set_lab_illuminant(&s->lab_params, 0.9638f, 1.0f, 0.8245f);
set_lab_gamut(&s->lab_params, 0, 100, -85, 85, -75, 125, FALSE);
t->compression = -1;
TIFFGetField(t->tiff_file, TIFFTAG_COMPRESSION, &t->compression);
t->fill_order = FILLORDER_LSB2MSB;
if (res_unit == RESUNIT_INCH)
t->image_x_resolution = x_resolution*100.0f/CM_PER_INCH;
else
t->image_x_resolution = x_resolution*100.0f;
/* Treat everything we can't match as R8. Most FAXes are this resolution anyway. */
if ((best_x_entry = match_resolution(res_unit, x_resolution, x_res_table)) < 0)
best_x_entry = 3;
s->metadata.x_resolution = x_res_table[best_x_entry].code;
if (res_unit == RESUNIT_INCH)
t->image_y_resolution = y_resolution*100.0f/CM_PER_INCH;
else
t->image_y_resolution = y_resolution*100.0f;
if ((best_y_entry = match_resolution(res_unit, y_resolution, y_res_table)) < 0)
best_y_entry = 0;
s->metadata.y_resolution = y_res_table[best_y_entry].code;
s->metadata.resolution_code = resolution_map[best_y_entry][best_x_entry];
t4_tx_set_image_width(s, s->image_width);
t4_tx_set_image_length(s, s->image_length);
t4_tx_set_max_2d_rows_per_1d_row(s, -s->metadata.y_resolution);
#if defined(SPANDSP_SUPPORT_TIFF_FX)
if (TIFFGetField(t->tiff_file, TIFFTAG_PROFILETYPE, &parm32))
span_log(&s->logging, SPAN_LOG_FLOW, "Profile type %u\n", parm32);
if (TIFFGetField(t->tiff_file, TIFFTAG_FAXPROFILE, &parm8))
span_log(&s->logging, SPAN_LOG_FLOW, "FAX profile %s (%u)\n", tiff_fx_fax_profiles[parm8], parm8);
if (TIFFGetField(t->tiff_file, TIFFTAG_CODINGMETHODS, &parm32))
span_log(&s->logging, SPAN_LOG_FLOW, "Coding methods 0x%x\n", parm32);
if (TIFFGetField(t->tiff_file, TIFFTAG_VERSIONYEAR, &u))
{
memcpy(uu, u, 4);
uu[4] = '\0';
span_log(&s->logging, SPAN_LOG_FLOW, "Version year \"%s\"\n", uu);
}
if (TIFFGetField(t->tiff_file, TIFFTAG_MODENUMBER, &parm8))
span_log(&s->logging, SPAN_LOG_FLOW, "Mode number %u\n", parm8);
/* If global parameters are present they should only be on the first page of the file.
However, as we scan the file we might as well look for them on any page. */
if (TIFFGetField(t->tiff_file, TIFFTAG_GLOBALPARAMETERSIFD, &diroff))
{
span_log(&s->logging, SPAN_LOG_FLOW, "Global parameters IFD at %" PRIu64 "\n", diroff);
if (!TIFFReadCustomDirectory(t->tiff_file, diroff, &tiff_fx_field_array))
{
span_log(&s->logging, SPAN_LOG_FLOW, "Global parameter read failed\n");
}
else
{
span_log(&s->logging, SPAN_LOG_FLOW, "Global parameters\n");
if (TIFFGetField(t->tiff_file, TIFFTAG_PROFILETYPE, &parm32))
span_log(&s->logging, SPAN_LOG_FLOW, " Profile type %u\n", parm32);
if (TIFFGetField(t->tiff_file, TIFFTAG_FAXPROFILE, &parm8))
span_log(&s->logging, SPAN_LOG_FLOW, " FAX profile %s (%u)\n", tiff_fx_fax_profiles[parm8], parm8);
if (TIFFGetField(t->tiff_file, TIFFTAG_CODINGMETHODS, &parm32))
span_log(&s->logging, SPAN_LOG_FLOW, " Coding methods 0x%x\n", parm32);
if (TIFFGetField(t->tiff_file, TIFFTAG_VERSIONYEAR, &u))
{
memcpy(uu, u, 4);
uu[4] = '\0';
span_log(&s->logging, SPAN_LOG_FLOW, " Version year \"%s\"\n", uu);
}
if (TIFFGetField(t->tiff_file, TIFFTAG_MODENUMBER, &parm8))
span_log(&s->logging, SPAN_LOG_FLOW, " Mode number %u\n", parm8);
if (!TIFFSetDirectory(t->tiff_file, (tdir_t) s->current_page))
span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set directory to page %d\n", s->current_page);
}
}
#endif
return 0;
}
/*- End of function --------------------------------------------------------*/
static int test_tiff_directory_info(t4_tx_state_t *s)
{
uint16_t res_unit;
uint32_t parm32;
uint16_t bits_per_sample;
uint16_t samples_per_pixel;
int image_type;
int best_x_entry;
int best_y_entry;
float x_resolution;
float y_resolution;
t4_tx_tiff_state_t *t;
t = &s->tiff;
bits_per_sample = 1;
TIFFGetField(t->tiff_file, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
samples_per_pixel = 1;
TIFFGetField(t->tiff_file, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
if (samples_per_pixel == 1 && bits_per_sample == 1)
image_type = T4_IMAGE_TYPE_BILEVEL;
else if (samples_per_pixel == 3 && bits_per_sample == 1)
image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL;
else if (samples_per_pixel == 1 && bits_per_sample == 8)
image_type = T4_IMAGE_TYPE_GRAY_8BIT;
else if (samples_per_pixel == 1 && bits_per_sample > 8)
image_type = T4_IMAGE_TYPE_GRAY_12BIT;
else if (samples_per_pixel == 3 && bits_per_sample == 8)
image_type = T4_IMAGE_TYPE_COLOUR_8BIT;
else if (samples_per_pixel == 3 && bits_per_sample > 8)
image_type = T4_IMAGE_TYPE_COLOUR_12BIT;
else
image_type = -1;
#if 0
/* Limit ourselves to plain black and white pages */
if (t->image_type != T4_IMAGE_TYPE_BILEVEL)
return -1;
#endif
if (t->image_type != image_type)
return 1;
parm32 = 0;
TIFFGetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, &parm32);
if (s->image_width != (int) parm32)
return 1;
x_resolution = 0.0f;
TIFFGetField(t->tiff_file, TIFFTAG_XRESOLUTION, &x_resolution);
y_resolution = 0.0f;
TIFFGetField(t->tiff_file, TIFFTAG_YRESOLUTION, &y_resolution);
res_unit = RESUNIT_INCH;
TIFFGetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, &res_unit);
/* Treat everything we can't match as R8. Most FAXes are this resolution anyway. */
if ((best_x_entry = match_resolution(res_unit, x_resolution, x_res_table)) < 0)
return 1;
if (s->metadata.x_resolution != x_res_table[best_x_entry].code)
return 1;
if ((best_y_entry = match_resolution(res_unit, y_resolution, y_res_table)) < 0)
return 1;
if (s->metadata.y_resolution != y_res_table[best_y_entry].code)
return 1;
return 0;
}
/*- End of function --------------------------------------------------------*/
static int get_tiff_total_pages(t4_tx_state_t *s)
{
int max;
/* Each page *should* contain the total number of pages, but can this be
trusted? Some files say 0. Actually searching for the last page is
more reliable. */
max = 0;
while (TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) max))
max++;
/* Back to the previous page */
if (!TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) s->current_page))
return -1;
return max;
}
/*- End of function --------------------------------------------------------*/
static int open_tiff_input_file(t4_tx_state_t *s, const char *file)
{
if ((s->tiff.tiff_file = TIFFOpen(file, "r")) == NULL)
return -1;
return 0;
}
/*- End of function --------------------------------------------------------*/
static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len)
{
t4_tx_state_t *s;
int i;
int j;
s = (t4_tx_state_t *) user_data;
if (s->tiff.row >= s->image_length)
return 0;
memcpy(buf, &s->tiff.image_buffer[s->tiff.row*len], len);
s->tiff.row++;
/* If this is a bi-level image which has more vertical resolution than the
far end will accept, we need to squash it down to size. */
for (i = 1; i < s->row_squashing_ratio && s->tiff.row < s->image_length; i++)
{
for (j = 0; j < s->image_width/8; j++)
buf[j] |= s->tiff.image_buffer[s->tiff.row*len + j];
s->tiff.row++;
}
return len;
}
/*- End of function --------------------------------------------------------*/
static int row_read(void *user_data, uint8_t buf[], size_t len)
{
t4_tx_state_t *s;
s = (t4_tx_state_t *) user_data;
if (s->tiff.raw_row >= s->tiff.image_length)
return 0;
if (TIFFReadScanline(s->tiff.tiff_file, buf, s->tiff.raw_row, 0) < 0)
return 0;
if (s->apply_lab)
lab_to_srgb(&s->lab_params, buf, buf, len/3);
s->tiff.raw_row++;
return len;
}
/*- End of function --------------------------------------------------------*/
static int packing_row_write_handler(void *user_data, const uint8_t buf[], size_t len)
{
packer_t *s;
s = (packer_t *) user_data;
memcpy(&s->buf[s->ptr], buf, len);
s->ptr += len;
s->row++;
return 0;
}
/*- End of function --------------------------------------------------------*/
static int embedded_comment_handler(void *user_data, const uint8_t buf[], size_t len)
{
t4_tx_state_t *s;
s = (t4_tx_state_t *) user_data;
if (buf)
span_log(&s->logging, SPAN_LOG_WARNING, "T.85 comment (%d): %s\n", (int) len, buf);
else
span_log(&s->logging, SPAN_LOG_WARNING, "T.85 comment (%d): ---\n", (int) len);
return 0;
}
/*- End of function --------------------------------------------------------*/
static int read_tiff_t85_image(t4_tx_state_t *s)
{
int biggest;
int num_strips;
int total_len;
int len;
int i;
int result;
uint8_t *t;
uint8_t *raw_data;
t85_decode_state_t t85;
packer_t pack;
/* Size up and allocate the buffer for the raw data */
num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
biggest = TIFFRawStripSize(s->tiff.tiff_file, 0);
for (i = 1; i < num_strips; i++)
{
len = TIFFRawStripSize(s->tiff.tiff_file, i);
if (len > biggest)
biggest = len;
}
if ((raw_data = malloc(biggest)) == NULL)
return -1;
s->tiff.image_size = s->image_length*((s->image_width + 7)/8);
if (s->tiff.image_size >= s->tiff.image_buffer_size)
{
if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL)
return -1;
s->tiff.image_buffer_size = s->tiff.image_size;
s->tiff.image_buffer = t;
}
pack.buf = s->tiff.image_buffer;
pack.ptr = 0;
pack.row = 0;
t85_decode_init(&t85, packing_row_write_handler, &pack);
t85_decode_set_comment_handler(&t85, 1000, embedded_comment_handler, s);
total_len = 0;
result = -1;
for (i = 0; i < num_strips; i++, total_len += len)
{
len = TIFFRawStripSize(s->tiff.tiff_file, i);
if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, raw_data, len)) < 0)
{
span_log(&s->logging, SPAN_LOG_WARNING, "%s: ReadRaw error.\n", s->tiff.file);
return -1;
}
result = t85_decode_put(&t85, raw_data, len);
if (result != T4_DECODE_MORE_DATA)
break;
}
if (result == T4_DECODE_MORE_DATA)
result = t85_decode_put(&t85, NULL, 0);
len = t85_decode_get_compressed_image_size(&t85);
span_log(&s->logging, SPAN_LOG_WARNING, "Compressed image is %d bytes, %d rows\n", len/8, s->image_length);
t85_decode_release(&t85);
free(raw_data);
return 0;
}
/*- End of function --------------------------------------------------------*/
#if defined(SPANDSP_SUPPORT_T43)
static int read_tiff_t43_image(t4_tx_state_t *s, uint8_t **buf)
{
int num_strips;
int total_len;
int len;
int i;
int total_image_len;
int image_size;
logging_state_t *logging;
uint8_t *raw_data;
t43_decode_state_t t43;
packer_t pack;
num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
total_image_len = 0;
for (i = 0; i < num_strips; i++)
total_image_len += TIFFRawStripSize(s->tiff.tiff_file, i);
if ((raw_data = malloc(total_image_len)) == NULL)
return -1;
total_len = 0;
for (i = 0; i < num_strips; i++, total_len += len)
{
if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &raw_data[total_len], total_image_len - total_len)) < 0)
{
span_log(&s->logging, SPAN_LOG_FLOW, "TIFF read error.\n");
return -1;
}
}
t43_decode_init(&t43, packing_row_write_handler, &pack);
t43_decode_set_comment_handler(&t43, 1000, embedded_comment_handler, NULL);
logging = t43_decode_get_logging_state(&t43);
span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
image_size = 3*s->image_length*s->image_width;
if ((*buf = malloc(image_size)) == NULL)
return -1;
pack.buf = *buf;
pack.ptr = 0;
pack.row = 0;
t43_decode_put(&t43, raw_data, total_len);
t43_decode_release(&t43);
free(raw_data);
return image_size;
}
/*- End of function --------------------------------------------------------*/
#endif
#if 0
static int read_tiff_t42_t81_image(t4_tx_state_t *s, uint8_t **buf)
{
int total_len;
int len;
int i;
int num_strips;
int total_image_len;
int image_size;
uint8_t *raw_data;
uint8_t *jpeg_table;
uint32_t jpeg_table_len;
uint32_t w;
uint32_t h;
tsize_t off;
num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
total_image_len = 0;
jpeg_table_len = 0;
if (TIFFGetField(s->tiff.tiff_file, TIFFTAG_JPEGTABLES, &jpeg_table_len, &jpeg_table))
{
total_image_len += (jpeg_table_len - 4);
span_log(&s->logging, SPAN_LOG_FLOW, "JPEG tables %u\n", jpeg_table_len);
}
for (i = 0; i < num_strips; i++)
total_image_len += TIFFRawStripSize(s->tiff.tiff_file, i);
if ((raw_data = malloc(total_image_len)) == NULL)
return -1;
total_len = 0;
if (jpeg_table_len > 0)
total_len += jpeg_table_len - 4;
for (i = 0; i < num_strips; i++, total_len += len)
{
if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &raw_data[total_len], total_image_len - total_len)) < 0)
{
span_log(&s->logging, SPAN_LOG_FLOW, "TIFF read error.\n");
return -1;
}
}
if (jpeg_table_len > 0)
memcpy(raw_data, jpeg_table, jpeg_table_len - 2);
if (total_len != total_image_len)
span_log(&s->logging, SPAN_LOG_FLOW, "Size mismatch %d %d\n", (int) total_len, (int) total_image_len);
image_size = 3*s->image_length*s->image_width;
if ((*buf = malloc(image_size)) == NULL)
return -1;
off = 0;
if (!t42_itulab_to_srgb(&s->logging, &s->lab_params, *buf, &off, raw_data, total_image_len, &w, &h))
{
span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert from ITULAB.\n");
return -1;
}
free(raw_data);
return image_size;
}
/*- End of function --------------------------------------------------------*/
#endif
static int read_tiff_decompressed_image(t4_tx_state_t *s)
{
int total_len;
int len;
int num_strips;
int i;
uint8_t *t;
/* Decode the whole image into a buffer */
/* Let libtiff handle the decompression */
s->tiff.image_size = s->image_length*TIFFScanlineSize(s->tiff.tiff_file);
if (s->tiff.image_size >= s->tiff.image_buffer_size)
{
if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL)
return -1;
s->tiff.image_buffer_size = s->tiff.image_size;
s->tiff.image_buffer = t;
}
/* Allow for the image being stored in multiple strips, although it is rare to find
a stripped image in a T.4 or T.6 encoded file. */
num_strips = TIFFNumberOfStrips(s->tiff.tiff_file);
for (i = 0, total_len = 0; i < num_strips; i++, total_len += len)
{
if ((len = TIFFReadEncodedStrip(s->tiff.tiff_file, i, &s->tiff.image_buffer[total_len], s->tiff.image_size - total_len)) < 0)
{
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Read error.\n", s->tiff.file);
return -1;
}
}
/* We might need to flip all the bits, so 1 = black and 0 = white. */
if (s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE)
{
span_log(&s->logging, SPAN_LOG_FLOW, "%s: Photometric needs swapping.\n", s->tiff.file);
for (i = 0; i < s->tiff.image_size; i++)
s->tiff.image_buffer[i] = ~s->tiff.image_buffer[i];
s->tiff.photo_metric = PHOTOMETRIC_MINISWHITE;
}
/* We might need to bit reverse each of the bytes of the image. */
if (s->tiff.fill_order != FILLORDER_LSB2MSB)
bit_reverse(s->tiff.image_buffer, s->tiff.image_buffer, s->tiff.image_size);
return 0;
}
/*- End of function --------------------------------------------------------*/
static int read_tiff_image(t4_tx_state_t *s)
{
int total_len;
int i;
uint8_t *t;
image_translate_state_t *translator;
if (s->tiff.image_type != T4_IMAGE_TYPE_BILEVEL)
{
/* We need to dither this image down to pure black and white, possibly resizing it
along the way. */
if ((translator = image_translate_init(NULL, s->tiff.image_type, s->image_width, s->image_length, T4_IMAGE_TYPE_BILEVEL, 1728, -1, row_read, s)) == NULL)
return -1;
s->image_width = image_translate_get_output_width(translator);
s->image_length = image_translate_get_output_length(translator);
s->metadata.x_resolution = T4_X_RESOLUTION_R8;
s->metadata.y_resolution = T4_Y_RESOLUTION_FINE;
s->metadata.resolution_code = T4_RESOLUTION_R8_FINE;
s->tiff.image_size = (s->image_width*s->image_length + 7)/8;
if (s->tiff.image_size >= s->tiff.image_buffer_size)
{
if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL)
return -1;
s->tiff.image_buffer_size = s->tiff.image_size;
s->tiff.image_buffer = t;
}
s->tiff.raw_row = 0;
switch (s->tiff.photo_metric)
{
case PHOTOMETRIC_CIELAB:
/* The default luminant is D50 */
set_lab_illuminant(&s->lab_params, 0.96422f, 1.0f, 0.82521f);
set_lab_gamut(&s->lab_params, 0, 100, -128, 127, -128, 127, TRUE);
s->apply_lab = TRUE;
break;
case PHOTOMETRIC_ITULAB:
set_lab_illuminant(&s->lab_params, 0.9638f, 1.0f, 0.8245f);
set_lab_gamut(&s->lab_params, 0, 100, -85, 85, -75, 125, FALSE);
s->apply_lab = TRUE;
break;
default:
s->apply_lab = FALSE;
break;
}
total_len = 0;
for (i = 0; i < s->image_length; i++)
total_len += image_translate_row(translator, &s->tiff.image_buffer[total_len], s->image_width/8);
image_translate_free(translator);
}
else
{
/* The original image is a bi-level one. We can't really rescale it, as that works out
really poorly for a bi-level image. It has to be used in its original form. The only
practical exception is to conver a superfine resolution image to a fine resolution one,
or a fine image to a standard resolution one. We could pad slightly short rows or crop
slightly long one, but lets not bother. */
switch (s->tiff.compression)
{
case COMPRESSION_T85:
/* Decode the whole image into a buffer */
/* libtiff probably cannot decompress T.85, so we must handle it ourselves */
/* Decode the whole image into a buffer */
if (read_tiff_t85_image(s) < 0)
return -1;
break;
default:
/* Decode the whole image into a buffer */
/* Let libtiff handle the decompression */
if (read_tiff_decompressed_image(s) < 0)
return -1;
break;
}
}
s->tiff.row = 0;
return s->image_length;
}
/*- End of function --------------------------------------------------------*/
static void tiff_tx_release(t4_tx_state_t *s)
{
if (s->tiff.tiff_file)
{
TIFFClose(s->tiff.tiff_file);
s->tiff.tiff_file = NULL;
if (s->tiff.file)
free((char *) s->tiff.file);
s->tiff.file = NULL;
}
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 --------------------------------------------------------*/
static int set_row_read_handler(t4_tx_state_t *s, t4_row_read_handler_t handler, void *user_data)
{
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
return t4_t6_encode_set_row_read_handler(&s->encoder.t4_t6, handler, user_data);
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
return t85_encode_set_row_read_handler(&s->encoder.t85, handler, user_data);
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
break;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
return t42_encode_set_row_read_handler(&s->encoder.t42, handler, user_data);
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
return t43_encode_set_row_read_handler(&s->encoder.t43, handler, user_data);
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
break;
#endif
}
return -1;
}
/*- End of function --------------------------------------------------------*/
static int make_header(t4_tx_state_t *s)
{
time_t now;
struct tm tm;
static const char *months[] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
if (s->header_text == NULL)
{
if ((s->header_text = malloc(132 + 1)) == NULL)
return -1;
}
/* This is very English oriented, but then most FAX machines are, too. Some
measure of i18n in the time and date, and even the header_info string, is
entirely possible, although the font area would need some serious work to
properly deal with East Asian script. There is no spec for what the header
should contain, or how much of the page it might occupy. The present format
follows the common practice of a few FAX machines. Nothing more. */
time(&now);
if (s->tz)
tz_localtime(s->tz, &tm, now);
else
tm = *localtime(&now);
snprintf(s->header_text,
132,
" %2d-%s-%d %02d:%02d %-50s %-21s p.%d",
tm.tm_mday,
months[tm.tm_mon],
tm.tm_year + 1900,
tm.tm_hour,
tm.tm_min,
(s->header_info) ? s->header_info : "",
(s->local_ident) ? s->local_ident : "",
s->current_page + 1);
return 0;
}
/*- End of function --------------------------------------------------------*/
static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len)
{
int repeats;
int pattern;
int pos;
int row;
char *t;
t4_tx_state_t *s;
s = (t4_tx_state_t *) user_data;
switch (s->metadata.y_resolution)
{
case T4_Y_RESOLUTION_1200:
repeats = 12;
break;
case T4_Y_RESOLUTION_800:
repeats = 8;
break;
case T4_Y_RESOLUTION_600:
repeats = 6;
break;
case T4_Y_RESOLUTION_SUPERFINE:
repeats = 4;
break;
case T4_Y_RESOLUTION_300:
repeats = 3;
break;
case T4_Y_RESOLUTION_FINE:
repeats = 2;
break;
default:
repeats = 1;
break;
}
repeats /= s->row_squashing_ratio;
if (s->header_overlays_image)
{
/* Read and dump a row of the real image, allowing for the possibility
that the real image might end within the header itself */
if (len != s->row_handler(s->row_handler_user_data, buf, len))
{
set_row_read_handler(s, s->row_handler, s->row_handler_user_data);
return len;
}
}
row = s->header_row/repeats;
pos = 0;
for (t = s->header_text; *t && pos <= len - 2; t++)
{
pattern = header_font[(uint8_t) *t][row];
buf[pos++] = (uint8_t) (pattern >> 8);
buf[pos++] = (uint8_t) (pattern & 0xFF);
}
while (pos < len)
buf[pos++] = 0;
s->header_row++;
if (s->header_row >= 16*repeats)
{
/* End of header. Change to normal image row data. */
set_row_read_handler(s, s->row_handler, s->row_handler_user_data);
}
return len;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_set_row_squashing_ratio(t4_tx_state_t *s, int row_squashing_ratio)
{
s->row_squashing_ratio = row_squashing_ratio;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_next_page_has_different_format(t4_tx_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Checking for the existence of page %d\n", s->current_page + 1);
if (s->current_page >= s->stop_page)
return -1;
if (s->tiff.file)
{
if (!TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) s->current_page + 1))
return -1;
return test_tiff_directory_info(s);
}
return -1;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_set_row_read_handler(t4_tx_state_t *s, t4_row_read_handler_t handler, void *user_data)
{
s->row_handler = handler;
s->row_handler_user_data = user_data;
return set_row_read_handler(s, handler, user_data);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_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_encode_init(&s->encoder.t4_t6, encoding, s->image_width, 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;
}
s->line_encoding = encoding;
if (t4_t6_encode_set_encoding(&s->encoder.t4_t6, encoding))
return -1;
return s->line_encoding;
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_encode_init(&s->encoder.t85, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data);
break;
}
s->line_encoding = encoding;
return s->line_encoding;
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
switch (s->line_encoding)
{
case T4_COMPRESSION_T88:
break;
default:
break;
}
s->line_encoding = encoding;
return s->line_encoding;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
switch (s->line_encoding)
{
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
break;
default:
t42_encode_init(&s->encoder.t42, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data);
break;
}
s->line_encoding = encoding;
return s->line_encoding;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
switch (s->line_encoding)
{
case T4_COMPRESSION_T43:
break;
default:
t43_encode_init(&s->encoder.t43, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data);
break;
}
s->line_encoding = encoding;
return s->line_encoding;
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
switch (s->line_encoding)
{
case T4_COMPRESSION_T45:
break;
default:
break;
}
s->line_encoding = encoding;
return s->line_encoding;
#endif
}
}
return -1;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_set_min_bits_per_row(t4_tx_state_t *s, int bits)
{
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
t4_t6_encode_set_min_bits_per_row(&s->encoder.t4_t6, bits);
break;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width)
{
s->image_width = image_width;
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
t4_t6_encode_set_image_width(&s->encoder.t4_t6, image_width);
break;
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
t85_encode_set_image_width(&s->encoder.t85, image_width);
break;
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
break;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
t42_encode_set_image_width(&s->encoder.t42, image_width);
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
t43_encode_set_image_width(&s->encoder.t43, image_width);
break;
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
break;
#endif
}
}
/*- End of function --------------------------------------------------------*/
static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length)
{
s->image_length = image_length;
switch (s->line_encoding)
{
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
t85_encode_set_image_length(&s->encoder.t85, image_length);
break;
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
break;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
t42_encode_set_image_length(&s->encoder.t42, image_length);
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
t43_encode_set_image_length(&s->encoder.t43, image_length);
break;
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
break;
#endif
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_set_max_2d_rows_per_1d_row(t4_tx_state_t *s, int max)
{
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
t4_t6_encode_set_max_2d_rows_per_1d_row(&s->encoder.t4_t6, max);
break;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get_image_width(t4_tx_state_t *s)
{
return s->image_width;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_set_header_overlays_image(t4_tx_state_t *s, int header_overlays_image)
{
s->header_overlays_image = header_overlays_image;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_set_local_ident(t4_tx_state_t *s, const char *ident)
{
s->local_ident = (ident && ident[0]) ? ident : NULL;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_set_header_info(t4_tx_state_t *s, const char *info)
{
s->header_info = (info && info[0]) ? info : NULL;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_set_header_tz(t4_tx_state_t *s, struct tz_s *tz)
{
s->tz = tz;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get_y_resolution(t4_tx_state_t *s)
{
return s->metadata.y_resolution;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get_x_resolution(t4_tx_state_t *s)
{
return s->metadata.x_resolution;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get_resolution(t4_tx_state_t *s)
{
return s->metadata.resolution_code;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get_image_type(t4_tx_state_t *s)
{
return s->metadata.image_type;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get_pages_in_file(t4_tx_state_t *s)
{
int max;
if (s->tiff.file)
max = get_tiff_total_pages(s);
else
max = 1;
if (max >= 0)
s->tiff.pages_in_file = max;
return max;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get_current_page_in_file(t4_tx_state_t *s)
{
return s->current_page;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t)
{
memset(t, 0, sizeof(*t));
t->pages_transferred = s->current_page - s->start_page;
t->pages_in_file = s->tiff.pages_in_file;
t->image_type = s->tiff.image_type;
t->image_width = s->tiff.image_width;
t->image_length = s->tiff.image_length;
t->image_x_resolution = s->tiff.image_x_resolution;
t->image_y_resolution = s->tiff.image_y_resolution;
t->x_resolution = s->metadata.x_resolution;
t->y_resolution = s->metadata.y_resolution/s->row_squashing_ratio;
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_encode_get_image_width(&s->encoder.t4_t6);
t->length = t4_t6_encode_get_image_length(&s->encoder.t4_t6)/s->row_squashing_ratio;
t->line_image_size = t4_t6_encode_get_compressed_image_size(&s->encoder.t4_t6)/8;
break;
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
t->type = T4_IMAGE_TYPE_BILEVEL;
t->width = t85_encode_get_image_width(&s->encoder.t85);
t->length = t85_encode_get_image_length(&s->encoder.t85)/s->row_squashing_ratio;
t->line_image_size = t85_encode_get_compressed_image_size(&s->encoder.t85)/8;
break;
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
break;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
t->type = 0;
t->width = t42_encode_get_image_width(&s->encoder.t42);
t->length = t42_encode_get_image_length(&s->encoder.t42)/s->row_squashing_ratio;
t->line_image_size = t42_encode_get_compressed_image_size(&s->encoder.t42)/8;
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
t->type = 0;
t->width = t43_encode_get_image_width(&s->encoder.t43);
t->length = t43_encode_get_image_length(&s->encoder.t43)/s->row_squashing_ratio;
t->line_image_size = t43_encode_get_compressed_image_size(&s->encoder.t43)/8;
break;
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
break;
#endif
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_image_complete(t4_tx_state_t *s)
{
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
return t4_t6_encode_image_complete(&s->encoder.t4_t6);
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
return t85_encode_image_complete(&s->encoder.t85);
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
break;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
return t42_encode_image_complete(&s->encoder.t42);
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
return t43_encode_image_complete(&s->encoder.t43);
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
break;
#endif
}
return SIG_STATUS_END_OF_DATA;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get_bit(t4_tx_state_t *s)
{
/* We only get bit by bit for T.4 1D and T.4 2-D. */
return t4_t6_encode_get_bit(&s->encoder.t4_t6);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_get(t4_tx_state_t *s, uint8_t buf[], size_t max_len)
{
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
return t4_t6_encode_get(&s->encoder.t4_t6, buf, max_len);
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
return t85_encode_get(&s->encoder.t85, buf, max_len);
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
break;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
return t42_encode_get(&s->encoder.t42, buf, max_len);
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
return t43_encode_get(&s->encoder.t43, buf, max_len);
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
break;
#endif
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_start_page(t4_tx_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Start tx page %d - compression %s\n", s->current_page, t4_encoding_to_str(s->line_encoding));
if (s->current_page > s->stop_page)
return -1;
if (s->tiff.file)
{
if (!TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) s->current_page))
return -1;
get_tiff_directory_info(s);
if (read_tiff_image(s) < 0)
return -1;
}
else
{
s->image_length = UINT32_MAX;
}
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
t4_t6_encode_restart(&s->encoder.t4_t6, s->image_width);
break;
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
t85_encode_restart(&s->encoder.t85, s->image_width, s->image_length);
break;
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
break;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
t42_encode_restart(&s->encoder.t42, s->image_width, s->image_length);
break;
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
t43_encode_restart(&s->encoder.t43, s->image_width, s->image_length);
break;
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
break;
#endif
}
/* If there is a page header, create that first */
if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL && s->header_info && s->header_info[0] && make_header(s) == 0)
{
s->header_row = 0;
set_row_read_handler(s, header_row_read_handler, (void *) s);
}
else
{
set_row_read_handler(s, s->row_handler, s->row_handler_user_data);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_restart_page(t4_tx_state_t *s)
{
/* This is currently the same as starting a page, but keep it a separate call,
as the two things might diverge a little in the future. */
return t4_tx_start_page(s);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_end_page(t4_tx_state_t *s)
{
s->current_page++;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(logging_state_t *) t4_tx_get_logging_state(t4_tx_state_t *s)
{
return &s->logging;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(t4_tx_state_t *) t4_tx_init(t4_tx_state_t *s, const char *file, int start_page, int stop_page)
{
int allocated;
allocated = FALSE;
if (s == NULL)
{
if ((s = (t4_tx_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
allocated = TRUE;
}
memset(s, 0, sizeof(*s));
#if defined(SPANDSP_SUPPORT_TIFF_FX)
TIFF_FX_init();
#endif
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 tx document\n");
s->current_page =
s->start_page = (start_page >= 0) ? start_page : 0;
s->stop_page = (stop_page >= 0) ? stop_page : INT_MAX;
s->line_encoding = T4_COMPRESSION_NONE;
s->row_handler = tiff_row_read_handler;
s->row_handler_user_data = (void *) s;
s->row_squashing_ratio = 1;
if (file)
{
if (open_tiff_input_file(s, file) < 0)
{
if (allocated)
free(s);
return NULL;
}
s->tiff.file = strdup(file);
s->tiff.pages_in_file = -1;
if (!TIFFSetDirectory(s->tiff.tiff_file, (tdir_t) s->current_page)
||
get_tiff_directory_info(s))
{
tiff_tx_release(s);
if (allocated)
free(s);
return NULL;
}
}
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_release(t4_tx_state_t *s)
{
if (s->tiff.file)
tiff_tx_release(s);
if (s->header_text)
{
free(s->header_text);
s->header_text = NULL;
}
if (s->colour_map)
{
free(s->colour_map);
s->colour_map = NULL;
}
switch (s->line_encoding)
{
case T4_COMPRESSION_T4_1D:
case T4_COMPRESSION_T4_2D:
case T4_COMPRESSION_T6:
return t4_t6_encode_release(&s->encoder.t4_t6);
case T4_COMPRESSION_T85:
case T4_COMPRESSION_T85_L0:
return t85_encode_release(&s->encoder.t85);
#if defined(SPANDSP_SUPPORT_T88)
case T4_COMPRESSION_T88:
break;
#endif
case T4_COMPRESSION_T42_T81:
case T4_COMPRESSION_SYCC_T81:
return t42_encode_release(&s->encoder.t42);
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_T43:
return t43_encode_release(&s->encoder.t43);
#endif
#if defined(SPANDSP_SUPPORT_T45)
case T4_COMPRESSION_T45:
break;
#endif
}
return -1;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t4_tx_free(t4_tx_state_t *s)
{
int ret;
ret = t4_tx_release(s);
free(s);
return ret;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/