324 lines
10 KiB
C++
324 lines
10 KiB
C++
// Main program file
|
|
//
|
|
// 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):
|
|
// None
|
|
//
|
|
// 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 <cassert>
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <boost/filesystem/operations.hpp>
|
|
#include <boost/filesystem/convenience.hpp>
|
|
|
|
extern "C"
|
|
{
|
|
#define XMD_H
|
|
#include <jpeglib.h>
|
|
#include <tiffio.h>
|
|
}
|
|
|
|
#include "sfftypes.h"
|
|
#include "common.h"
|
|
|
|
#include "codes.h"
|
|
#include "output.h"
|
|
#include "decoder.h"
|
|
#include "input.h"
|
|
#include "cmdline.h"
|
|
|
|
#include "main.h"
|
|
|
|
using namespace std;
|
|
namespace fs = boost::filesystem;
|
|
|
|
int main( int argc, char *argv[] )
|
|
{
|
|
int rc = 0;
|
|
|
|
fs::path pathInFileName;
|
|
fs::path pathOutFileName;
|
|
fs::path pathOutDirectory;
|
|
stringstream ssFilename;
|
|
|
|
CCmdLineProcessor proc(argv, argc);
|
|
|
|
int nIdx = 0;
|
|
try
|
|
{
|
|
proc.parseCmdLine();
|
|
|
|
if (!proc.getFileCount())
|
|
return 0;
|
|
|
|
const string& strOutSpec = proc.getOutSpec();
|
|
|
|
bool bStdOut = (strOutSpec == "-");
|
|
bool bQuiet = proc.beQuiet() || bStdOut;
|
|
|
|
if (strOutSpec.length() && (strOutSpec != "-")) {
|
|
if (proc.getFileCount() > 1) {
|
|
// More than one input file -> interpret OutSpec as a directory
|
|
if (!fs::exists(strOutSpec)) { // Create directory, if necessary
|
|
if (!bQuiet) cout << endl << "Creating directory " << strOutSpec.c_str() << "." << endl;
|
|
try {
|
|
fs::create_directories(strOutSpec);
|
|
}
|
|
catch (const std::exception&) {
|
|
}
|
|
}
|
|
// Check that dir exists
|
|
if (!(fs::exists(strOutSpec) && fs::is_directory(strOutSpec))) {
|
|
throw CSimpleException(CSimpleException::err_outdir);
|
|
}
|
|
pathOutDirectory = strOutSpec;
|
|
} else {
|
|
// Exact one input file -> interpret OutSpec as one file
|
|
if (fs::exists(strOutSpec) && fs::is_directory(strOutSpec)) {
|
|
throw CSimpleException(CSimpleException::err_outfileisdir);
|
|
}
|
|
pathOutFileName = strOutSpec;
|
|
}
|
|
}
|
|
|
|
while (proc.getNextFile(pathInFileName))
|
|
{
|
|
COutputFilter *pOut = NULL;
|
|
CSffFile *pInfile = NULL;
|
|
|
|
try
|
|
{
|
|
fs::path outPath;
|
|
|
|
CSffFile *pInfile = new CSffFile(pathInFileName.string());
|
|
int nPageCount = pInfile->GetPageCount();
|
|
if (!bQuiet)
|
|
cout << "File " << pathInFileName.string()
|
|
<< " seems to have " << nPageCount << " page(s)." << endl;
|
|
int nFileCountOut = nPageCount;
|
|
time_t modTime = pInfile->GetModificationTime();
|
|
|
|
switch (proc.getOutputFormat())
|
|
{
|
|
case CCmdLineProcessor::fmt_bmp:
|
|
pOut = new CBMPFilter(nPageCount);
|
|
break;
|
|
case CCmdLineProcessor::fmt_pbm:
|
|
pOut = new CPBMFilter(nPageCount);
|
|
break;
|
|
case CCmdLineProcessor::fmt_jpeg:
|
|
pOut = new CJPEGFilter(nPageCount, proc.getJpegQuality());
|
|
break;
|
|
case CCmdLineProcessor::fmt_tiff:
|
|
nFileCountOut = 1; // all pages in one file
|
|
pOut = new CTIFFFilter(nPageCount,
|
|
proc.getTiffCompression(), modTime);
|
|
break;
|
|
case CCmdLineProcessor::fmt_tiff_single_pages:
|
|
pOut = new CTIFFFilter(nPageCount,
|
|
proc.getTiffCompression(), modTime);
|
|
break;
|
|
}
|
|
|
|
CFile fileOut;
|
|
if (bStdOut) {
|
|
fileOut.OpenTemp();
|
|
pOut->Init(&fileOut);
|
|
}
|
|
|
|
for (int nPage = 0; nPage < nPageCount; nPage++)
|
|
{
|
|
TSFFPage* pPage = pInfile->GetPage(nPage);
|
|
if (pPage->height <= 0) {
|
|
if (!bQuiet) {
|
|
cout << "Skipping page (no lines)." << endl;
|
|
}
|
|
continue;
|
|
}
|
|
if (!bStdOut)
|
|
{
|
|
if (pathOutFileName.string().length()) {
|
|
// A fixed name was given, so use it as a base name
|
|
outPath = pathOutFileName;
|
|
std::string orgExt = outPath.extension().generic_string();
|
|
if (nFileCountOut > 1) {
|
|
outPath.replace_extension("");
|
|
ssFilename << outPath.generic_string();
|
|
ssFilename << "_" << setw(3) << setfill('0') << nPage+1;
|
|
ssFilename << orgExt;
|
|
outPath = fs::path(ssFilename.str());
|
|
}
|
|
} else {
|
|
// Otherwise construct output filename from input filename
|
|
outPath = pathOutDirectory / pathInFileName.filename();
|
|
if (nFileCountOut > 1) {
|
|
outPath.replace_extension("");
|
|
ssFilename << outPath.generic_string();
|
|
ssFilename << "_" << setw(3) << setfill('0') << nPage+1;
|
|
ssFilename << pOut->GetExtension();
|
|
outPath = fs::path(ssFilename.str());
|
|
} else {
|
|
outPath.replace_extension(pOut->GetExtension());
|
|
}
|
|
}
|
|
if (!proc.doOverwrite() && !((nPage > 0) && (nFileCountOut == 1)) && fs::exists(outPath)) {
|
|
throw CSimpleException(CSimpleException::err_outfileexists);
|
|
}
|
|
ssFilename.str("");
|
|
ssFilename.clear();
|
|
}
|
|
|
|
bool bIsLowRes = pInfile->IsLowRes(nPage);
|
|
bool bDoubleLines = !proc.keepVRes() && bIsLowRes;
|
|
|
|
if (!bQuiet) {
|
|
if (!((nPage > 0) && (nFileCountOut == 1))) {
|
|
cout << "- Destination File " << outPath.string() << " : " << endl;
|
|
}
|
|
cout << " Converting page " << nPage+1
|
|
<< " (" << pPage->width << "x" << pPage->height << "px / ";
|
|
cout << pPage->dpi << "x" << pPage->lpi << "dpi), ";
|
|
cout << (bIsLowRes ? "LowRes" : "HiRes") << " ..." << endl;
|
|
}
|
|
|
|
if (!pInfile->SeekPage(nPage)) {
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
}
|
|
|
|
if (!bStdOut && !((nPage > 0) && (nFileCountOut == 1))) {
|
|
fileOut.Open(outPath.string(), "wb+");
|
|
pOut->Init(&fileOut);
|
|
}
|
|
|
|
if (bDoubleLines) {
|
|
pOut->BeginPage(nPage, pPage->width,
|
|
pPage->height*2, pPage->dpi, pPage->lpi*2);
|
|
} else {
|
|
pOut->BeginPage(nPage, pPage->width,
|
|
pPage->height, pPage->dpi, pPage->lpi);
|
|
}
|
|
|
|
pOut->BlankLine();
|
|
|
|
TSFFRecord rec;
|
|
|
|
while (pInfile->GetRecord(rec))
|
|
{
|
|
switch(rec.type)
|
|
{
|
|
case NORMAL :
|
|
pOut->BlankLine();
|
|
if (pInfile->DecodeRecord(rec, pOut->GetBitSink())) {
|
|
pOut->WriteLine();
|
|
if (bDoubleLines) {
|
|
pOut->WriteLine();
|
|
}
|
|
}
|
|
if (rec.pData != 0) free(rec.pData);
|
|
break;
|
|
case USERINFO :
|
|
// not yet considered
|
|
if (rec.pData != 0) free(rec.pData);
|
|
break;
|
|
case BADLINE :
|
|
pOut->WriteLine();
|
|
if (bDoubleLines) {
|
|
pOut->WriteLine();
|
|
}
|
|
break;
|
|
case WHITESKIP :
|
|
pOut->BlankLine();
|
|
for (int j=0; j < rec.cb; ++j) {
|
|
pOut->WriteLine();
|
|
if (bDoubleLines) {
|
|
pOut->WriteLine();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
pOut->EndPage();
|
|
|
|
if (!bStdOut) {
|
|
if ((nFileCountOut > 1) || ((nFileCountOut == 1) && (nPage == nPageCount-1))) {
|
|
pOut->Finalize();
|
|
fileOut.Close();
|
|
if (proc.keepDate()) {
|
|
fileOut.SetModificationTime(modTime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bStdOut) {
|
|
fileOut.DumpToStdOut();
|
|
fileOut.Close();
|
|
if (pOut) {
|
|
pOut->Finalize();
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (const std::exception& e) {
|
|
cerr << "ERROR: " << pathInFileName.string() << ": " << e.what() << endl;
|
|
rc = 2;
|
|
}
|
|
catch (const CSimpleException& e) {
|
|
cerr << "ERROR: " << pathInFileName.string() << ": " << e.what() << endl;
|
|
rc = 2;
|
|
}
|
|
if (pOut) {
|
|
delete pOut;
|
|
}
|
|
if (pInfile) {
|
|
delete pInfile;
|
|
}
|
|
if (!bQuiet) cout << endl;
|
|
}
|
|
if (!bQuiet) cout << "Finished. " << endl << endl;
|
|
}
|
|
catch (const std::exception& e) {
|
|
cerr << "ERROR: " << e.what() << endl;
|
|
rc = 2;
|
|
}
|
|
catch (const CSimpleException& e) {
|
|
cerr << "ERROR: " << e.what() << endl;
|
|
rc = 2;
|
|
}
|
|
return rc;
|
|
}
|