// 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 (StdOut output support) // Ulf Zibis (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 #include #include #include #include #ifdef WIN32 #include #endif #include "sfftypes.h" #include "common.h" extern "C" { #define XMD_H #include #include } #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; }