// Input 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): // Ulf Zibis (resolution preserving, RVS COM resolutions, // inheritance from CFile) // // 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$ ---RCS-Info--------------------------------------------------*/ #include #include #include #include "sfftypes.h" #include "common.h" extern "C" { #define XMD_H #include #include } #include "codes.h" #include "output.h" #include "decoder.h" #include "input.h" using namespace std; //-Constants------------------------------------------------------- sff_byte CSffFile::m_SFFID[4] = { 0x53, 0x66, 0x66, 0x66 }; //-Types----------------------------------------------------------- typedef enum { NEED_MAGIC, NEED_PAGESTART, NEED_PAGEHEADER, NEED_RECORD, LAST_PAGE } TScannerState; //----------------------------------------------------------------- CSffFile::CSffFile(const std::string& strFileName) : CFile(strFileName) { ScanFile(); } CSffFile::~CSffFile() { PAGEVECTOR::iterator it = m_acPages.begin(); for (; it != m_acPages.end(); it++) { delete (*it); } } void CSffFile::ScanFile() { TSFFFileHeader dh; TSFFPageHeader ph; sff_byte b1 = 0, b2 = 0; sff_word w; int nLineCount = 0; int fuzz = 0; TSFFPage *pPage; if (Eof()) return; TScannerState state = NEED_MAGIC; do { switch (state) { case NEED_MAGIC : for (fuzz = 0; fuzz < 2048; ++fuzz) { Seek(fuzz, CFile::sk_from_start); Read(&dh, sizeof(dh)); if (Eof()) throw CSimpleException(CSimpleException::err_invalidfile); if (::memcmp(&dh.sff_id, &m_SFFID, sizeof(m_SFFID)) == 0) break; } if (::memcmp(&dh.sff_id, &m_SFFID, sizeof(m_SFFID)) != 0) throw CSimpleException(CSimpleException::err_invalidfile); if (dh.version > 1) throw CSimpleException(CSimpleException::err_invalidversion); if (fuzz>0) { cerr << "NOTE: File starts with " << fuzz << " bytes of garbage." << endl; dh.first_page += fuzz; } Seek(dh.first_page, CFile::sk_from_start); state = NEED_PAGESTART; break; case NEED_PAGESTART : b1 = GetC(); // record header (0xFE fuer beginning of page) if (Eof()) throw CSimpleException(CSimpleException::err_corruptfile); if (b1 != 0xFE) throw CSimpleException(CSimpleException::err_corruptfile); b1 = GetC(); // record length (usually 0x10) if (b1 == 0) state = LAST_PAGE; else state = NEED_PAGEHEADER; break; case NEED_PAGEHEADER : Read(&ph, sizeof(TSFFPageHeader)); if (Eof()) throw CSimpleException(CSimpleException::err_corruptfile); if (ph.coding > 0) throw CSimpleException(CSimpleException::err_unknowncoding); Seek(b1 - sizeof(TSFFPageHeader), CFile::sk_current); // skip user data pPage = new TSFFPage; pPage->filepos = Tell(); pPage->width = ph.linelen; pPage->height = ph.pagelen; // Values 254/255 are known for RVS COM pPage->dpi = (ph.horiz_res == 0) ? 203 : (ph.horiz_res == 255) ? 300 : (ph.horiz_res == 254) ? 400 : 0; pPage->lpi = (ph.vert_res == 0) ? 98 : (ph.vert_res == 1) ? 196 : (ph.vert_res == 255) ? 300 : (ph.vert_res == 254) ? 400 : 0; m_acPages.push_back(pPage); state = NEED_RECORD; nLineCount = 0; break; case NEED_RECORD : b1 = GetC(); // read record type if (Eof()) { m_acPages[GetPageCount()-1]->height = nLineCount; state = LAST_PAGE; } else if (b1 == 0) { // variable amount of bytes following b1 = GetC(); // LSB if (Eof()) throw CSimpleException(CSimpleException::err_corruptfile); b2 = GetC(); // MSB if (Eof()) throw CSimpleException(CSimpleException::err_corruptfile); w = ((b2 << 8) | b1); Seek(w, CFile::sk_current); // skip data if (Eof()) throw CSimpleException(CSimpleException::err_corruptfile); ++nLineCount; } else if (b1 < 217) { // normal amount of bytes following Seek((long)b1, CFile::sk_current); // skip data if (Eof()) { throw CSimpleException(CSimpleException::err_corruptfile); } ++nLineCount; } else if (b1 < 254) { // white skip nLineCount+=(b1 - 216); } else if (b1 < 255) { // 254 -> page header m_acPages[GetPageCount()-1]->height = nLineCount; nLineCount = 0; b1 = GetC(); // record length (usually 0x10) if (Eof()) throw CSimpleException(CSimpleException::err_corruptfile); if (b1 == 0) { state = LAST_PAGE; } else { state = NEED_PAGEHEADER; } } else { // bad line or user info b1 = GetC(); // LSB if (Eof()) throw CSimpleException(CSimpleException::err_corruptfile); if (b1 == 0) { ++nLineCount; } else { Seek(b1, CFile::sk_current); // skip user info if (Eof()) throw CSimpleException(CSimpleException::err_corruptfile); } } break; case LAST_PAGE : break; } } while(state != LAST_PAGE); return; } bool CSffFile::PageIsValid(int nPage) { return ((nPage >= 0) && (nPage < GetPageCount())); } bool CSffFile::SeekPage(int nPage) { if (!PageIsValid(nPage)) return false; Seek(m_acPages[nPage]->filepos, CFile::sk_from_start); return true; } TSFFPage *CSffFile::GetPage(int nPage) { return PageIsValid(nPage) ? m_acPages[nPage] : (TSFFPage *)NULL; } bool CSffFile::IsLowRes(int nPage) { return PageIsValid(nPage) && (m_acPages[nPage]->lpi == 98); } sff_word CSffFile::GetHorizontalResolution(int nPage) { return PageIsValid(nPage) ? m_acPages[nPage]->dpi :0; } sff_word CSffFile::GetVerticalResolution(int nPage) { return PageIsValid(nPage) ? m_acPages[nPage]->lpi :0; } sff_dword CSffFile::GetPageWidth(int nPage) { return PageIsValid(nPage) ? m_acPages[nPage]->width :0; } sff_dword CSffFile::GetPageHeight(int nPage) { return PageIsValid(nPage) ? m_acPages[nPage]->height :0; } bool CSffFile::GetRecord(TSFFRecord& rec) { sff_byte b1, b2; sff_word w; bool result; if (Eof()) { return false; } b1 = GetC(); // read record type if (Eof()) { result = FALSE; } else if (b1 == 0) { // variable amount of bytes following b1 = GetC(); // LSB b2 = GetC(); // MSB w = ((b2 << 8) | b1); rec.type = NORMAL; rec.cb = w; rec.pData = (sff_byte *)malloc(w); Read(rec.pData, w); result = TRUE; } else if (b1 < 217) { // normal amount of bytes following rec.type = NORMAL; rec.cb = b1; rec.pData = (sff_byte *)malloc(b1); Read(rec.pData, b1); result = TRUE; } else if (b1 < 254) { // white skip rec.type = WHITESKIP; rec.cb = (b1 - 216); rec.pData = 0; result = TRUE; } else if (b1 < 255) { // 254 -> page header result = FALSE; } else { // bad line or user info b1 = GetC(); // LSB if (b1 == 0) { rec.type = BADLINE; rec.cb = 1; rec.pData = 0; } else { rec.type = USERINFO; rec.cb = b1; rec.pData = (sff_byte *)malloc(b1); Read(rec.pData, b1); } result = TRUE; } return result; } bool CSffFile::DecodeRecord(TSFFRecord& rec, CBitSink& bitsink) { bool rc; if (rec.type != NORMAL) return FALSE; CHuffDecoder source(rec.pData, rec.cb); try { rec.runlength = source.DecodeLine(bitsink); rc = TRUE; } catch(const CSimpleException& e) { cerr << "ERROR: " << e.what() << endl; rc = FALSE; } return rc; }