307 lines
9.7 KiB
C++
307 lines
9.7 KiB
C++
// Output classes
|
|
//
|
|
// This file is part of sfftobmp, a program to convert
|
|
// structured fax files (sff) to windows bitmap files (bmp),
|
|
// portable bitmap graphics (pbm), tagged image file format (tiff)
|
|
// or JPEG (jpg).
|
|
//
|
|
// Copyright (C) 1998-2012 Peter Schaefer-Hutter and contributors ("THE AUTHORS")
|
|
//
|
|
// Permission to use, copy, modify, distribute, and sell this software and
|
|
// its documentation for any purpose is hereby granted without fee.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
|
|
// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
|
|
// INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY
|
|
// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
// WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY
|
|
// THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE
|
|
// OR PERFORMANCE OF THIS SOFTWARE.
|
|
//
|
|
// Contributor(s):
|
|
// Gernot Hillier <ghillie@suse.de> (StdOut output support)
|
|
// Ulf Zibis <ulf.zibis@gmx.de> (Resolution preserving output, Cleanups,
|
|
// inheritance from CFile, destructor handling, preserving filetime)
|
|
//
|
|
// You can contact the original author by email at peter.schaefer@gmx.de.
|
|
//
|
|
// I'm always pleased to hear that somebody is actually using my software.
|
|
// If you can manage it, e-mail me a quick notice. Thanks!
|
|
//
|
|
/*-RCS-Info----------------------------------------------------
|
|
|
|
$Id: output.cpp,v 1.5 2009/04/28 19:02:08 pschaefer Exp $
|
|
|
|
---RCS-Info--------------------------------------------------*/
|
|
|
|
#include <cstring>
|
|
#include <cassert>
|
|
#include <iostream>
|
|
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <boost/filesystem/convenience.hpp>
|
|
|
|
#ifdef WIN32
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#include "sfftypes.h"
|
|
#include "common.h"
|
|
|
|
extern "C"
|
|
{
|
|
#define XMD_H
|
|
#include <jpeglib.h>
|
|
#include <tiffio.h>
|
|
}
|
|
|
|
#include "codes.h"
|
|
#include "decoder.h"
|
|
#include "output.h"
|
|
|
|
using namespace std;
|
|
namespace fs = boost::filesystem;
|
|
|
|
//-Constants-------------------------------------------------------
|
|
|
|
TBitMapFileHeader CBMPFilter::m_bmpHeader =
|
|
{
|
|
0x4d42, /* magic 'BM' */
|
|
0, /* filesize */
|
|
0, /* reserved */
|
|
0, /* reserved */
|
|
sizeof(TBitMapFileHeader), /* offset to bitmap data */
|
|
0x28, /* length of bitmap info header 0x28 -> Windows BMP */
|
|
0, /* width in pixels */
|
|
0, /* height in pixels */
|
|
1, /* no. of planes */
|
|
1, /* bpp -> monochrome */
|
|
0, /* compression -> none */
|
|
0, /* size of bitmap data in bytes rounded to 4 byte boundary */
|
|
0, /* horz. resolution in pixels/meter */
|
|
0, /* vert. resolution in pixels/meter */
|
|
2, /* no. of colours */
|
|
2, /* no. of important colours */
|
|
{ 255,255,255, 0 }, /* palette */
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
CBitSink& COutputFilter::GetBitSink()
|
|
{
|
|
m_sink.Reset();
|
|
return m_sink;
|
|
}
|
|
|
|
void COutputFilter::BlankLine()
|
|
{
|
|
::memset(m_abBuffer, 0x00, sizeof(m_abBuffer));
|
|
}
|
|
|
|
void COutputFilter::Init(CFile *pFile)
|
|
{
|
|
m_pFile = pFile;
|
|
}
|
|
|
|
void COutputFilter::EndPage()
|
|
{
|
|
}
|
|
|
|
void COutputFilter::Finalize()
|
|
{
|
|
// base class does nothing
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void CBMPFilter::BeginPage(sff_dword aPage, sff_dword aWidth,
|
|
sff_dword aHeight, sff_word aDpi,
|
|
sff_word aLpi)
|
|
{
|
|
assert(m_pFile);
|
|
|
|
m_bmpHeader.dwPixelWidth = aWidth;
|
|
m_bmpHeader.dwPixelHeight = aHeight;
|
|
m_bmpHeader.dwPixelWidthPerMeter = (aDpi*10000+127)/254;
|
|
m_bmpHeader.dwPixelHeightPerMeter = (aLpi*10000+127)/254;
|
|
m_bmpHeader.dwSizeImage = (m_bmpHeader.dwPixelWidth >> 3) * m_bmpHeader.dwPixelHeight;
|
|
m_nLineToWrite = aHeight;
|
|
|
|
m_pFile->Seek(0, CFile::sk_from_start);
|
|
m_pFile->Write(&m_bmpHeader, sizeof(m_bmpHeader));
|
|
}
|
|
|
|
void CBMPFilter::WriteLine()
|
|
{
|
|
assert(m_pFile);
|
|
|
|
sff_dword dwBytesPerLine = (m_bmpHeader.dwPixelWidth >> 3);
|
|
/* we write lines from the end to the top of the file */
|
|
if (m_nLineToWrite-- > 0) {
|
|
m_pFile->Seek(dwBytesPerLine * m_nLineToWrite + sizeof(m_bmpHeader), CFile::sk_from_start);
|
|
m_pFile->Write(m_abBuffer, dwBytesPerLine);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void CPBMFilter::BeginPage(sff_dword aPage, sff_dword aWidth,
|
|
sff_dword aHeight, sff_word aDpi,
|
|
sff_word aLpi)
|
|
{
|
|
char acBuf[50];
|
|
|
|
assert(m_pFile);
|
|
|
|
m_Width = aWidth;
|
|
m_pFile->Seek(0, CFile::sk_from_start);
|
|
sprintf(acBuf, "%s", "P4\n");
|
|
m_pFile->Write(acBuf, strlen(acBuf));
|
|
sprintf(acBuf, "%s", "# generated with SffToBmp\n");
|
|
m_pFile->Write(acBuf, strlen(acBuf));
|
|
sprintf(acBuf, "%ld %ld\n", (long int) aWidth, (long int) aHeight);
|
|
m_pFile->Write(acBuf, strlen(acBuf));
|
|
}
|
|
|
|
void CPBMFilter::WriteLine()
|
|
{
|
|
assert(m_pFile);
|
|
m_pFile->Write(m_abBuffer, m_Width >> 3);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void CJPEGFilter::BeginPage(sff_dword aPage, sff_dword aWidth,
|
|
sff_dword aHeight, sff_word aDpi,
|
|
sff_word aLpi)
|
|
{
|
|
assert(m_pFile);
|
|
memset(&m_cinfo, 0, sizeof(m_cinfo));
|
|
m_cinfo.err = jpeg_std_error(&m_jerr);
|
|
jpeg_create_compress(&m_cinfo);
|
|
m_cinfo.in_color_space = JCS_GRAYSCALE;
|
|
jpeg_set_defaults(&m_cinfo);
|
|
m_cinfo.image_width = aWidth;
|
|
m_cinfo.image_height = aHeight;
|
|
m_cinfo.input_components = 1;
|
|
m_cinfo.dct_method = JDCT_FLOAT;
|
|
m_cinfo.density_unit = 1;
|
|
m_cinfo.X_density = aDpi;
|
|
m_cinfo.Y_density = aLpi;
|
|
jpeg_stdio_dest(&m_cinfo, m_pFile->GetFP());
|
|
jpeg_set_quality(&m_cinfo, m_quality, FALSE);
|
|
jpeg_set_colorspace(&m_cinfo, JCS_GRAYSCALE);
|
|
jpeg_start_compress(&m_cinfo, TRUE);
|
|
}
|
|
|
|
void CJPEGFilter::WriteLine()
|
|
{
|
|
m_row_pointer[0] = m_abBuffer;
|
|
jpeg_write_scanlines(&m_cinfo, m_row_pointer, 1);
|
|
}
|
|
|
|
void CJPEGFilter::EndPage()
|
|
{
|
|
assert(m_pFile);
|
|
jpeg_finish_compress(&m_cinfo);
|
|
jpeg_destroy_compress(&m_cinfo);
|
|
}
|
|
|
|
CBitSink& CJPEGFilter::GetBitSink()
|
|
{
|
|
m_bytesink.Reset();
|
|
return m_bytesink;
|
|
}
|
|
|
|
void CJPEGFilter::BlankLine()
|
|
{
|
|
::memset(m_abBuffer, 0xFF, sizeof(m_abBuffer));
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void CTIFFFilter::Init(CFile *pFile)
|
|
{
|
|
COutputFilter::Init(pFile);
|
|
}
|
|
|
|
void CTIFFFilter::BeginPage(sff_dword aPage, sff_dword aWidth,
|
|
sff_dword aHeight, sff_word aDpi,
|
|
sff_word aLpi)
|
|
{
|
|
tm *pTime;
|
|
char acTime[25];
|
|
|
|
if (!m_tiffFile) {
|
|
#ifndef WIN32
|
|
m_tiffFile = TIFFFdOpen(m_pFile->GetFN(), m_pFile->GetFileName(), "w");
|
|
#else
|
|
m_tiffFile = TIFFFdOpen(_get_osfhandle(m_pFile->GetFN()),
|
|
m_pFile->GetFileName(), "w");
|
|
#endif
|
|
}
|
|
m_nRow = 0;
|
|
// --- TIFF required:
|
|
TIFFSetField(m_tiffFile, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
|
|
TIFFSetField(m_tiffFile, TIFFTAG_IMAGEWIDTH, aWidth);
|
|
// TIFFTAG_IMAGELENGTH ...automaticaly set by TIFFWriteScanline()
|
|
// TIFFTAG_STRIPOFFSETS ...automaticaly set by TIFFWriteScanline()
|
|
TIFFSetField(m_tiffFile, TIFFTAG_ROWSPERSTRIP, aHeight);
|
|
// TIFFTAG_STRIPBYTECOUNTS ...automaticaly set by TIFFWriteScanline()
|
|
TIFFSetField(m_tiffFile, TIFFTAG_XRESOLUTION, (float)aDpi);
|
|
TIFFSetField(m_tiffFile, TIFFTAG_YRESOLUTION, (float)aLpi);
|
|
TIFFSetField(m_tiffFile, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
|
|
// --- Class B required:
|
|
TIFFSetField(m_tiffFile, TIFFTAG_BITSPERSAMPLE, 1);
|
|
TIFFSetField(m_tiffFile, TIFFTAG_COMPRESSION, m_wCompression);
|
|
TIFFSetField(m_tiffFile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
|
|
TIFFSetField(m_tiffFile, TIFFTAG_SAMPLESPERPIXEL, 1);
|
|
// --- Class F required:
|
|
TIFFSetField(m_tiffFile, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
|
|
TIFFSetField(m_tiffFile, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
|
|
if (m_wCompression == COMPRESSION_CCITTFAX3) {
|
|
TIFFSetField(m_tiffFile, TIFFTAG_GROUP3OPTIONS, GROUP3OPT_FILLBITS|GROUP3OPT_2DENCODING);
|
|
}
|
|
TIFFSetField(m_tiffFile, TIFFTAG_PAGENUMBER, aPage, m_nPageCount);
|
|
// --- Class F recommended:
|
|
TIFFSetField(m_tiffFile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
|
TIFFSetField(m_tiffFile, TIFFTAG_SOFTWARE, "SffToBmp");
|
|
// - Some viewers interpret this as fax sender
|
|
TIFFSetField(m_tiffFile, TIFFTAG_IMAGEDESCRIPTION, "");
|
|
// - Some viewers interpret this as receive time
|
|
// - Has to be exactly 19 chars: 2007-01-01 24:00:00
|
|
if ((pTime = ::gmtime(&m_nModTime))) {
|
|
strftime(acTime, 20, "%Y-%m-%d %H:%M:%S", pTime);
|
|
}
|
|
acTime[19]='\0';
|
|
TIFFSetField(m_tiffFile, TIFFTAG_DATETIME, acTime);
|
|
// - not recommended, but required by TIFFWriteScanline(); why ?:
|
|
TIFFSetField(m_tiffFile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
|
//TIFFSetWarningHandler(NULL);
|
|
}
|
|
|
|
void CTIFFFilter::WriteLine()
|
|
{
|
|
if (m_tiffFile) {
|
|
TIFFWriteScanline(m_tiffFile, m_abBuffer, m_nRow++,0);
|
|
}
|
|
}
|
|
|
|
void CTIFFFilter::EndPage()
|
|
{
|
|
TIFFWriteDirectory(m_tiffFile);
|
|
}
|
|
|
|
void CTIFFFilter::Finalize()
|
|
{
|
|
if (m_tiffFile) {
|
|
TIFFClose(m_tiffFile);
|
|
}
|
|
m_tiffFile = NULL;
|
|
}
|
|
|