From 1145733c29db0a678537ce99ff60e21613f622a8 Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Fri, 6 Jan 2023 10:02:49 +0200 Subject: Import iscan 2.30.4-2 --- lib/Makefile.am | 61 +++++++ lib/basic-imgstream.cc | 301 +++++++++++++++++++++++++++++++++ lib/basic-imgstream.hh | 144 ++++++++++++++++ lib/fax-encoder.cc | 411 ++++++++++++++++++++++++++++++++++++++++++++++ lib/fax-encoder.hh | 58 +++++++ lib/file-opener.cc | 321 ++++++++++++++++++++++++++++++++++++ lib/file-opener.hh | 101 ++++++++++++ lib/imgstream.cc | 142 ++++++++++++++++ lib/imgstream.hh | 100 +++++++++++ lib/jpegstream.cc | 270 ++++++++++++++++++++++++++++++ lib/jpegstream.hh | 118 +++++++++++++ lib/pcxstream.cc | 320 ++++++++++++++++++++++++++++++++++++ lib/pcxstream.hh | 93 +++++++++++ lib/pdf/Makefile.am | 44 +++++ lib/pdf/array.cc | 115 +++++++++++++ lib/pdf/array.hh | 87 ++++++++++ lib/pdf/dictionary.cc | 123 ++++++++++++++ lib/pdf/dictionary.hh | 93 +++++++++++ lib/pdf/object.cc | 103 ++++++++++++ lib/pdf/object.hh | 120 ++++++++++++++ lib/pdf/primitive.cc | 100 +++++++++++ lib/pdf/primitive.hh | 92 +++++++++++ lib/pdf/writer.cc | 236 ++++++++++++++++++++++++++ lib/pdf/writer.hh | 165 +++++++++++++++++++ lib/pdfstream.cc | 351 +++++++++++++++++++++++++++++++++++++++ lib/pdfstream.hh | 105 ++++++++++++ lib/pngstream.cc | 257 +++++++++++++++++++++++++++++ lib/pngstream.hh | 145 ++++++++++++++++ lib/pnmstream.cc | 112 +++++++++++++ lib/pnmstream.hh | 73 ++++++++ lib/tests/Makefile.am | 46 ++++++ lib/tests/even-width.pbm | Bin 0 -> 2427 bytes lib/tests/even-width.pgm | Bin 0 -> 19343 bytes lib/tests/even-width.ppm | Bin 0 -> 57999 bytes lib/tests/odd-width.pbm | Bin 0 -> 2612 bytes lib/tests/odd-width.pgm | Bin 0 -> 19752 bytes lib/tests/odd-width.ppm | Bin 0 -> 59226 bytes lib/tests/pnm.c | 141 ++++++++++++++++ lib/tests/pnm.h | 62 +++++++ lib/tests/run-test-pcx.sh | 76 +++++++++ lib/tests/test-pcx.cc | 85 ++++++++++ lib/tiffstream.cc | 268 ++++++++++++++++++++++++++++++ lib/tiffstream.hh | 105 ++++++++++++ 43 files changed, 5544 insertions(+) create mode 100644 lib/Makefile.am create mode 100644 lib/basic-imgstream.cc create mode 100644 lib/basic-imgstream.hh create mode 100644 lib/fax-encoder.cc create mode 100644 lib/fax-encoder.hh create mode 100644 lib/file-opener.cc create mode 100644 lib/file-opener.hh create mode 100644 lib/imgstream.cc create mode 100644 lib/imgstream.hh create mode 100644 lib/jpegstream.cc create mode 100644 lib/jpegstream.hh create mode 100644 lib/pcxstream.cc create mode 100644 lib/pcxstream.hh create mode 100644 lib/pdf/Makefile.am create mode 100644 lib/pdf/array.cc create mode 100644 lib/pdf/array.hh create mode 100644 lib/pdf/dictionary.cc create mode 100644 lib/pdf/dictionary.hh create mode 100644 lib/pdf/object.cc create mode 100644 lib/pdf/object.hh create mode 100644 lib/pdf/primitive.cc create mode 100644 lib/pdf/primitive.hh create mode 100644 lib/pdf/writer.cc create mode 100644 lib/pdf/writer.hh create mode 100644 lib/pdfstream.cc create mode 100644 lib/pdfstream.hh create mode 100644 lib/pngstream.cc create mode 100644 lib/pngstream.hh create mode 100644 lib/pnmstream.cc create mode 100644 lib/pnmstream.hh create mode 100644 lib/tests/Makefile.am create mode 100644 lib/tests/even-width.pbm create mode 100644 lib/tests/even-width.pgm create mode 100644 lib/tests/even-width.ppm create mode 100644 lib/tests/odd-width.pbm create mode 100644 lib/tests/odd-width.pgm create mode 100644 lib/tests/odd-width.ppm create mode 100644 lib/tests/pnm.c create mode 100644 lib/tests/pnm.h create mode 100755 lib/tests/run-test-pcx.sh create mode 100644 lib/tests/test-pcx.cc create mode 100644 lib/tiffstream.cc create mode 100644 lib/tiffstream.hh (limited to 'lib') diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..922ca44 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,61 @@ +## Makefile.am -- an automake template for a Makefile.in file +## Copyright (C) 2004 Olaf Meeuwissen +## +## This file is part of the "Image Scan!" build infra-structure. +## +## The "Image Scan!" build infra-structure is free software. +## You can redistribute it and/or modify it under the terms of the GNU +## General Public License as published by the Free Software Foundation; +## either version 2 of the License or at your option any later version. +## +## This program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +## FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +## See the GNU General Public License for more details. +## +## You should have received a verbatim copy of the GNU General Public +## License along with this program; if not, write to: +## +## Free Software Foundation, Inc. +## 59 Temple Place, Suite 330 +## Boston, MA 02111-1307 USA + + +SUBDIRS = \ + pdf \ + tests + +if ENABLE_FRONTEND +noinst_LTLIBRARIES = libimage-stream.la +libimage_stream_la_CPPFLAGS = -I$(top_srcdir)/include +libimage_stream_la_LDFLAGS = -static +libimage_stream_la_LIBADD = \ + $(LIBLTDL) \ + $(top_builddir)/lib/pdf/libpdf.la +libimage_stream_la_SOURCES = \ + $(libimage_stream_la_files) +endif +libimage_stream_la_files = \ + basic-imgstream.cc \ + basic-imgstream.hh \ + fax-encoder.cc \ + fax-encoder.hh \ + file-opener.cc \ + file-opener.hh \ + imgstream.cc \ + imgstream.hh \ + jpegstream.cc \ + jpegstream.hh \ + pcxstream.cc \ + pcxstream.hh \ + pdfstream.cc \ + pdfstream.hh \ + pngstream.cc \ + pngstream.hh \ + pnmstream.cc \ + pnmstream.hh \ + tiffstream.cc \ + tiffstream.hh + +EXTRA_DIST = \ + $(libimage_stream_la_files) diff --git a/lib/basic-imgstream.cc b/lib/basic-imgstream.cc new file mode 100644 index 0000000..9b7aea8 --- /dev/null +++ b/lib/basic-imgstream.cc @@ -0,0 +1,301 @@ +// basic-imgstream.cc -- the mother of all image streams +// Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "basic-imgstream.hh" + +#include +#include + +namespace iscan +{ + +#if __GLIBC_PREREQ(2, 10) + typedef const dirent** dirtype; +#else + typedef const void* dirtype; +#endif + + basic_imgstream::basic_imgstream (void) + : _h_sz (0), _v_sz (0), _hres (0), _vres (0), _bits (0), _cspc (NONE) + { + } + + basic_imgstream::~basic_imgstream (void) + { + } + + basic_imgstream& + basic_imgstream::flush (void) + { + return *this; + } + + basic_imgstream& + basic_imgstream::size (size_type h_sz, size_type v_sz) + { + _h_sz = h_sz; + _v_sz = v_sz; + return *this; + } + + basic_imgstream& + basic_imgstream::resolution (size_type hres, size_type vres) + { + _hres = hres; + _vres = vres; + return *this; + } + + basic_imgstream& + basic_imgstream::depth (size_type bits) + { + _bits = bits; + return *this; + } + + basic_imgstream& + basic_imgstream::colour (colour_space space) + { + _cspc = space; + return *this; + } + + + basic_imgstream::dl_handle + basic_imgstream::dlopen (const char *libname, + bool (*validate) (lt_dlhandle)) + { + if (0 != lt_dlinit ()) + { + throw runtime_error (lt_dlerror ()); + } + + dl_handle lib = find_dlopen (libname, validate); + if (!lib) + { + lt_dlexit (); + throw runtime_error ("no usable library found"); + } + + return lib; + } + + basic_imgstream::dl_ptr + basic_imgstream::dlsym (dl_handle lib, const char *funcname) + { + return lt_dlsym (lib, funcname); + } + + int + basic_imgstream::dlclose (dl_handle lib) + { + return lt_dlclose (lib); + } + + // forward declarations + static int reversionsort (dirtype, dirtype); + int selector (const dirent *); + + //! + /*! A number of distributions seems to have switched to a policy where + the lib*.so files are provided by their -devel packages. Moreover, + the typical workstation install does not include such packages and + lt_dlopenext() will understandably have trouble finding your lib*. + + This function is a fallback for such cases. It will look for your + library in the exact same places as lt_dlopenext(), but with this + difference that it will try to open any file that matches lib*.so, + starting with the one with the highest version number. + + Actually, it is just as smart lt_dlopenext() and uses the correct + shared library extension for your platform. However, it does not + try libtool's .la extension. + + The general policy for memory allocation and access problems is to + ignore them and muddle on or return the current result rightaway. + + This function returns NULL if no suitable library could be found + and a handle to library otherwise. + */ + basic_imgstream::dl_handle + basic_imgstream::find_dlopen (const char *libname, + bool (*validate) (lt_dlhandle)) + { + using std::bad_alloc; + using std::string; + + dl_handle result = NULL; + + try + { // prepping the selector() + char *name = new char[strlen (libname) + + strlen (LT_MODULE_EXT) + 1]; + name = strcpy (name, libname); + name = strcat (name, LT_MODULE_EXT); + + _libname = name; // deleting _libname below, never mind + // that name goes out of scope here + } + catch (bad_alloc& oops) + { + return result; + } + + char *pathz = NULL; + size_t length = 0; + bool is_pathz_ok = true; + { // set up a library search path like + // that used by lt_dlopen() + int delimiter = ':'; + + const char *path = NULL; + + if ((path = lt_dlgetsearchpath ()) + && 0 != argz_add_sep (&pathz, &length, path, delimiter)) + { + is_pathz_ok = false; + } + if ((path = getenv ("LTDL_LIBRARY_PATH")) + && 0 != argz_add_sep (&pathz, &length, path, delimiter)) + { + is_pathz_ok = false; + } + if ((path = getenv (LT_MODULE_PATH_VAR)) + && 0 != argz_add_sep (&pathz, &length, path, delimiter)) + { + is_pathz_ok = false; + } + if ("x86_64" == string (ISCAN_HOST_CPU) + && (path = "/usr/local/lib64:/usr/lib64:/lib64") + && 0 != argz_add_sep (&pathz, &length, path, delimiter)) + { + is_pathz_ok = false; + } + // Kludge for multiarch support introduced in Ubuntu 11.04 + if ("x86_64" == string (ISCAN_HOST_CPU)) + { + if ((path = "/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu") + && 0 != argz_add_sep (&pathz, &length, path, delimiter)) + { + is_pathz_ok = false; + } + } + else + { + if ((path = "/usr/lib/i386-linux-gnu:/lib/i386-linux-gnu") + && 0 != argz_add_sep (&pathz, &length, path, delimiter)) + { + is_pathz_ok = false; + } + } + if ((path = LT_DLSEARCH_PATH) + && 0 != argz_add_sep (&pathz, &length, path, delimiter)) + { + is_pathz_ok = false; + } + } + + if (is_pathz_ok) + { // go fetch! + const char *dir_name = NULL; + while (!result + && (dir_name = argz_next (pathz, length, dir_name))) + { + struct dirent **match = NULL; + int count = scandir (dir_name, &match, selector, reversionsort); + + for (int i = 0; !result && i < count; ++i) + { + + const char *file_name = match[i]->d_name; + try + { + char *abs_file_name + = new char[strlen (dir_name) + strlen ("/") + + strlen (file_name) + 1]; + strcpy (abs_file_name, dir_name); + strcat (abs_file_name, "/"); + strcat (abs_file_name, file_name); + + result = lt_dlopen (abs_file_name); + if (validate && !validate (result)) + { + lt_dlclose (result); + result = NULL; + } + delete [] abs_file_name; + } + catch (bad_alloc& oops) + { + // just ignore and continue with the next match + } + free (match[i]); + } + if (match) free (match); // malloc'd by scandir() + } + } + + delete [] _libname; // we new'd a name for our selector() + free (pathz); // malloc'd by argz_add_sep() + + return result; + } + + //! Library name we are looking for. + /*! The scandir() API does not allow for passing arbitrary data to the + selector(). We use this variable to work around that limitation. + + Note that this makes users of selector() thread unsafe. + */ + const char *basic_imgstream::_libname = NULL; + + //! Selects relevant library filenames. + /*! Returns true if the leading part of the directory entry's file + name matches the library name we are looking for. The file name + may contain trailing version information which is ignored. + */ + int + selector (const dirent *dir) + { + return (0 == strncmp (dir->d_name, basic_imgstream::_libname, + strlen (basic_imgstream::_libname))); + } + + //! The C library's versionsort() function in reverse. + static int + reversionsort (dirtype a, dirtype b) + { + return versionsort (b, a); + } + +} // namespace iscan diff --git a/lib/basic-imgstream.hh b/lib/basic-imgstream.hh new file mode 100644 index 0000000..f55bc60 --- /dev/null +++ b/lib/basic-imgstream.hh @@ -0,0 +1,144 @@ +// basic-imgstream.hh -- the mother of all image streams +// Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifndef iscan_basic_imgstream_hh_included +#define iscan_basic_imgstream_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" + +#ifndef LT_MODULE_EXT +#define LT_MODULE_EXT LTDL_SHLIB_EXT +#endif + +#ifndef LT_MODULE_PATH_VAR +#define LT_MODULE_PATH_VAR LTDL_SHLIBPATH_VAR +#endif + +#ifndef LT_DLSEARCH_PATH +#define LT_DLSEARCH_PATH LTDL_SYSSEARCHPATH +#endif + +#endif + +#include +#include +#include + +namespace iscan +{ + using std::runtime_error; + + enum colour_space + { + NONE, + + monochrome, + mono = monochrome, + + grayscale, + greyscale = grayscale, + gray = grayscale, + grey = grayscale, + + RGB, + RGB_alpha + }; + + class basic_imgstream + { + public: + typedef char byte_type; + typedef size_t size_type; + + virtual ~basic_imgstream (void); + + virtual basic_imgstream& write (const byte_type *line, size_type n) = 0; + virtual basic_imgstream& flush (void); + + virtual basic_imgstream& size (size_type h_sz, size_type v_sz); + virtual basic_imgstream& resolution (size_type hres, size_type vres); + virtual basic_imgstream& depth (size_type bits); + virtual basic_imgstream& colour (colour_space space); + virtual void rotate_180 (bool yes) {}; + + protected: + basic_imgstream (void); + + size_type _h_sz; + size_type _v_sz; + size_type _hres; + size_type _vres; + + size_type _bits; + colour_space _cspc; + + private: // undefined to prevent copying + basic_imgstream (const basic_imgstream&); + basic_imgstream& operator= (const basic_imgstream&); + + + // API to deal with external image format libraries + + protected: + typedef lt_dlhandle dl_handle; + typedef lt_ptr dl_ptr; + + static dl_handle dlopen (const char *libname, + bool (*validate) (dl_handle) = NULL); + static dl_ptr dlsym (dl_handle lib, const char *funcname); + static int dlclose (dl_handle lib); + + private: + static dl_handle find_dlopen (const char *libname, + bool (*validate) (dl_handle)); + static const char *_libname; + + friend int selector (const dirent *); + +#ifdef __GNUC__ +#define fundecl(returntype,funcname,arglist...) \ + typedef returntype (*funcname##_f) (arglist); \ + funcname##_f funcname; +#else +#error "Your compiler is not known to support macros with a variable" +#error "number of arguments. In case it does, please report this to" +#error "the library maintainers and include a suitable preprocessor" +#error "check for them to add. A patch will be most appreciated." +#endif + + }; + +} // namespace iscan + +#endif /* !defined (iscan_basic_imgstream_hh_included) */ diff --git a/lib/fax-encoder.cc b/lib/fax-encoder.cc new file mode 100644 index 0000000..90abe8c --- /dev/null +++ b/lib/fax-encoder.cc @@ -0,0 +1,411 @@ +// fax-encoder.cc -- convert scanlines to fascimile format +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fax-encoder.hh" + +#include +#include + +#define WHITE false +#define BLACK true + +namespace iscan +{ + using namespace std; + + struct code { + unsigned int bits; + unsigned int code; + }; + + static const size_t g3_make_up_min = 64; + static const size_t g3_make_up_inc = 64; + static const size_t g3_extra_make_up_min = 1792; + static const size_t g3_extra_make_up_max = 2560; + + //! Terminating codes for white runs of less than 64 pixels. + static const struct code g3_white_terminal[] = + { + { 8, 0x35 }, + { 6, 0x07 }, + { 4, 0x07 }, + { 4, 0x08 }, + { 4, 0x0b }, + { 4, 0x0c }, + { 4, 0x0e }, + { 4, 0x0f }, + { 5, 0x13 }, + { 5, 0x14 }, + { 5, 0x07 }, + { 5, 0x08 }, + { 6, 0x08 }, + { 6, 0x03 }, + { 6, 0x34 }, + { 6, 0x35 }, + { 6, 0x2a }, + { 6, 0x2b }, + { 7, 0x27 }, + { 7, 0x0c }, + { 7, 0x08 }, + { 7, 0x17 }, + { 7, 0x03 }, + { 7, 0x04 }, + { 7, 0x28 }, + { 7, 0x2b }, + { 7, 0x13 }, + { 7, 0x24 }, + { 7, 0x18 }, + { 8, 0x02 }, + { 8, 0x03 }, + { 8, 0x1a }, + { 8, 0x1b }, + { 8, 0x12 }, + { 8, 0x13 }, + { 8, 0x14 }, + { 8, 0x15 }, + { 8, 0x16 }, + { 8, 0x17 }, + { 8, 0x28 }, + { 8, 0x29 }, + { 8, 0x2a }, + { 8, 0x2b }, + { 8, 0x2c }, + { 8, 0x2d }, + { 8, 0x04 }, + { 8, 0x05 }, + { 8, 0x0a }, + { 8, 0x0b }, + { 8, 0x52 }, + { 8, 0x53 }, + { 8, 0x54 }, + { 8, 0x55 }, + { 8, 0x24 }, + { 8, 0x25 }, + { 8, 0x58 }, + { 8, 0x59 }, + { 8, 0x5a }, + { 8, 0x5b }, + { 8, 0x4a }, + { 8, 0x4b }, + { 8, 0x32 }, + { 8, 0x33 }, + { 8, 0x34 }, + }; + + //! Terminating codes for black runs of less than 64 pixels. + static const struct code g3_black_terminal[] = + { + { 10, 0x37 }, + { 3, 0x02 }, + { 2, 0x03 }, + { 2, 0x02 }, + { 3, 0x03 }, + { 4, 0x03 }, + { 4, 0x02 }, + { 5, 0x03 }, + { 6, 0x05 }, + { 6, 0x04 }, + { 7, 0x04 }, + { 7, 0x05 }, + { 7, 0x07 }, + { 8, 0x04 }, + { 8, 0x07 }, + { 9, 0x18 }, + { 10, 0x17 }, + { 10, 0x18 }, + { 10, 0x08 }, + { 11, 0x67 }, + { 11, 0x68 }, + { 11, 0x6c }, + { 11, 0x37 }, + { 11, 0x28 }, + { 11, 0x17 }, + { 11, 0x18 }, + { 12, 0xca }, + { 12, 0xcb }, + { 12, 0xcc }, + { 12, 0xcd }, + { 12, 0x68 }, + { 12, 0x69 }, + { 12, 0x6a }, + { 12, 0x6b }, + { 12, 0xd2 }, + { 12, 0xd3 }, + { 12, 0xd4 }, + { 12, 0xd5 }, + { 12, 0xd6 }, + { 12, 0xd7 }, + { 12, 0x6c }, + { 12, 0x6d }, + { 12, 0xda }, + { 12, 0xdb }, + { 12, 0x54 }, + { 12, 0x55 }, + { 12, 0x56 }, + { 12, 0x57 }, + { 12, 0x64 }, + { 12, 0x65 }, + { 12, 0x52 }, + { 12, 0x53 }, + { 12, 0x24 }, + { 12, 0x37 }, + { 12, 0x38 }, + { 12, 0x27 }, + { 12, 0x28 }, + { 12, 0x58 }, + { 12, 0x59 }, + { 12, 0x2b }, + { 12, 0x2c }, + { 12, 0x5a }, + { 12, 0x66 }, + { 12, 0x67 }, + }; + + //! Make up codes for white runs of 64 to 1728 + 63 pixels. + static const struct code g3_white_make_up[] = + { // runlength 64 + index * 64 + { 5, 0x1b }, + { 5, 0x12 }, + { 6, 0x17 }, + { 7, 0x37 }, + { 8, 0x36 }, + { 8, 0x37 }, + { 8, 0x64 }, + { 8, 0x65 }, + { 8, 0x68 }, + { 8, 0x67 }, + { 9, 0xcc }, + { 9, 0xcd }, + { 9, 0xd2 }, + { 9, 0xd3 }, + { 9, 0xd4 }, + { 9, 0xd5 }, + { 9, 0xd6 }, + { 9, 0xd7 }, + { 9, 0xd8 }, + { 9, 0xd9 }, + { 9, 0xda }, + { 9, 0xdb }, + { 9, 0x98 }, + { 9, 0x99 }, + { 9, 0x9a }, + { 6, 0x18 }, + { 9, 0x9b }, + }; + + //! Make up codes for black runs of 64 to 1728 + 63 pixels. + static const struct code g3_black_make_up[] = + { // runlength 64 + index * 64 + { 10, 0x0f }, + { 12, 0xc8 }, + { 12, 0xc9 }, + { 12, 0x5b }, + { 12, 0x33 }, + { 12, 0x34 }, + { 12, 0x35 }, + { 13, 0x6c }, + { 13, 0x6d }, + { 13, 0x4a }, + { 13, 0x4b }, + { 13, 0x4c }, + { 13, 0x4d }, + { 13, 0x72 }, + { 13, 0x73 }, + { 13, 0x74 }, + { 13, 0x75 }, + { 13, 0x76 }, + { 13, 0x77 }, + { 13, 0x52 }, + { 13, 0x53 }, + { 13, 0x54 }, + { 13, 0x55 }, + { 13, 0x5a }, + { 13, 0x5b }, + { 13, 0x64 }, + { 13, 0x65 }, + }; + + //! Additional make up codes for run of more than 1792 pixels. + static const struct code g3_extra_make_up[] = + { // runlength 1792 + index * 64 + { 11, 0x08 }, + { 11, 0x0c }, + { 11, 0x0d }, + { 12, 0x12 }, + { 12, 0x13 }, + { 12, 0x14 }, + { 12, 0x15 }, + { 12, 0x16 }, + { 12, 0x17 }, + { 12, 0x1c }, + { 12, 0x1d }, + { 12, 0x1e }, + { 12, 0x1f }, + }; + + static string transform (vector& runs); + + //! Converts a packed \a line of pixels into FAX G3 encoded scanline. + /*! This functions merely collects the run lengths into a vector and + passes that vector off to transform(). + */ + string + fax_encoder::operator() (const byte_type* line, size_type n) + { + bool colour = WHITE; + size_t length = 0; + vector runs; + + uint8_t bit = 0x80; + + n *= 8; + + while (0 < n--) + { + if (colour == bool ((*line | ~bit) & bit)) + { + ++length; + } + else + { + runs.push_back (length); + colour = (WHITE == colour ? BLACK : WHITE); + length = 1; + } + + bit >>= 1; + if (0 == bit) + { + bit = 0x80; + ++line; + } + } + runs.push_back (length); + + return transform (runs); + } + + //! Converts a vector of run lengths into a FAX G3 encoded string. + /*! The string always starts with an end-of-line marker and will be + filled if necessary. + */ + static string + transform (vector& runs) + { + vector::iterator it = runs.begin (); + bool colour = WHITE; + + string result; + unsigned char ch = 0x00; + size_t i = 0; + + unsigned int mask = 1 << 11; + unsigned int code = 1; + + while (mask) + { + if (code & mask) ch |= (1 << (7 - i % 8)); + ++i; + mask >>= 1; + if (0 == i % 8) + { + result.push_back (ch); + ch = 0x00; + } + } + + while (runs.end () != it) + { + bool terminal = false; + do + { + const struct code *c = NULL; + + if (g3_extra_make_up_max <= *it) + { + size_t index = ((g3_extra_make_up_max - g3_extra_make_up_min) + / g3_make_up_inc); + + c = g3_extra_make_up + index; + *it -= g3_extra_make_up_max; + } + else if (g3_extra_make_up_min <= *it) + { + size_t index = (*it - g3_extra_make_up_min) / g3_make_up_inc; + + c = g3_extra_make_up + index; + *it -= g3_extra_make_up_min + index * g3_make_up_inc; + } + else if (g3_make_up_min <= *it) + { + size_t index = (*it - g3_make_up_min) / g3_make_up_inc; + + c = ((WHITE == colour ? g3_white_make_up : g3_black_make_up) + + index); + *it -= g3_make_up_min + index * g3_make_up_inc; + } + else + { + c = ((WHITE == colour ? g3_white_terminal : g3_black_terminal) + + *it); + *it = 0; + terminal = true; + } + + unsigned int mask = 1 << (c->bits - 1); + unsigned int code = c->code; + + while (mask) + { + if (code & mask) ch |= (1 << (7 - i % 8)); + ++i; + mask >>= 1; + if (0 == i % 8) + { + result.push_back (ch); + ch = 0x00; + } + } + } + while (!terminal); + + ++it; + colour = (WHITE == colour ? BLACK : WHITE); + } + if (0 != i % 8) + result.push_back (ch); + + return result; + } + +} // namespace iscan diff --git a/lib/fax-encoder.hh b/lib/fax-encoder.hh new file mode 100644 index 0000000..4ae6f38 --- /dev/null +++ b/lib/fax-encoder.hh @@ -0,0 +1,58 @@ +// fax-encoder.hh -- convert scanlines to fascimile format +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifndef iscan_fax_encoder_hh_included +#define iscan_fax_encoder_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "basic-imgstream.hh" + +namespace iscan +{ + using std::string; + + class fax_encoder + { + public: + typedef basic_imgstream::byte_type byte_type; + typedef basic_imgstream::size_type size_type; + + string operator() (const byte_type* line, size_type n); + }; + +} // namespace iscan + +#endif /* !defined (iscan_fax_encoder_hh_included) */ diff --git a/lib/file-opener.cc b/lib/file-opener.cc new file mode 100644 index 0000000..c0ccdd8 --- /dev/null +++ b/lib/file-opener.cc @@ -0,0 +1,321 @@ +// file-opener.cc -- dealing with files when doing multi-image scans +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "file-opener.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace iscan +{ + static string tempfile (const string& dirname = string ()); + + //! Opening one or more files in a temporary file location. + file_opener::file_opener (bool collate) + : _collate (collate), _filename (string ()), _tempfile (string ()), + _fp (NULL), _pattern (NULL) + { + } + + //! Opening a file by \a name. + file_opener::file_opener (const string& name) + : _collate (true), _filename (string ()), _tempfile (string ()), + _fp (NULL), _pattern (NULL) + { + common_init (name); + } + + //! Opening files following a naming \a pattern. + file_opener::file_opener (const string& pattern, unsigned int start_index) + : _collate (false), _filename (string ()), _tempfile (string ()), + _fp (NULL), _pattern (NULL) + { + common_init (pattern); + + string::size_type hash = _pattern->basename.find_last_not_of (hash_mark); + + if (string::npos == hash) + { + delete _pattern; // new'd in common_init() + throw std::invalid_argument ("invalid file name pattern"); + } + ++hash; // first hash_mark + + _pattern->digits = _pattern->basename.length () - hash; + _pattern->basename = _pattern->basename.substr (0, hash); + _pattern->index = start_index; + } + + file_opener::~file_opener (void) + { + if (_fp) close (); + if (_tempfile != _filename) + rename (); + delete _pattern; + } + + //! Returns the C \c FILE pointer associated with a name(). + file_opener::operator FILE * (void) + { + if (_filename.empty ()) set_names (); + if (!_fp) open (); + return _fp; + } + + //! Returns the pathname of the output destination. + const string& + file_opener::name (void) const + { + if (_filename.empty ()) + const_cast (this)->set_names (); + return _filename; + } + + //! Returns the pathname of the interim output destination. + /*! \deprecated Flaming hack to quickly fix our TIFF support. + */ + const string& + file_opener::temp (void) const + { + if (_filename.empty ()) + const_cast (this)->set_names (); + return _tempfile; + } + + //! Returns the file's extension (without extension separator). + string + file_opener::extension (void) const + { + if (!_pattern) return null_ext; + return _pattern->extension.substr (ext_sep.size ()); + } + + //! Opens the next file, unless the file_opener is_collating(). + file_opener& + file_opener::operator++ (void) + { + if (_collate) return *this; + + if (_fp) close (); + if (_tempfile != _filename) + rename (); + set_names (true); + open (); + + return *this; + } + + //! Tells whether or not output will be collated. + bool + file_opener::is_collating (void) const + { + return _collate; + } + + //! Removes the output destination. + void + file_opener::remove (void) + { + if (_fp) + { + close (); + if (0 != ::remove (_tempfile.c_str ())) + throw std::ios_base::failure (strerror (errno)); + } + _tempfile = string (); + _filename = string (); + } + + const string file_opener::dir_sep = "/"; + const string file_opener::ext_sep = "."; + const char file_opener::hash_mark = '#'; + + const string file_opener::null_ext = string (); + + //! Handles common part of the constructors taking a string. + void + file_opener::common_init (const string& s) + { + string::size_type sep = s.rfind (dir_sep); + string::size_type dot = s.rfind (ext_sep); + + if (string::npos != sep && dot < sep) + dot = string::npos; + + _pattern = new struct pattern; + if (string::npos != sep) + { + ++sep; + _pattern->dirname = s.substr (0, sep); + _pattern->basename = s.substr (sep, dot - sep); + } + else + { + _pattern->dirname = string (); + _pattern->basename = s.substr (0, dot); + } + + if (string::npos != dot) + { + _pattern->extension = s.substr (dot); + } + else + { + _pattern->extension = null_ext; + } + _pattern->digits = 0; + _pattern->index = 0; + } + + void + file_opener::set_names (bool next) + { + using std::stringstream; + using std::setfill; + using std::setw; + + if (!_pattern) + { + _tempfile = tempfile (); + _filename = _tempfile; + } + else + { + if (next) ++_pattern->index; + + stringstream ss; + ss << _pattern->dirname + << _pattern->basename; + if (_pattern->digits) + { + ss << setfill ('0') + << setw (_pattern->digits) + << _pattern->index; + } + ss << _pattern->extension; + + _filename = ss.str (); + _tempfile = (!_pattern->digits + ? _filename : tempfile (_pattern->dirname.empty () + ? "." : _pattern->dirname)); + } + } + + //! Error handling wrapper around the C fopen() call. + void + file_opener::open (void) + { + _fp = fopen (_tempfile.c_str (), "wb"); + if (!_fp) + throw std::ios_base::failure (strerror (errno)); + } + + //! Error handling wrapper around the C fclose() call. + void + file_opener::close (void) + { + if (!_fp) return; + + int rv = fclose (_fp); + _fp = NULL; + + if (0 != rv) + throw std::ios_base::failure (strerror (errno)); + } + + void + file_opener::rename (void) + { + if (0 != ::rename (_tempfile.c_str (), _filename.c_str ())) + throw std::ios_base::failure (strerror (errno)); + + _tempfile = _filename; + } + + + //! Creates a temporary file in a secure way. + static string + tempfile (const string& dirname) + { + using std::list; + + list dirs; + if (!dirname.empty ()) dirs.push_back (dirname); + if (getenv ("TMPDIR")) dirs.push_back (getenv ("TMPDIR")); +#ifdef P_tmpdir + dirs.push_back (P_tmpdir); // C library default +#endif + dirs.push_back ("/tmp"); // last resort + + string filename; + list::iterator it = dirs.begin (); + + while (dirs.end () != it && filename.empty ()) + { + string ts = (*it + file_opener::dir_sep + + (!dirname.empty () ? "." : "") + + PACKAGE_TARNAME "XXXXXX"); + char *tc = new char [ts.length() + 1]; + + ts.copy (tc, ts.length ()); + tc[ts.length ()] = '\0'; + + mode_t um = umask (0); + umask (dirname.empty () ? 0077 : um); // use safe permissions + if (0 <= mkstemp (tc)) + filename = tc; + umask (um); + + // mkstemp from glibc 2.0.7 and later always uses 0600, (try + // to) revert here + if (!dirname.empty ()) + { + chmod (filename.c_str (), ( S_IRUSR | S_IRGRP | S_IROTH + | S_IWUSR | S_IWGRP | S_IWOTH) & ~um); + } + + delete [] tc; + ++it; + } + + return filename; + } + +} // namespace iscan diff --git a/lib/file-opener.hh b/lib/file-opener.hh new file mode 100644 index 0000000..70c475c --- /dev/null +++ b/lib/file-opener.hh @@ -0,0 +1,101 @@ +// file-opener.hh -- dealing with files when doing multi-image scans +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifndef iscan_file_opener_hh_included +#define iscan_file_opener_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +namespace iscan +{ + using std::string; + + class file_opener + { + public: + explicit file_opener (bool collate); + explicit file_opener (const string& name); + file_opener (const string& pattern, unsigned int start_index); + ~file_opener (void); + + operator FILE * (void); + + const string& name (void) const; + const string& temp (void) const; + string extension (void) const; + + file_opener& operator++ (void); + + bool is_collating (void) const; + + void remove (void); + + static const string dir_sep; + static const string ext_sep; + static const char hash_mark; + + static const string null_ext; + + private: + void common_init (const string& s); + void set_names (bool next = false); + + void open (void); + void close (void); + void rename (void); + + bool _collate; + + string _filename; + string _tempfile; + FILE *_fp; + + struct pattern + { + string extension; + string basename; + string dirname; // includes final dir_sep + size_t digits; + unsigned int index; + }; + struct pattern *_pattern; + }; + +} // namespace iscan + +#endif /* !defined (iscan_file_opener_hh_included) */ diff --git a/lib/imgstream.cc b/lib/imgstream.cc new file mode 100644 index 0000000..80f87fb --- /dev/null +++ b/lib/imgstream.cc @@ -0,0 +1,142 @@ +// imgstream.cc -- provides an interface to write multiple images +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imgstream.hh" + +#include "jpegstream.hh" +#include "pcxstream.hh" +#include "pngstream.hh" +#include "pnmstream.hh" +#include "pdfstream.hh" +#include "tiffstream.hh" + +namespace iscan +{ + imgstream::imgstream (file_opener& opener, file_format format, + bool match_direction) + : _page (0), _match_direction (match_direction), + _opener (&opener), _format (format), _configured (false) + { + _stream = create_stream (); + } + + imgstream::imgstream (void) + : _page (0), _match_direction (false), _opener (NULL), _format (NO_FORMAT), + _stream (NULL), _configured (false) + { + } + + imgstream::~imgstream (void) + { + delete _stream; + } + + imgstream& + imgstream::write (const byte_type *data, size_type n) + { + if (!_stream) return *this; + + if (!_configured) + { + _stream->size (_h_sz, _v_sz); + _stream->resolution (_hres, _vres); + _stream->colour (_cspc); + _stream->depth (_bits); + _configured = true; + } + + _stream->write (data, n); + + return *this; + } + + imgstream& + imgstream::flush (void) + { + if (_stream) _stream->flush (); + return *this; + } + + void + imgstream::next (void) + { + if (!_configured) return; + + delete _stream; + _configured = false; + + ++_page; + ++(*_opener); + _stream = create_stream (); + if (_match_direction) _stream->rotate_180 (is_back (_page)); + } + + bool + imgstream::is_back (unsigned long page) + { + return 0 == (page+1)%2; + } + + bool + imgstream::is_usable (void) + { + return true; + } + + basic_imgstream * + imgstream::create_stream (void) + { + if (PCX == _format) return new pcxstream (*_opener); + if (PNM == _format) return new pnmstream (*_opener); + if (PNG == _format) return new pngstream (*_opener); + if (JPG == _format) return new jpegstream (*_opener); + if (PDF == _format) return new pdfstream (*_opener); + if (TIF == _format) return new tiffstream (*_opener, _opener->temp ()); + + throw std::invalid_argument ("unsupported file format"); + } + + imgstream * + create_imgstream (file_opener& opener, file_format format, + bool match_direction) + { + if (opener.is_collating ()) + { + if (PDF == format) return new pdfstream (opener, match_direction); + if (TIF == format) return new tiffstream (opener, opener.name ()); + } + + return new imgstream (opener, format, match_direction); + } + +} // namespace iscan diff --git a/lib/imgstream.hh b/lib/imgstream.hh new file mode 100644 index 0000000..5904049 --- /dev/null +++ b/lib/imgstream.hh @@ -0,0 +1,100 @@ +// imgstream.hh -- provides an interface to write multiple images +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + +#ifndef iscan_imgstream_hh_included +#define iscan_imgstream_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "basic-imgstream.hh" +#include "file-opener.hh" + + +namespace iscan +{ + enum file_format + { + PCX, + PNM, + PNG, + JPG, JPEG = JPG, + PDF, + TIF, // libtiff uses TIFF as a type already! + NO_FORMAT, + }; + + class imgstream : public basic_imgstream + { + public: + typedef basic_imgstream::byte_type byte_type; + typedef basic_imgstream::size_type size_type; + + imgstream (file_opener& opener, file_format format, + bool match_direction = false); + virtual ~imgstream (void); + + virtual imgstream& write (const byte_type *data, size_type n); + virtual imgstream& flush (void); + + virtual void next (void); + + static bool is_usable (void); + + protected: + imgstream (void); + + bool is_back (unsigned long); // indicates whether a page is the + // back page of a duplex scan + + unsigned long _page; + bool _match_direction; // when true, match front and back + // orientation for duplex scans + + private: + basic_imgstream * create_stream (void); + + file_opener* _opener; + file_format _format; + + basic_imgstream *_stream; + bool _configured; + }; + + imgstream * + create_imgstream (file_opener& opener, file_format format, + bool match_direction = false); + +} // namespace iscan + +#endif /* iscan_imgstream_hh_included */ diff --git a/lib/jpegstream.cc b/lib/jpegstream.cc new file mode 100644 index 0000000..66bf92d --- /dev/null +++ b/lib/jpegstream.cc @@ -0,0 +1,270 @@ +// jpegstream.cc -- image streams producing JPEG files +// Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "jpegstream.hh" + +#include +#include + +namespace iscan +{ + jpegstream::jpegstream (FILE *fp, const string& name) + : _stream (fp), _header (false), _scanline (NULL) + { + if (!_stream) throw std::invalid_argument ("invalid file handle"); +#if HAVE_JPEGLIB_H + init (); +#endif + } + + jpegstream::~jpegstream (void) + { + delete [] _scanline; +#if HAVE_JPEGLIB_H + if (_header) + { + if (0 == _v_sz) lib->finish_compress (&_info); + lib->destroy_compress (&_info); + } +#endif + fflush (_stream); + } + + basic_imgstream& + jpegstream::write (const byte_type *line, size_type n) + { + if (!line || 0 == n) return *this; +#if HAVE_JPEGLIB_H + if (!_header) + { + write_header (); + } + if (!_scanline) + { + lib->write_scanlines (&_info, (JSAMPLE **) &line, 1); + if (0 < _info.err->msg_code) + throw std::ios_base::failure ("write error"); + } + else + { + // FIXME: assumes that _bits == 1, whereas the condition for + // _scanline to be true, see write_init (), requires + // only that _bits != 8. + for (unsigned int i = 0; i < _h_sz; ++i) + { + div_t index = div (i, 8 * sizeof (JSAMPLE)); + int offset = 8 * sizeof (JSAMPLE) - 1 - index.rem; + _scanline[i] = ((line[index.quot] & (1 << offset)) + ? 0 : ~0); + } + lib->write_scanlines (&_info, (JSAMPLE **) &_scanline, 1); + if (0 < _info.err->msg_code) + throw std::ios_base::failure ("write error"); + } + --_v_sz; +#endif /* HAVE_JPEGLIB_H */ + return *this; + } + + bool + jpegstream::is_usable (void) + { + if (lib) + { + return lib->is_usable; + } + + lib = new (std::nothrow) jpeg_lib_handle (); + if (!lib) + { + return false; + } + + lib->is_usable = false; + lib->message = string (); + lib->lib = NULL; +#if HAVE_JPEGLIB_H + try + { + basic_imgstream::dlopen ("libjpeg", validate); + } + catch (std::runtime_error& e) + { + lib->message = e.what (); + return lib->is_usable; + } +#endif /* HAVE_JPEGLIB_H */ + + return lib->is_usable; + } + +#if HAVE_JPEGLIB_H +#define funcsym(name) \ + lib->name \ + = ((jpegstream::jpeg_lib_handle::name##_f) \ + basic_imgstream::dlsym (lib->lib, "jpeg_"#name)); +#endif + + bool + jpegstream::validate (lt_dlhandle h) + { + if (!h) return false; + +#if HAVE_JPEGLIB_H + lib->lib = h; + +# ifndef jpeg_create_compress + funcsym (create_compress); +# else + funcsym (CreateCompress); +# endif + funcsym (finish_compress); + funcsym (destroy_compress); + funcsym (destroy); + funcsym (stdio_dest); + funcsym (std_error); + funcsym (write_scanlines); + funcsym (set_defaults); + funcsym (start_compress); + funcsym (default_qtables); + + // restrict usage of libjpeg to the version range it was compiled against; + // either before version 7.0 or 7.0 and later + bool is_version_consistent = + ((JPEG_LIB_VERSION < 70 && !lib->default_qtables) || + (JPEG_LIB_VERSION >= 70 && lib->default_qtables)); + + lib->is_usable = ( +# ifndef jpeg_create_compress + lib->create_compress +# else + lib->CreateCompress +# endif + && lib->finish_compress + && lib->destroy_compress + && lib->destroy + && lib->stdio_dest + && lib->std_error + && lib->write_scanlines + && lib->set_defaults + && lib->start_compress + && is_version_consistent); +#endif /* HAVE_JPEGLIB_H */ + + return lib->is_usable; + } + +#if HAVE_JPEGLIB_H +#undef funcsym +#endif + + basic_imgstream& + jpegstream::write_header (void) + { + check_consistency (); + +#if HAVE_JPEGLIB_H + + _info.image_width = _h_sz; + _info.image_height = _v_sz; + + _info.in_color_space = (RGB == _cspc ? JCS_RGB : JCS_GRAYSCALE); + _info.input_components = (RGB == _cspc ? 3 : 1); + + lib->set_defaults (&_info); + + size_type density_max = (1 << sizeof (_info.X_density) * 8) - 1; + _info.density_unit = 1; + _info.X_density = (_hres <= density_max) ? _hres : density_max; + _info.Y_density = (_vres <= density_max) ? _vres : density_max; + + lib->start_compress (&_info, true); + + if (mono == _cspc && 8 != _bits) + { + _scanline = new byte_type[_h_sz]; + } + else + { + _scanline = NULL; + } +#endif /* HAVE_JPEGLIB_H */ + + _header = true; + return *this; + } + + void + jpegstream::check_consistency (void) const + { + if (!(mono == _cspc || grey == _cspc || RGB == _cspc)) + { + throw std::logic_error ("unsupported colour space"); + } + } + + void + jpegstream::init (void) + { + if (!is_usable ()) + { + throw std::runtime_error (lib->message); + } + +#if HAVE_JPEGLIB_H + // set up JPEG library default error handlers first, then override + // error handling for fatal errors (because the default would just + // end up calling exit()) + _info.err = lib->std_error (&_err); + _err.error_exit = error_exit; +# ifndef jpeg_create_compress + lib->create_compress (&_info); +# else + lib->CreateCompress (&_info, JPEG_LIB_VERSION, + (size_t) sizeof (struct jpeg_compress_struct)); +# endif + lib->stdio_dest (&_info, _stream); +#endif /* HAVE_JPEGLIB_H */ + } + + jpegstream::jpeg_lib_handle *jpegstream::lib = NULL; + +#if HAVE_JPEGLIB_H + void + jpegstream::error_exit (jpeg_common_struct *info) + { + jpegstream::lib->destroy (info); + } +#endif + +} // namespace iscan diff --git a/lib/jpegstream.hh b/lib/jpegstream.hh new file mode 100644 index 0000000..855341c --- /dev/null +++ b/lib/jpegstream.hh @@ -0,0 +1,118 @@ +// jpegstream.hh -- image streams producing JPEG files +// Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifndef iscan_jpegstream_hh_included +#define iscan_jpegstream_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "basic-imgstream.hh" + +#include +#include + +#if HAVE_JPEGLIB_H +#include +#endif + +namespace iscan +{ + using std::string; + + class jpegstream : public basic_imgstream + { + public: + typedef basic_imgstream::byte_type byte_type; + typedef basic_imgstream::size_type size_type; + + explicit jpegstream (FILE *fp, const string& pathname = string ()); + virtual ~jpegstream (void); + + virtual basic_imgstream& write (const byte_type *line, size_type n); + + static bool is_usable (void); + + private: + basic_imgstream& write_header (void); + void check_consistency (void) const; + + void init (void); + + FILE *_stream; + bool _header; + + byte_type *_scanline; + + static bool validate (lt_dlhandle h); + struct jpeg_lib_handle + { + bool is_usable; + string message; + lt_dlhandle lib; + +#if HAVE_JPEGLIB_H +# ifndef jpeg_create_compress + fundecl (void, create_compress, jpeg_compress_struct *); +# else + fundecl (void, CreateCompress, jpeg_compress_struct *, int, size_t); +# endif + fundecl (void, finish_compress, jpeg_compress_struct *); + fundecl (void, destroy_compress, jpeg_compress_struct *); + fundecl (void, destroy, jpeg_common_struct *); + + fundecl (void, stdio_dest, jpeg_compress_struct *, FILE *); + fundecl (struct jpeg_error_mgr *, std_error, jpeg_error_mgr *); + + fundecl (void, write_scanlines, jpeg_compress_struct *, JSAMPLE **, int); + fundecl (void, set_defaults, jpeg_compress_struct *); + fundecl (void, start_compress, jpeg_compress_struct *, bool); + + // only used for version detection purposes; available since libjpeg 7.0 + fundecl (void, default_qtables, jpeg_compress_struct *, bool); +#endif /* HAVE_JPEGLIB_H */ + }; + static jpeg_lib_handle *lib; + +#if HAVE_JPEGLIB_H + static void error_exit (j_common_ptr info); + + struct jpeg_compress_struct _info; + struct jpeg_error_mgr _err; +#endif + }; + +} // namespace iscan + +#endif /* !defined (iscan_jpegstream_hh_included) */ diff --git a/lib/pcxstream.cc b/lib/pcxstream.cc new file mode 100644 index 0000000..722e0b0 --- /dev/null +++ b/lib/pcxstream.cc @@ -0,0 +1,320 @@ +// pcxstream.cc -- image streams producing PCX files +// Copyright (C) 2011 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcxstream.hh" + +#include +#include + +namespace iscan +{ + pcxstream::pcxstream (FILE *fp, const string& name) + : _stream (fp), + _header (false), + _footer (false), + _bytesperline (0), + _row_buf (NULL), + _zbuf (NULL) + { + if (!_stream) throw std::invalid_argument ("invalid file handle"); + + memset (_pcx_header, 0, sizeof (_pcx_header)); + _pcx_header [0] = 0x0a; // Manufacturer + _pcx_header [1] = 0x05; // Version + _pcx_header [2] = 1; // Encoding + } + + pcxstream::~pcxstream (void) + { + fflush (_stream); + } + + basic_imgstream& + pcxstream::flush (void) + { + if (!_footer) + { + if (mono == _cspc || grey == _cspc) + { + write_palette (); + } + delete [] _row_buf; + delete [] _zbuf; + _footer = true; + } + + fflush (_stream); + return *this; + } + + void + pcxstream::write_palette (void) + { + const size_type palette_size = 256 * 3 + 1; + byte_type palette [palette_size]; + byte_type *ptr = palette; + *ptr = 0x0c; // set palette identifier + ++ptr; + + for (int i=0; 256>i; ++i) + { + *ptr = *(ptr+1) = *(ptr+2) = i; + ptr += 3; + } + size_t rv = fwrite (palette, sizeof (*palette), palette_size, _stream); + if (palette_size != rv) + { + throw std::ios_base::failure ("write error"); + } + } + + basic_imgstream& + pcxstream::write (const byte_type *line, size_type n) + { + if (!line || 0 == n) return *this; + if (!_header) + { + size_type sz = 0; + write_header (); + if (mono == _cspc) + { + sz = n * 8; + pwrite = &pcxstream::write_mono; + } + else if (grey == _cspc) + { + sz = 1; // dummy + pwrite = &pcxstream::write_gray; + } + else if (RGB == _cspc) + { + sz = _h_sz * 3; // r + g + b + pwrite = &pcxstream::write_color; + } + _row_buf = new byte_type [sz]; + _zbuf = new byte_type [_h_sz * 2 + 1]; // assume worst case + } + + (this->*pwrite) (line, n); + + return *this; + } + + void + pcxstream::write_color (const byte_type *line, size_type n) + { + byte_type *r = _row_buf; + byte_type *g = r + _h_sz; + byte_type *b = g + _h_sz; + + for (size_type i=0; _h_sz>i; ++i) + { + r [i] = *line; ++line; + g [i] = *line; ++line; + b [i] = *line; ++line; + } + write_row (r, _h_sz); + write_row (g, _h_sz); + write_row (b, _h_sz); + } + + void + pcxstream::write_gray (const byte_type *line, size_type n) + { + write_row (line, n); + } + + void + pcxstream::write_mono (const byte_type *line, size_type n) + { + for (size_type i=0; n>i; ++i) + { + for (size_type j=0; 8>j; ++j) + { + _row_buf [i*8+7-j] = (line[i] & (0x1 << j)) ? 0x0 : 0xff; + } + } + write_row (_row_buf, _h_sz); + } + + void + pcxstream::write_row (const byte_type *line, size_type n) + { + size_type zlen = compress_row (line, n, _zbuf); + + // 2 byte align + if (n < _bytesperline) + { + _zbuf [zlen] = 0x00; // add padding + ++zlen; + } + + write_bytes (_zbuf, zlen); + } + + void + pcxstream::write_bytes (const byte_type *bytes, size_type n) + { + size_type rv; + rv = fwrite (bytes, sizeof (*bytes), n, _stream); + if (n != rv) + { + throw std::ios_base::failure ("write error"); + } + } + + basic_imgstream::size_type + pcxstream::compress_row (const byte_type *line, + const size_type n, + byte_type *compressed) + { + byte_type cnt = 0; + byte_type *ptr = compressed; + byte_type saved = 0; + + for (size_type i=0; i cnt && saved == line[i]) + { + ++cnt; + } + else if (63 == cnt || saved != line[i]) + { + if (1 < cnt || 0xc0 == (saved & 0xc0)) + { + *ptr = 0xc0 | cnt; + ++ptr; + } + *ptr = saved; + ++ptr; + saved = line[i]; + cnt = 1; + } + } + // put the rest + if (1 < cnt || 0xc0 == (saved & 0xc0)) + { + *ptr = 0xc0 | cnt; + ++ptr; + } + *ptr = saved; + ++ptr; + + return (ptr - compressed); + } + + bool + pcxstream::is_usable (void) + { + return true; + } + + basic_imgstream& + pcxstream::write_header (void) + { + check_consistency (); + + // Even monochrome image, BitsPerPixel in PCX header is set to 8 + // in accordance with the implementation of GIMP. + size_type bpp = 8; + _pcx_header [3] = bpp; // BitsPerPixel + + set_value_le ((_h_sz-1), &_pcx_header [8], 2); // Window.Xmax + set_value_le ((_v_sz-1), &_pcx_header [10], 2); // Window.Ymax + set_value_le (_hres, &_pcx_header [12], 2); // HDpi + set_value_le (_vres, &_pcx_header [14], 2); // VDpi + + size_type nplanes = 1; + size_type paletteinfo = 2; + if (RGB == _cspc) + { + nplanes = 3; + paletteinfo = 1; + } + _pcx_header [65] = nplanes; // NPlanes + set_value_le (paletteinfo, &_pcx_header [68], 2); // PaletteInfo + + _bytesperline = (_h_sz * bpp + 7) / 8; + _bytesperline += (_bytesperline & 0x1); // 2 byte align + set_value_le (_bytesperline, &_pcx_header [66], 2); // BytesPerLine + + write_bytes (_pcx_header, sizeof (_pcx_header)); + + _header = true; + return *this; + } + + void + pcxstream::check_consistency (void) const + { + if (!(mono == _cspc || grey == _cspc || RGB == _cspc)) + { + throw std::logic_error ("unsupported colour space"); + } + if (!(_bits == 1 || _bits == 8)) + { + throw std::logic_error ("unsupported bit depth"); + } + size_type max = (1 << 16) - 1; + if (_h_sz > max || _v_sz > max) + { + throw std::logic_error ("maximum image size exceeded"); + } + } + + // set value to array with little endian + void + pcxstream::set_value_le (const size_type value, + byte_type *array, + const size_type array_size) + { + if (!array || 0 >= array_size) + { + throw std::invalid_argument ("invalid argment"); + } + + size_type v = value; + + for (size_type i=0; array_size>i; ++i) + { + array[i] = v & 0xff; + v >>= 8; + } + } + +} // namespace iscan diff --git a/lib/pcxstream.hh b/lib/pcxstream.hh new file mode 100644 index 0000000..883b0af --- /dev/null +++ b/lib/pcxstream.hh @@ -0,0 +1,93 @@ +// pcxstream.hh -- image streams producing PCX files +// Copyright (C) 2011 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifndef iscan_pcxstream_hh_included +#define iscan_pcxstream_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "basic-imgstream.hh" + +#include +#include + +namespace iscan +{ + using std::string; + + class pcxstream : public basic_imgstream + { + public: + typedef basic_imgstream::byte_type byte_type; + typedef basic_imgstream::size_type size_type; + + explicit pcxstream (FILE *fp, const string& name = string ()); + virtual ~pcxstream (void); + + virtual basic_imgstream& write (const byte_type *line, size_type n); + virtual basic_imgstream& flush (void); + + static bool is_usable (void); + + private: + basic_imgstream& write_header (void); + void write_color (const byte_type *line, size_type n); + void write_gray (const byte_type *line, size_type n); + void write_mono (const byte_type *line, size_type n); + void write_row (const byte_type *line, size_type n); + void write_bytes (const byte_type *bytes, size_type n); + void write_palette (void); + size_type compress_row (const byte_type *line, + const size_type n, + byte_type *compressed); + void check_consistency (void) const; + void set_value_le (const size_type value, + byte_type *array, + const size_type array_size); + + FILE *_stream; + bool _header; + bool _footer; + byte_type _pcx_header [128]; + size_type _bytesperline; + byte_type *_row_buf; + byte_type *_zbuf; + + void (pcxstream::*pwrite) (const byte_type *line, size_type n); + }; + +} // namespace iscan + +#endif /* !defined (iscan_pcxstream_hh_included) */ diff --git a/lib/pdf/Makefile.am b/lib/pdf/Makefile.am new file mode 100644 index 0000000..4cc3e95 --- /dev/null +++ b/lib/pdf/Makefile.am @@ -0,0 +1,44 @@ +## Makefile.am -- an automake template for a Makefile.in file +## Copyright (C) 2008 SEIKO EPSON CORPORATION +## +## This file is part of the "Image Scan!" build infra-structure. +## +## The "Image Scan!" build infra-structure is free software. +## You can redistribute it and/or modify it under the terms of the GNU +## General Public License as published by the Free Software Foundation; +## either version 2 of the License or at your option any later version. +## +## This program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +## FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +## See the GNU General Public License for more details. +## +## You should have received a verbatim copy of the GNU General Public +## License along with this program; if not, write to: +## +## Free Software Foundation, Inc. +## 59 Temple Place, Suite 330 +## Boston, MA 02111-1307 USA + + +if ENABLE_FRONTEND +noinst_LTLIBRARIES = libpdf.la +libpdf_la_LDFLAGS = -static +libpdf_la_SOURCES = \ + $(libpdf_la_files) +endif + +libpdf_la_files = \ + array.cc \ + array.hh \ + dictionary.cc \ + dictionary.hh \ + object.cc \ + object.hh \ + primitive.cc \ + primitive.hh \ + writer.cc \ + writer.hh + +EXTRA_DIST = \ + $(libpdf_la_files) diff --git a/lib/pdf/array.cc b/lib/pdf/array.cc new file mode 100644 index 0000000..812ee03 --- /dev/null +++ b/lib/pdf/array.cc @@ -0,0 +1,115 @@ +// array.cc -- PDF array objects +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "array.hh" + +namespace iscan +{ + +namespace pdf +{ + +array::~array () +{ + store_citer it; + + for (it = _mine.begin (); _mine.end () != it; ++it) + { + object* obj = *it; + delete obj; + obj = NULL; + } +} + +void +array::insert (object *value) +{ + _store.push_back (value); +} + +void +array::insert (primitive value) +{ + primitive *copy = new primitive (); + + *copy = value; + _mine.push_back (copy); + insert (copy); +} + +void +array::insert (object value) +{ + object *copy = new object (); + + *copy = value; + _mine.push_back (copy); + insert (copy); +} + +size_t +array::size () const +{ + return _store.size (); +} + +const object * +array::operator[] (size_t index) const +{ + return _store[index]; +} + +void +array::print (FILE* fp) const +{ + store_citer it; + + fprintf (fp, "[ "); + if (4 < _store.size ()) + { + fprintf (fp, "\n"); + } + for (it = _store.begin (); _store.end () != it; ++it) + { + (*it)->print (fp); + fprintf (fp, " "); + if (4 < _store.size ()) + { + fprintf (fp, "\n"); + } + } + fprintf (fp, "]"); +} + +} // namespace pdf +} // namespace iscan diff --git a/lib/pdf/array.hh b/lib/pdf/array.hh new file mode 100644 index 0000000..7bb5ac5 --- /dev/null +++ b/lib/pdf/array.hh @@ -0,0 +1,87 @@ +// array.hh -- PDF array objects +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + +#ifndef iscan_pdf_array_hh_included +#define iscan_pdf_array_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "object.hh" +#include "primitive.hh" + +#include + +namespace iscan +{ + +namespace pdf +{ + +/*! Defines a PDF array object [p 58]. + */ +class array : public object +{ +private: + typedef std::vector store_type; + typedef store_type::iterator store_iter; + typedef store_type::const_iterator store_citer; + + store_type _store; + store_type _mine; + +public: + virtual ~array (); + + /*! Insert an object at the end the array + */ + void insert (object* obj); + + void insert (primitive obj); + void insert (object obj); + + /*! Count the number of objects in the array + */ + size_t size() const; + + /*! Obtain a reference to an object at a given index + */ + const object* operator[] (size_t index) const; + + virtual void print (FILE* fp) const; +}; + +} // namespace pdf +} // namespace iscan + +#endif // iscan_pdf_array_hh_included diff --git a/lib/pdf/dictionary.cc b/lib/pdf/dictionary.cc new file mode 100644 index 0000000..e6822e7 --- /dev/null +++ b/lib/pdf/dictionary.cc @@ -0,0 +1,123 @@ +// dictionary.cc -- PDF dictionaries +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "dictionary.hh" + +namespace iscan +{ + +namespace pdf +{ + +dictionary::~dictionary () +{ + store_citer it; + + for (it = _mine.begin (); _mine.end () != it; ++it) + { + object* obj = it->second; + delete obj; + obj = NULL; + } +} + +void +dictionary::insert (const char *key, object *value) +{ + if (_mine.end () != _mine.find (key)) + { + delete _mine[key]; + } + _store[key] = value; +} + +void +dictionary::insert (const char *key, primitive value) +{ + primitive *copy = new primitive (); + + *copy = value; + insert (key, copy); + _mine[key] = copy; +} + +void +dictionary::insert (const char *key, object value) +{ + object *copy = new object (); + + *copy = value; + insert (key, copy); + _mine[key] = copy; +} + +size_t +dictionary::size () const +{ + return _store.size (); +} + +const object * +dictionary::operator[] (const char* key) const +{ + store_citer it = _store.find (key); + + return (_store.end () != it ? it->second : NULL); +} + +void +dictionary::print (FILE* fp) const +{ + store_citer it; + + if (1 >= _store.size ()) + { + it = _store.begin (); + fprintf (fp, "<< /%s ", it->first); + it->second->print (fp); + fprintf (fp, " >>"); + return; + } + + fprintf (fp, "<<\n"); + for (it = _store.begin (); _store.end () != it; ++it) + { + fprintf (fp, "/%s ", it->first); + it->second->print (fp); + fprintf (fp, "\n"); + } + fprintf (fp, ">>"); +} + +} // namespace pdf +} // namespace iscan diff --git a/lib/pdf/dictionary.hh b/lib/pdf/dictionary.hh new file mode 100644 index 0000000..d20e0d2 --- /dev/null +++ b/lib/pdf/dictionary.hh @@ -0,0 +1,93 @@ +// dictionary.hh -- PDF dictionaries +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifndef iscan_pdf_dictionary_hh_included +#define iscan_pdf_dictionary_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "object.hh" +#include "primitive.hh" + +#include + +namespace iscan +{ + +namespace pdf +{ + +/*! Defines a pdf dictionary object [p 59] + */ +class dictionary : public object +{ +private: + typedef std::map store_type; + typedef store_type::iterator store_iter; + typedef store_type::const_iterator store_citer; + + store_type _store; + store_type _mine; + +public: + virtual ~dictionary (); + + /*! Insert a key/value pair into the dictionary + * + * If the key already exists, its value is replaced with the new one. + * + * The key is written to the PDF file as a name object as defined in the + * PDF spec [p. 59] + */ + void insert (const char *key, object *value); + + void insert (const char *key, primitive value); + void insert (const char *key, object value); + + /*! Count the number of objects in the dictionary + */ + size_t size () const; + + /*! Obtain a reference to an object with a given key + */ + const object * operator[] (const char *key) const; + + virtual void print (FILE* fp) const; +}; + +} // namespace pdf +} // namespace iscan + +#endif // iscan_pdf_dictionary_hh_included diff --git a/lib/pdf/object.cc b/lib/pdf/object.cc new file mode 100644 index 0000000..dba25da --- /dev/null +++ b/lib/pdf/object.cc @@ -0,0 +1,103 @@ +// object.cc -- PDF objects +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "object.hh" + +#include + +namespace iscan +{ + +namespace pdf +{ + + +object::object () +{ + _obj_num = 0; +} + +object::object (size_t num) +{ + // FIXME: what if num has already been used? + _obj_num = num; +} + +object::~object (void) +{ +} + +size_t +object::obj_num () +{ + if (65535 == next_obj_num) + { + throw std::runtime_error ("PDF object number overflow"); + } + + if (is_direct ()) + { + _obj_num = ++next_obj_num; + } + return _obj_num; +} + +bool +object::operator== (object& that) const +{ + // FIXME: what if one or both instances have not gotten an object + // number yet? + return _obj_num == that._obj_num; +} + +void +object::print (FILE* fp) const +{ + fprintf (fp, "%zu 0 R", _obj_num); +} + +bool +object::is_direct () const +{ + return (0 == _obj_num); +} + +size_t object::next_obj_num = 0; + +void object::reset_object_numbers () +{ + next_obj_num = 0; +} + +} // namespace pdf +} // namespace iscan diff --git a/lib/pdf/object.hh b/lib/pdf/object.hh new file mode 100644 index 0000000..ef38e85 --- /dev/null +++ b/lib/pdf/object.hh @@ -0,0 +1,120 @@ +// object.hh -- PDF objects +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + +#ifndef iscan_pdf_object_hh_included +#define iscan_pdf_object_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +namespace iscan +{ + +namespace pdf +{ + +using namespace std; + +/*! A base class for all pdf objects [p 51]. + * + * A pdf::object is also used to pass around object numbers in a transparent + * fashion so that object references can be output correctly as elements of + * arrays and dictionaries. + */ +class object +{ +private: + + size_t _obj_num; + static size_t next_obj_num; // the next free object number + +public: + + object (); + + /*! Creates a new pdf::object with its object number set to \a num. + * + * Constructs an indirect object. + */ + object (size_t num); + + virtual ~object (void); + + /*! Obtain the pdf::object's object number. + * + * If the object has not been allocated an object number yet, a new one is + * allocated and returned. + */ + size_t obj_num (); + + /*! Determine whether the object is direct or indirect [p 63]. + * + * Probably don't need this method. + * + * @return True if the object is direct, False if it is indirect + */ + bool is_direct () const; + + /*! Output the object contents. + * + * In the case of pdf::object, this only outputs an indirect reference to + * itself [p 64]. + * + * Each subclass re-implements this in order to print its own content. + * It should only ever output the object contents, ommitting the object + * definition header and footer [p 64] + */ + virtual void print (FILE* fp) const; + + /*! Compare the contents of two pdf::objects. + * + * Only the object contents are compared, the object number of the two + * objects can be different. + * + * In the case of pdf::object, the object numbers are compared. + */ + virtual bool operator== (object& that) const; + + /*! Reset the current object number to recycle them for new documents + */ + static void reset_object_numbers (); + +}; + +} // namespace pdf +} // namespace iscan + +#endif // iscan_pdf_object_hh_included diff --git a/lib/pdf/primitive.cc b/lib/pdf/primitive.cc new file mode 100644 index 0000000..8a47c14 --- /dev/null +++ b/lib/pdf/primitive.cc @@ -0,0 +1,100 @@ +// primitive.cc -- PDF primitives +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "primitive.hh" + +#include + +namespace iscan +{ + +namespace pdf +{ + +primitive::primitive () + : _str (string ()) +{ +} + +primitive::primitive (string value) + : _str (value) +{ +} + +primitive::primitive (int value) +{ + stringstream ss; + ss << value; + ss >> _str; +} + +primitive::primitive (size_t value) +{ + stringstream ss; + ss << value; + ss >> _str; +} + +primitive::primitive (double value) +{ + stringstream ss; + ss << value; + ss >> _str; +} + +primitive::~primitive (void) +{ +} + +bool +primitive::operator== (const primitive& that) const +{ + return _str == that._str; +} + +void +primitive::print (FILE* fp) const +{ + fprintf (fp, "%s", _str.c_str ()); +} + +// FIXME: doesn't do the default assignment just what we want? +void +primitive::operator= (const primitive& that) +{ + _str = that._str; +} + + +} // namespace pdf +} // namespace iscan diff --git a/lib/pdf/primitive.hh b/lib/pdf/primitive.hh new file mode 100644 index 0000000..e45adc0 --- /dev/null +++ b/lib/pdf/primitive.hh @@ -0,0 +1,92 @@ +// primitive.hh -- PDF primitives +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + +#ifndef iscan_pdf_primitive_hh_included +#define iscan_pdf_primitive_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "object.hh" + +#include + +namespace iscan +{ + +namespace pdf +{ + using std::string; + +/*! Defines a primitive pdf object: one of string, name, integer, or real. + */ +class primitive : public object +{ +private: + string _str; + +public: + primitive (); + + /*! Create a new pdf string/name object with the given value. + */ + primitive (string value); + + /*! Create a new pdf integer object + */ + primitive (int value); + + primitive (size_t value); + + /*! Create a new pdf real object + */ + primitive (double value); + + virtual ~primitive (void); + + /*! Compare the contents of two pdf::primitives. + * + * Only the object contents are compared, the object number of the + * two objects may differ. + */ + virtual bool operator== (const primitive& other) const; + + void operator= (const primitive& that); + + virtual void print (FILE* fp) const; +}; + +} // namespace pdf +} // namespace iscan + +#endif // iscan_pdf_primitive_hh_included diff --git a/lib/pdf/writer.cc b/lib/pdf/writer.cc new file mode 100644 index 0000000..78a9355 --- /dev/null +++ b/lib/pdf/writer.cc @@ -0,0 +1,236 @@ +// writer.cc -- putting PDF object in a file +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "writer.hh" + +#include +#include + +namespace iscan +{ + +namespace pdf +{ + using std::runtime_error; + using std::string; + +writer::writer (FILE *file) + : _xref (xref ()), _file (file) +{ + _xref_pos = 0; + _last_xref_pos = 0; + _saved_pos = 0; + _mode = object_mode; + _stream_len_obj = NULL; +} + +writer::~writer () +{ + delete _stream_len_obj; + _stream_len_obj = NULL; +} + +void +writer::write (object& obj) +{ + if (object_mode != _mode) + { + throw runtime_error ("invalid call to pdf::writer::write (object&)"); + } + + _xref[obj.obj_num ()] = ftell (_file); + + fprintf (_file, "%zu 0 obj\n", obj.obj_num ()); + obj.print (_file); + fprintf (_file, "\n"); + fprintf (_file, "endobj\n"); +} + +void +writer::begin_stream (dictionary& dict) +{ + if (stream_mode == _mode) + { + throw runtime_error ("invalid call to pdf::writer::begin_stream ()"); + } + _mode = stream_mode; + + _stream_len_obj = new primitive (); + dict.insert ("Length", object (_stream_len_obj->obj_num ())); + + _xref[dict.obj_num ()] = ftell (_file); + + fprintf (_file, "%zu 0 obj\n", dict.obj_num ()); + dict.print (_file); + fprintf (_file, "\n"); + fprintf (_file, "stream\n"); + + _saved_pos = ftell (_file); +} + +void +writer::write (const char *buf, size_t n) +{ + if (stream_mode != _mode) + { + throw runtime_error ("invalid call to pdf::writer::write ()"); + } + size_t rv = fwrite (buf, sizeof (char), n, _file); + if (rv != n) throw std::ios_base::failure ("write error"); +} + +void +writer::write (const string& s) +{ + if (stream_mode != _mode) + { + throw runtime_error ("invalid call to pdf::writer::write ()"); + } + fprintf (_file, "%s", s.c_str ()); +} + +void +writer::end_stream () +{ + if (stream_mode != _mode) + { + throw runtime_error ("invalid call to pdf::writer::end_stream ()"); + } + _mode = object_mode; + + size_t pos = ftell (_file); + size_t length = pos - _saved_pos; + + fprintf (_file, "\n"); + fprintf (_file, "endstream"); + fprintf (_file, "\n"); + fprintf (_file, "endobj"); + fprintf (_file, "\n"); + + // FIXME: overload the '=' operator in pdf::primitive + *_stream_len_obj = primitive (length); + + write (*_stream_len_obj); + delete _stream_len_obj; + _stream_len_obj = NULL; +} + +void +writer::header () +{ + if (_mode == stream_mode) + { + throw runtime_error ("cannot write header in stream mode"); + } + fprintf (_file, "%%PDF-1.0\n"); +} + +void +writer::trailer (dictionary& trailer_dict) +{ + if (_mode == stream_mode) + { + throw runtime_error ("cannot write trailer in stream mode"); + } + write_xref (); + write_trailer (trailer_dict); +} + +// FIXME: clean up this kludge +void +writer::write_xref () +{ + xref::const_iterator it; + + _last_xref_pos = _xref_pos; + _xref_pos = ftell (_file); + + fprintf (_file, "xref\n"); + + stringstream ss; + size_t start_obj_num = 0; + size_t cur_obj_num = 0; + size_t last_obj_num = 0; + + ss << "0000000000 65535 f " << endl; + for(it = _xref.begin (); _xref.end () != it; ++it) + { + cur_obj_num = it->first; + + if (cur_obj_num != last_obj_num + 1) + { + // write out the current xref section and start a new one + fprintf (_file, "%zu %zu\n", start_obj_num, + last_obj_num + 1 - start_obj_num); + fprintf (_file, "%s", ss.str ().c_str ()); + + ss.str (""); // flush stream + start_obj_num = cur_obj_num; + } + + last_obj_num = cur_obj_num; + + ss.width (10); + ss.fill ('0'); + ss << it->second << " 00000 n " << endl; + } + + if (!ss.str ().empty ()) + { + fprintf (_file, "%zu %zu\n", start_obj_num, + last_obj_num + 1 - start_obj_num); + fprintf (_file, "%s", ss.str ().c_str ()); + } +} + +void +writer::write_trailer (dictionary& trailer_dict) +{ + trailer_dict.insert ("Size", primitive (_xref.size () + 1)); + if (_last_xref_pos != 0) + { + trailer_dict.insert ("Prev", primitive (_last_xref_pos)); + } + + fprintf (_file, "trailer\n"); + trailer_dict.print (_file); + fprintf (_file, "\n"); + fprintf (_file, "startxref\n"); + fprintf (_file, "%zu\n", _xref_pos); + fprintf (_file, "%%%%EOF\n"); + + _xref.clear (); +} + +} // namespace pdf +} // namespace iscan diff --git a/lib/pdf/writer.hh b/lib/pdf/writer.hh new file mode 100644 index 0000000..f62c471 --- /dev/null +++ b/lib/pdf/writer.hh @@ -0,0 +1,165 @@ +// writer.hh -- putting PDF objects in a file +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + +#ifndef iscan_pdf_writer_hh_included +#define iscan_pdf_writer_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "object.hh" +#include "primitive.hh" +#include "dictionary.hh" + +#include + +namespace iscan +{ + +namespace pdf +{ + using std::string; + +/*! Writes PDF objects to a file. + * See section 3.4 of the PDF Reference version 1.7 for details on the basic + * file structure of a PDF file. + * + * There are two writing modes: object mode and stream mode. + * The default mode is object mode. When begin_stream() is called, the current + * mode is set to stream mode. When end_stream() is called, the mode is reset + * to object mode. + * + * In object mode, PDF objects are written all at once. Stream mode allows the + * writing of PDF stream objects which can be written incrementally. + */ +class writer +{ +private: + typedef std::map xref; + + xref _xref; + size_t _xref_pos; + size_t _last_xref_pos; + + FILE *_file; + size_t _saved_pos; + + primitive* _stream_len_obj; + + typedef enum { + object_mode, + stream_mode + } write_mode; + + write_mode _mode; + +public: + /*! Creates a new pdf::writer object which will write to the given stream. + */ + writer (FILE *file); + + ~writer (); + + /*! Writes a pdf::object to the file as an indirect object [p 63]. + * + * Recursively writes any child objects to the file. + * Can only be called while in object mode, which is the default + * mode. If this method is called between calls to begin_stream() and + * end_stream() a std::runtime_error exception is thrown. + */ + void write (object& object); + + /*! Initializes a PDF stream [p 60] and sets the current mode to stream mode. + * + * Pass a pdf::dictionary object, \a dict, defining the stream properties, + * omitting the mandatory "Length" property which is automatically + * calculated and written to the file. If this method is called when already + * in stream mode, a std::runtime_error exception is thrown. + */ + void begin_stream (dictionary& dict); + + /*! Writes \a n bytes from \a buf to the file as part of a PDF stream. + * + * Can only be called while in stream mode. + * Calls to this method must be contained within calls to + * begin_stream() and end_stream(). If not, a std::runtime_error + * exception is thrown. + */ + void write (const char* buf, size_t n); + + /*! Writes a string \a s to the file as part of a PDF stream. + * + * Can only be called while in stream mode. + * Calls to this method must be contained within calls to + * begin_stream() and end_stream(). If not, a std::runtime_error + * exception is thrown. + */ + void write (const string& s); + + /*! Finishes writing a PDF stream and sets the current mode to object mode. + * + * The "Length" property of the stream is written at this point as an + * indirect object. Can only be called when in stream mode, if not, a + * std::runtime_error exception is thrown. + */ + void end_stream (); + + /*! Writes the PDF header [p 92]. + * + * A std::runtime_error exception is thrown if this method is called while + * in stream mode. + */ + void header (); + + /*! Writes the PDF trailer [p 96] and xref table [p 93]. + * + * Also sets the "Prev" entry in \a trailer_dict and clears the internal + * xref table. A std::runtime_error exception is thrown if this method is + * called while in stream mode. + * + * \param trailer_dict The trailer entries to be written. + */ + void trailer (dictionary& trailer_dict); + +private: + // Writes the cross-reference table [p 93]. + void write_xref (); + + // Writes the file trailer [p 96]. + void write_trailer (dictionary& trailer_dict); +}; + +} // namespace pdf +} // namespace iscan + +#endif // iscan_pdf_writer_hh_included diff --git a/lib/pdfstream.cc b/lib/pdfstream.cc new file mode 100644 index 0000000..4875de5 --- /dev/null +++ b/lib/pdfstream.cc @@ -0,0 +1,351 @@ +// pdfstream.cc -- image streams producing PDF files +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pdfstream.hh" +#include "jpegstream.hh" + +#include + +namespace iscan +{ + +pdfstream::pdfstream (FILE *file, bool match_direction) + : imgstream (), // avoid recursion + _file (file), _g3 (NULL), _rotate_180 (false) +{ + _match_direction = match_direction; + init (); +} + +void +pdfstream::init () +{ + if (!is_usable ()) + { + throw std::runtime_error ("pdf not usable"); + } + + _page = 0; + _row = 0; + _need_page_trailer = false; + _data_size = 0; + _pdf_h_sz = 0; + _pdf_v_sz = 0; + _do_jpeg = false; + + _doc = NULL; + _pages = NULL; + _page_list = NULL; + _trailer = NULL; + _img_height_obj = NULL; + _stream = NULL; + + _doc = new pdf::writer (_file); + + pdf::object::reset_object_numbers (); +} + +pdfstream::~pdfstream () +{ + if (_need_page_trailer) + { + write_page_trailer (); + } + + fflush (_file); + + delete _doc; + delete _pages; + delete _page_list; + delete _trailer; + delete _img_height_obj; + + delete _stream; + + delete _g3; +} + +bool +pdfstream::is_usable () +{ + return true; +} + +void +pdfstream::rotate_180 (bool yes) +{ + _rotate_180 = yes; +} + +void +pdfstream::next () +{ + if (0 == _row) + { + return; + } + + write_page_trailer (); + _row = 0; + _rotate_180 = _match_direction && is_back (_page); +} + +imgstream& +pdfstream::write (const byte_type *line, size_type n) +{ + if (!line || 0 == n) return *this; + if (0 == _page) + { + write_header (); + } + if (0 == _row) + { + // adjust to PDF coordinate system (is this correct?) + _pdf_h_sz = (72 * _h_sz) / _hres; + _pdf_v_sz = (72 * _v_sz) / _vres; + + write_page_header (); + ++_page; + } + + if (_stream) + { + _stream->write (line, n); + } + else if (_g3) + { + string buf = (*_g3) (line, n); + _doc->write (buf.data (), buf.size ()); + } + else + { + _doc->write (line, n); + } + ++_row; + + return *this; +} + +void +pdfstream::write_header () +{ + _doc->header (); + + delete _pages; + _pages = new pdf::dictionary (); + + pdf::dictionary info; + info.insert ("Producer", pdf::primitive ("(iscan)")); + info.insert ("Creator", pdf::primitive ("(iscan)")); + _doc->write (info); + + pdf::dictionary catalog; + catalog.insert ("Type", pdf::primitive ("/Catalog")); + catalog.insert ("Pages", pdf::object (_pages->obj_num ())); + _doc->write (catalog); + + delete _trailer; + _trailer = new pdf::dictionary (); + _trailer->insert ("Info", pdf::object (info.obj_num ())); + _trailer->insert ("Root", pdf::object (catalog.obj_num ())); + + delete _page_list; + _page_list = new pdf::array (); +} + +void +pdfstream::write_page_header () +{ + pdf::dictionary page; + + _page_list->insert (pdf::object (page.obj_num ())); + + _pages->insert ("Type", pdf::primitive ("/Pages")); + _pages->insert ("Kids", _page_list); + _pages->insert ("Count", pdf::primitive (_page_list->size ())); + + _doc->write (*_pages); + + pdf::dictionary image; + pdf::dictionary contents; + + pdf::array mbox; + mbox.insert (pdf::primitive (0)); + mbox.insert (pdf::primitive (0)); + mbox.insert (pdf::primitive (_pdf_h_sz)); + mbox.insert (pdf::primitive (_pdf_v_sz)); + + std::stringstream ss2; + std::string img_name; + + ss2 << "iscanImage" << _page; + img_name = ss2.str (); + + pdf::array procset; + std::string cproc = "/ImageB"; + if (RGB == _cspc) + { + cproc = "/ImageC"; + } + pdf::dictionary tmp; + tmp.insert (img_name.c_str (), pdf::object (image.obj_num ())); + + procset.insert (pdf::primitive ("/PDF")); + procset.insert (pdf::primitive (cproc)); + + pdf::dictionary rsrc; + rsrc.insert ("XObject", &tmp); + rsrc.insert ("ProcSet", &procset); + + page.insert ("Type", pdf::primitive ("/Page")); + page.insert ("Parent", pdf::object (_pages->obj_num ())); + page.insert ("Resources", &rsrc); + page.insert ("MediaBox", &mbox); + page.insert ("Contents", pdf::object (contents.obj_num ())); + + _doc->write (page); + + _doc->begin_stream (contents); + + // transformation matrices must be specified in reverse order + std::stringstream ss; + ss << "q" << std::endl; + ss << _pdf_h_sz << " 0 0 " << _pdf_v_sz << " 0 0 cm" << std::endl; + if (_rotate_180) + { + // undo the translation below + ss << "1 0 0 1 0.5 0.5 cm" << std::endl; + + // reflect along x and y axis + ss << "-1 0 0 -1 0 0 cm" << std::endl; + + // translate so the image midpoint lies on the origin + ss << "1 0 0 1 -0.5 -0.5 cm" << std::endl; + } + ss << "/" << img_name << " Do" << std::endl; + ss << "Q"; + + _doc->write (ss.str ()); + _doc->end_stream (); + + write_image_object (image, img_name); + + _need_page_trailer = true; +} + +void +pdfstream::write_image_object (pdf::dictionary& image, std::string name) +{ + delete _img_height_obj; + _img_height_obj = new pdf::primitive (); + + image.insert ("Type", pdf::primitive ("/XObject")); + image.insert ("Subtype", pdf::primitive ("/Image")); + image.insert ("Width", pdf::primitive (_h_sz)); + image.insert ("Height", pdf::object (_img_height_obj->obj_num ())); + + pdf::array decode; + std::string dev = "/DeviceGray"; + if (RGB == _cspc) dev = "/DeviceRGB"; + + if (monochrome == _cspc) + { + if (!_g3) _g3 = new fax_encoder; + } + else + { + _do_jpeg = iscan::jpegstream::is_usable (); + } + image.insert ("ColorSpace", pdf::primitive (dev)); + image.insert ("BitsPerComponent", pdf::primitive (_bits)); + image.insert ("Interpolate", pdf::primitive ("true")); + + pdf::dictionary parms; + if (_do_jpeg) + { + image.insert ("Filter", pdf::primitive ("/DCTDecode")); + } + else if (_g3) + { + image.insert ("Filter", pdf::primitive ("/CCITTFaxDecode")); + + parms.insert ("Columns", pdf::primitive (_h_sz)); + parms.insert ("Rows", pdf::primitive (_v_sz)); + parms.insert ("EndOfBlock", pdf::primitive ("false")); + parms.insert ("EndOfLine", pdf::primitive ("true")); + parms.insert ("EncodedByteAlign", pdf::primitive ("true")); + parms.insert ("K", pdf::primitive (0)); // CCITT3 1-D encoding + image.insert ("DecodeParms", &parms); + } + + // see PDF reference 1.7 p. 342 and p. 1107 # 53 + image.insert ("Name", pdf::primitive ("/" + name)); + + _doc->begin_stream (image); + + if (_do_jpeg) + { + _stream = new jpegstream (_file); + } + + if (_stream) + { + _stream->size (_h_sz, _v_sz); + _stream->depth (_bits); + _stream->colour (_cspc); + _stream->resolution (_hres, _vres); + } +} + +void +pdfstream::write_page_trailer () +{ + delete _stream; + _stream = NULL; + + _doc->end_stream (); + + *_img_height_obj = pdf::primitive (_row); + _doc->write (*_img_height_obj); + + _doc->trailer (*_trailer); + + _need_page_trailer = false; + + _pdf_h_sz = 0; + _pdf_v_sz = 0; + + _do_jpeg = false; +} + +} // namespace iscan diff --git a/lib/pdfstream.hh b/lib/pdfstream.hh new file mode 100644 index 0000000..70f7069 --- /dev/null +++ b/lib/pdfstream.hh @@ -0,0 +1,105 @@ +// pdfstream.hh -- image streams producing PDF files +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + +#ifndef iscan_pdfstream_hh_included +#define iscan_pdfstream_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imgstream.hh" + +#include "pdf/writer.hh" +#include "pdf/dictionary.hh" +#include "pdf/array.hh" +#include "pdf/primitive.hh" +#include "fax-encoder.hh" + +#include + +namespace iscan +{ + using std::string; + +class pdfstream : public imgstream +{ + +public: + typedef basic_imgstream::byte_type byte_type; + typedef basic_imgstream::size_type size_type; + +private: + bool _need_page_trailer; + size_type _data_size; + size_type _pdf_h_sz; // scaled size to fit on a page at 72 dpi + size_type _pdf_v_sz; // but better to set the dpi in the PDF file, + // there is some way to do that, I read it in the spec! + + pdf::writer *_doc; + pdf::dictionary *_pages; + pdf::array *_page_list; + pdf::dictionary *_trailer; + + size_type _row; + pdf::primitive *_img_height_obj; + + basic_imgstream *_stream; + FILE *_file; + + bool _do_jpeg; + fax_encoder *_g3; + bool _rotate_180; + +public: + static bool is_usable (); + + explicit pdfstream (FILE *fp, bool match_direction = false); + + virtual ~pdfstream (); + + virtual imgstream& write (const byte_type *line, size_type n); + + virtual void next (); + virtual void rotate_180 (bool yes); + +private: + void init(); + void write_header (); + void write_page_header (); + void write_page_trailer (); + void write_image_object (pdf::dictionary& image, std::string name); +}; + +} // namespace iscan + +#endif /* iscan_pdfstream_hh_included */ diff --git a/lib/pngstream.cc b/lib/pngstream.cc new file mode 100644 index 0000000..d9344b4 --- /dev/null +++ b/lib/pngstream.cc @@ -0,0 +1,257 @@ +// pngstream.cc -- image streams producing PNG files +// Copyright (C) 2008, 2009, 2016 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pngstream.hh" + +#include + +namespace iscan +{ + pngstream::pngstream (FILE *fp, const string& name) + : _stream (fp), _header (false), _footer (false) + { + if (!_stream) throw std::invalid_argument ("invalid file handle"); +#if HAVE_PNG_H + init (); +#endif + } + + pngstream::~pngstream (void) + { + try + { + flush (); + } + catch (const std::exception& oops) + { + std::cerr << oops.what (); + } + +#if HAVE_PNG_H + lib->destroy_write_struct (&_png, &_info); +#endif + } + + basic_imgstream& + pngstream::write (const byte_type *line, size_type n) + { + if (!line || 0 == n) return *this; +#if HAVE_PNG_H + if (!_header) + { + write_header (); + } + set_error_handler (_png, _info); + lib->write_row (_png, (png_byte *) line); +#endif + return *this; + } + + basic_imgstream& + pngstream::flush (void) + { +#if HAVE_PNG_H + set_error_handler (_png, _info); + + if (_header && !_footer && _png->num_rows == _png->flush_rows) + { + lib->write_end (_png, _info); + _footer = true; + } +#endif + fflush (_stream); + + return *this; + } + + bool + pngstream::is_usable (void) + { + if (lib) + { + return lib->is_usable; + } + + lib = new (std::nothrow) png_lib_handle (); + if (!lib) + { + return false; + } + + lib->is_usable = false; + lib->message = string (); + lib->lib = NULL; +#if HAVE_PNG_H + try + { + // Some PNG libraries are *not* linked with libz which leads + // to a "file not found" error when calling dlopen. We just + // dlopen the libz library explicitly to work around this. + basic_imgstream::dlopen ("libz"); + basic_imgstream::dlopen ("libpng12", validate); + } + catch (std::runtime_error& e) + { + lib->message = e.what (); + return lib->is_usable; + } +#endif /* HAVE_PNG_H */ + + return lib->is_usable; + } + +#if HAVE_PNG_H +#define funcsym(name) \ + lib->name \ + = ((pngstream::png_lib_handle::name##_f) \ + basic_imgstream::dlsym (lib->lib, "png_"#name)); +#endif + + bool + pngstream::validate (lt_dlhandle h) + { + if (!h) return false; + +#if HAVE_PNG_H + lib->lib = h; + + funcsym (access_version_number); + funcsym (create_write_struct); + funcsym (create_info_struct); + funcsym (destroy_write_struct); + funcsym (init_io); + funcsym (set_IHDR); + funcsym (set_pHYs); + funcsym (set_invert_mono); + funcsym (write_info); + funcsym (write_row); + funcsym (write_flush); + funcsym (write_end); + + if (lib->access_version_number + && lib->create_write_struct + && lib->create_info_struct + && lib->destroy_write_struct + && lib->init_io + && lib->set_IHDR + && lib->set_pHYs + && lib->set_invert_mono + && lib->write_info + && lib->write_row + && lib->write_flush + && lib->write_end) + { + lib->is_usable = (PNG_LIBPNG_VER <= lib->access_version_number ()); + } +#endif /* HAVE_PNG_H */ + + return lib->is_usable; + } + +#if HAVE_PNG_H +#undef funcsym +#endif + + basic_imgstream& + pngstream::write_header (void) + { + check_consistency (); + +#if HAVE_PNG_H + set_error_handler (_png, _info); + + if (mono == _cspc) + { + _bits = 1; + lib->set_invert_mono (_png); + } + + lib->init_io (_png, _stream); + + lib->set_IHDR (_png, _info, _h_sz, _v_sz, _bits, + (RGB == _cspc + ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY), + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + size_type hres = size_type (100 / 2.54 * _hres + 0.5); + size_type vres = size_type (100 / 2.54 * _vres + 0.5); + lib->set_pHYs (_png, _info, hres, vres, PNG_RESOLUTION_METER); + + lib->write_info (_png, _info); +#endif /* HAVE_PNG_H */ + + _header = true; + return *this; + } + + void + pngstream::check_consistency (void) const + { + if (!(mono == _cspc || grey == _cspc || RGB == _cspc)) + { + throw std::logic_error ("unsupported colour space"); + } + } + + void + pngstream::init (void) + { + if (!is_usable ()) + { + throw std::runtime_error (lib->message); + } + +#if HAVE_PNG_H + _png = lib->create_write_struct (version_string, + NULL, NULL, NULL); + _info = NULL; + if (!_png) throw std::bad_alloc (); + + _info = lib->create_info_struct (_png); + if (!_info) + { + lib->destroy_write_struct (&_png, NULL); + throw std::bad_alloc (); + } +#endif /* HAVE_PNG_H */ + } + + pngstream::png_lib_handle *pngstream::lib = NULL; + +#if HAVE_PNG_H + png_const_charp pngstream::version_string = PNG_LIBPNG_VER_STRING; +#endif + +} // namespace iscan diff --git a/lib/pngstream.hh b/lib/pngstream.hh new file mode 100644 index 0000000..77d4f54 --- /dev/null +++ b/lib/pngstream.hh @@ -0,0 +1,145 @@ +// pngstream.hh -- image streams producing PNG files +// Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION +// +// This file is part of 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifndef iscan_pngstream_hh_included +#define iscan_pngstream_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "basic-imgstream.hh" + +#include +#include +#include + +#if HAVE_PNG_H +#include +#endif + +namespace iscan +{ + using std::string; + + class pngstream : public basic_imgstream + { + public: + typedef basic_imgstream::byte_type byte_type; + typedef basic_imgstream::size_type size_type; + + explicit pngstream (FILE *fp, const string& name = string ()); + virtual ~pngstream (void); + + virtual basic_imgstream& write (const byte_type *line, size_type n); + virtual basic_imgstream& flush (void); + + static bool is_usable (void); + + private: + basic_imgstream& write_header (void); + void check_consistency (void) const; + + void init (void); + + FILE *_stream; + bool _header; + bool _footer; + + static bool validate (lt_dlhandle h); + struct png_lib_handle + { + bool is_usable; + string message; + lt_dlhandle lib; + +#if HAVE_PNG_H + fundecl (png_uint_32, access_version_number, + void); + fundecl (png_structp, create_write_struct, + png_const_charp, png_voidp, png_error_ptr, png_error_ptr); + fundecl (png_infop , create_info_struct , + png_structp); + fundecl (void, destroy_write_struct, + png_structpp, png_infopp); + fundecl (void, init_io, + png_structp, FILE *); + fundecl (void, set_IHDR, + png_structp, png_infop, png_uint_32, png_uint_32, int, + int, int, int, int); + fundecl (void, set_pHYs, + png_structp, png_infop, png_uint_32, png_uint_32, int); + fundecl (void, set_invert_mono, png_structp); + fundecl (void, write_info, + png_structp, png_infop); + fundecl (void, write_row, + png_structp, png_bytep); + fundecl (void, write_flush, + png_structp); + fundecl (void, write_end, + png_structp, png_infop); +#endif /* HAVE_PNG_H */ + }; + static png_lib_handle *lib; + +#if HAVE_PNG_H + friend void set_error_handler (png_structp, png_infop); + + png_structp _png; + png_infop _info; + + static png_const_charp version_string; +#endif + }; + + +// implementation + +// This function needs to be expanded verbatim at the location it's +// invoked for the setjmp call to work as intended. +#if HAVE_PNG_H +#define set_error_handler(png, info) \ + { \ + if (!png || !info || setjmp (png_jmpbuf (png))) \ + { \ + pngstream::lib->destroy_write_struct (&png, &info); \ + png = NULL; \ + info = NULL; \ + throw std::ios_base::failure ("write error"); \ + } \ + } +#endif + +} // namespace iscan + +#endif /* !defined (iscan_pngstream_hh_included) */ diff --git a/lib/pnmstream.cc b/lib/pnmstream.cc new file mode 100644 index 0000000..0f4ee6b --- /dev/null +++ b/lib/pnmstream.cc @@ -0,0 +1,112 @@ +// pnmstream.cc -- image streams producing PNM files +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pnmstream.hh" + +#include + +namespace iscan +{ + pnmstream::pnmstream (FILE *fp, const string& name) + : _stream (fp), _header (false) + { + if (!_stream) throw std::invalid_argument ("invalid file handle"); + } + + pnmstream::~pnmstream (void) + { + fflush (_stream); + } + + basic_imgstream& + pnmstream::write (const byte_type *line, size_type n) + { + if (!line || 0 == n) return *this; + if (!_header) + { + write_header (); + } + size_t rv = fwrite (line, sizeof (byte_type), n, _stream); + if (n != rv) throw std::ios_base::failure ("write error"); + return *this; + } + + bool + pnmstream::is_usable (void) + { + return true; + } + + basic_imgstream& + pnmstream::write_header (void) + { + check_consistency (); + + string magic; + if (mono == _cspc) magic = "P4"; + if (grey == _cspc) magic = "P5"; + if (RGB == _cspc) magic = "P6"; + + int rv; + rv = fprintf (_stream, "%s\n%zd %zd\n", magic.c_str (), _h_sz, _v_sz); + if (0 > rv) throw std::ios_base::failure ("write error"); + if ("P4" != magic) + { + rv = fprintf (_stream, "%d\n", (1 << _bits) - 1); + if (0 > rv) throw std::ios_base::failure ("write error"); + } + _header = true; + return *this; + } + + void + pnmstream::check_consistency (void) const + { + if (!(mono == _cspc || grey == _cspc || RGB == _cspc)) + { + throw std::logic_error ("unsupported colour space"); + } + if (_bits > 16) + { + throw std::logic_error ("maximum bit depth exceeded"); + } + if (grey == _cspc || RGB == _cspc) + { + if (8 != _bits && 16 != _bits) + { + throw std::invalid_argument ("bit depth/colour space mismatch"); + } + } + } + +} // namespace iscan diff --git a/lib/pnmstream.hh b/lib/pnmstream.hh new file mode 100644 index 0000000..a2c6905 --- /dev/null +++ b/lib/pnmstream.hh @@ -0,0 +1,73 @@ +// pnmstream.hh -- image streams producing PNM files +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifndef iscan_pnmstream_hh_included +#define iscan_pnmstream_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "basic-imgstream.hh" + +#include +#include + +namespace iscan +{ + using std::string; + + class pnmstream : public basic_imgstream + { + public: + typedef basic_imgstream::byte_type byte_type; + typedef basic_imgstream::size_type size_type; + + explicit pnmstream (FILE *fp, const string& name = string ()); + virtual ~pnmstream (void); + + virtual basic_imgstream& write (const byte_type *line, size_type n); + + static bool is_usable (void); + + private: + basic_imgstream& write_header (void); + void check_consistency (void) const; + + FILE *_stream; + bool _header; + }; + +} // namespace iscan + +#endif /* !defined (iscan_pnmstream_hh_included) */ diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am new file mode 100644 index 0000000..c51b8ef --- /dev/null +++ b/lib/tests/Makefile.am @@ -0,0 +1,46 @@ +## Makefile.am -- an -*- automake -*- template for Makefile.in +## Copyright (C) 2011 SEIKO EPSON CORPORATION +## +## License: GPLv2+ +## Authors: AVASYS CORPORATION +## +## This file is part of the "Image Scan!" build infra-structure. +## +## The "Image Scan!" build infra-structure is free software. +## You can redistribute it and/or modify it under the terms of the GNU +## General Public License as published by the Free Software Foundation; +## either version 2 of the License or at your option any later version. +## +## 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 General Public License for more details. +## +## You ought to have received a copy of the GNU General Public License +## along with this package. If not, see . + +AM_CPPFLAGS = \ + -I$(top_srcdir)/lib + +TESTS = \ + run-test-pcx.sh + +check_PROGRAMS = \ + test-pcx + +test_pcx_LDADD = \ + ../libimage-stream.la \ + -lstdc++ +test_pcx_SOURCES = \ + test-pcx.cc \ + pnm.c \ + pnm.h + +EXTRA_DIST = \ + even-width.pbm \ + even-width.pgm \ + even-width.ppm \ + odd-width.pbm \ + odd-width.pgm \ + odd-width.ppm \ + run-test-pcx.sh diff --git a/lib/tests/even-width.pbm b/lib/tests/even-width.pbm new file mode 100644 index 0000000..65bf750 Binary files /dev/null and b/lib/tests/even-width.pbm differ diff --git a/lib/tests/even-width.pgm b/lib/tests/even-width.pgm new file mode 100644 index 0000000..3b4bf37 Binary files /dev/null and b/lib/tests/even-width.pgm differ diff --git a/lib/tests/even-width.ppm b/lib/tests/even-width.ppm new file mode 100644 index 0000000..0179c95 Binary files /dev/null and b/lib/tests/even-width.ppm differ diff --git a/lib/tests/odd-width.pbm b/lib/tests/odd-width.pbm new file mode 100644 index 0000000..76a669a Binary files /dev/null and b/lib/tests/odd-width.pbm differ diff --git a/lib/tests/odd-width.pgm b/lib/tests/odd-width.pgm new file mode 100644 index 0000000..9c29d53 Binary files /dev/null and b/lib/tests/odd-width.pgm differ diff --git a/lib/tests/odd-width.ppm b/lib/tests/odd-width.ppm new file mode 100644 index 0000000..0e945b9 Binary files /dev/null and b/lib/tests/odd-width.ppm differ diff --git a/lib/tests/pnm.c b/lib/tests/pnm.c new file mode 100644 index 0000000..d9ba1b6 --- /dev/null +++ b/lib/tests/pnm.c @@ -0,0 +1,141 @@ +/* pnm.c -- utility functions for testing purposes + * Copyright (C) 2019 SEIKO EPSON Corporation + * + * License: EPSON END USER SOFTWARE LICENSE + * Author : SEIKO EPSON Corporation + * + * This file is part of Image Scan! for Linux. + * It is distributed under the terms of the EPSON END USER SOFTWARE LICENSE. + * + * You should have received a verbatim copy of the EPSON END USER SOFTWARE + * LICENSE along with the software. + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pnm.h" + +#include +#include +#include + +pnm * +read_pnm (const char *file) +{ + pnm image, *rv; + + char *line = NULL; + size_t sz = 0; + + FILE *fp; + + if (file) + fp = fopen (file, "r"); + else + fp = stdin; + + if (!fp) return NULL; + + getline (&line, &sz, fp); /* file magic number */ + if ('P' != line[0]) return NULL; + + image.depth = 8; + /**/ if ('6' == line[1]) image.format = 1; + else if ('5' == line[1]) image.format = 0; + else if ('4' == line[1]) { image.format = 0; image.depth = 1; } + else return NULL; + + getline (&line, &sz, fp); + while ('#' == line[0]) /* skip header comments */ + getline (&line, &sz, fp); + sscanf (line, "%d%d", &image.pixels_per_line, &image.lines); + if (1 < image.depth) + { + getline (&line, &sz, fp); /* max color value */ + sscanf (line, "%d", &image.depth); + /**/ if (image.depth < (1 << 1)) image.depth = 1; + else if (image.depth < (1 << 8)) image.depth = 8; + else if (image.depth < (1 << 16)) image.depth = 16; + else return NULL; + } + + free (line); + sz = 0; + + image.bytes_per_line = (image.pixels_per_line + * (image.format ? 3 :1) + * (image.depth == 16 ? 2 : 1)); + if (1 == image.depth) + { + image.bytes_per_line = (image.bytes_per_line + 7) / 8; + } + image.size = image.bytes_per_line * image.lines; + + image.buffer = malloc (image.size); + if (!image.buffer) return NULL; + while (sz < image.size) + { + size_t s = fread (image.buffer, 1, image.size, fp); + if (0 == s) + { + if (ferror (fp)) + { + fprintf (stderr, "error reading image file\n"); + free (image.buffer); + return NULL; + } + if (feof (fp)) + { + fprintf (stderr, "premature end of image file\n"); + free (image.buffer); + return NULL; + } + } + sz += s; + } + fclose (fp); + + rv = (pnm *) malloc (sizeof (pnm)); + if (!rv) + { + free (image.buffer); + return NULL; + } + memcpy (rv, &image, sizeof (pnm)); + + return rv; +} + +void +write_pnm (const char *file, const pnm *image, const char *comment) +{ + FILE *fp; + + if (file) + fp = fopen (file, "w"); + else + fp = stdout; + + if (!fp) return; + + if (1 == image->depth) + { + fprintf (fp, "P4\n"); + } + else + { + fprintf (fp, "P%d\n", (image->format ? 6 : 5)); + } + fprintf (fp, "# %s\n", comment); + fprintf (fp, "%d %d\n", image->pixels_per_line, image->lines); + if (1 != image->depth) + { + fprintf (fp, "%d\n", (1 << image->depth) - 1); + } + fwrite (image->buffer, 1, image->bytes_per_line * image->lines, fp); + fflush (fp); + fclose (fp); +} diff --git a/lib/tests/pnm.h b/lib/tests/pnm.h new file mode 100644 index 0000000..fcc2b9e --- /dev/null +++ b/lib/tests/pnm.h @@ -0,0 +1,62 @@ +/* pnm.c -- utility functions for testing purposes + * Copyright (C) 2019 SEIKO EPSON Corporation + * + * License: EPSON END USER SOFTWARE LICENSE + * Author : SEIKO EPSON Corporation + * + * This file is part of Image Scan! for Linux. + * It is distributed under the terms of the EPSON END USER SOFTWARE LICENSE. + * + * You should have received a verbatim copy of the EPSON END USER SOFTWARE + * LICENSE along with the software. + */ + + +#ifndef pnm_h +#define pnm_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct { + void *buffer; /*!< image data */ + size_t size; /*!< number of bytes allocated for buffer */ + int format; /*!< zero for grayscale, non-zero for RGB */ + int32_t bytes_per_line; + int32_t pixels_per_line; + int32_t lines; + int32_t depth; + } pnm; + + /*! \brief Reads a PNM image from \a file + * + * Comments in the image file are allowed only between the first + * and second non-comment lines. That is, only a single block of + * consecutive comment lines after the first line is supported. + * + * Memory to hold the image data is acquired using malloc() and the + * caller is responsible for releasing this resource. + * + * Passing the \c NULL pointer for \a file will read from \c stdin. + */ + pnm * read_pnm (const char *file); + + /*! \brief Outputs a PNM \a image to \a file + * + * Files produced are reusable for input. + * + * Passing the \c NULL pointer for \a file will result in output on + * \c stdout. + */ + void write_pnm (const char *file, const pnm *image, + const char *comment); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* defined (pnm_h) */ diff --git a/lib/tests/run-test-pcx.sh b/lib/tests/run-test-pcx.sh new file mode 100755 index 0000000..c186ea2 --- /dev/null +++ b/lib/tests/run-test-pcx.sh @@ -0,0 +1,76 @@ +#! /bin/sh +# run-test-pcx.sh -- unit test for pcxstream class +# Copyright (C) 2011 SEIKO EPSON CORPORATION +# +# License: GPLv2+ +# Authors: AVASYS CORPORATION +# +# This file is part of the "Image Scan!" test suite. +# +# The "Image Scan!" test suite is free software. +# You can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation; +# either version 2 of the License or at your option any later version. +# +# 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 General Public License for more details. +# +# You ought to have received a copy of the GNU General Public License +# along with this package. If not, see . + +if ! test -x ./test-pcx; then + echo "FAIL: ./test-pcx not found, run make first" + exit 1 +fi + +DO_COMPARE=`type convert >/dev/null && echo yes` +if test "xyes" != x$DO_COMPARE; then + echo "INFO: compare test is skipped" +fi + +# Compare test is additional. +compare () { + FMT=`echo $SRC | sed 's,.*\.,,'` + convert "pcx:$DST" "$FMT:-" 2>/dev/null | cmp "$SRC" - + return $? +} + +# Make temporary output in $builddir unless overridden. Only clean up +# if tests succeed. +run_test () { + SRC="$input" + DST=`mktemp ${TMPDIR:=.}/pcx.XXXXXXXX` + + if ! ./test-pcx "$SRC" "$DST"; then + echo "FAIL: ./test-pcx $SRC $DST" + TEST_RESULT=FAIL + return + fi + if test "xyes" = x$DO_COMPARE; then + if ! compare "$SRC" "$DST"; then + echo "FAIL: compare $SRC $DST" + TEST_RESULT=FAIL + return + fi + fi + rm -f "$DST" +} + +# `make check` normally sets $srcdir +SRCDIR=${srcdir:=.} +TEST_RESULT=PASS +for input in \ + "$SRCDIR/even-width.pbm" \ + "$SRCDIR/even-width.pgm" \ + "$SRCDIR/even-width.ppm" \ + "$SRCDIR/odd-width.pbm" \ + "$SRCDIR/odd-width.pgm" \ + "$SRCDIR/odd-width.ppm" \ + ; do + run_test +done + +test "PASS" = "$TEST_RESULT" +exit $? diff --git a/lib/tests/test-pcx.cc b/lib/tests/test-pcx.cc new file mode 100644 index 0000000..86e9a0e --- /dev/null +++ b/lib/tests/test-pcx.cc @@ -0,0 +1,85 @@ +/* test-pcx.cc -- utility functions for testing purposes + * Copyright (C) 2019 SEIKO EPSON Corporation + * + * License: EPSON END USER SOFTWARE LICENSE + * Author : SEIKO EPSON Corporation + * + * This file is part of Image Scan! for Linux. + * It is distributed under the terms of the EPSON END USER SOFTWARE LICENSE. + * + * You should have received a verbatim copy of the EPSON END USER SOFTWARE + * LICENSE along with the software. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "file-opener.hh" +#include "imgstream.hh" +#include "pnm.h" + +int main (int argc, char *argv[]) +{ + using iscan::file_opener; + using iscan::imgstream; + + char *i = NULL; + char *o = NULL; + + pnm *img = NULL; + int x_res = 0; + int y_res = 0; + + if (argc != 3) + { + std::cerr << "usage: ./test-pcx input.pnm output.pcx" + << std::endl; + return EXIT_FAILURE; + } + i = argv[1]; + o = argv[2]; + + img = read_pnm (i); + if (!img) + return EXIT_FAILURE; + + x_res = 300; + y_res = 300; + + file_opener *fo = NULL; + imgstream *is = NULL; + iscan::file_format format = iscan::PCX; + fo = new file_opener (std::string (o)); + is = create_imgstream (*fo, format, false); + + is->next (); + is->size (img->pixels_per_line, img->lines); + is->depth (img->depth); + iscan::colour_space space; + if (1 == img->format) + space = iscan::RGB; + else if (0 == img->format && 1 == img->depth) + space = iscan::mono; + else if (0 == img->format) + space = iscan::grey; + else + return EXIT_FAILURE; + is->colour (space); + is->resolution (x_res, y_res); + + int l; + char *ptr = (char *)img->buffer; + for (l=0; llines; ++l) + { + is->write (ptr, img->bytes_per_line); + ptr += img->bytes_per_line; + } + is->flush (); + + delete is; + delete fo; + + return 0; +} diff --git a/lib/tiffstream.cc b/lib/tiffstream.cc new file mode 100644 index 0000000..d9bebf4 --- /dev/null +++ b/lib/tiffstream.cc @@ -0,0 +1,268 @@ +// tiffstream.cc -- image streams producing TIFF files +// Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tiffstream.hh" + +#include +#include +#include + +namespace iscan +{ + // Forward declaration of handlers and support functions. + static void handle_error (const char *module, const char *fmt, va_list ap); + static void handle_warning (const char *module, const char *fmt, va_list ap); + + + tiffstream::tiffstream (FILE *fp, const string& name) + : _stream (fp) + { + if (!_stream) throw std::invalid_argument ("invalid file handle"); + init (name); // handles HAVE_TIFFIO_H only stuff + } + + tiffstream::~tiffstream (void) + { +#if HAVE_TIFFIO_H + lib->Close (_tiff); +#endif + fflush (_stream); + } + + imgstream& + tiffstream::write (const byte_type *line, size_type n) + { + if (!line || 0 == n) return *this; + + if (0 == _page) + { + set_tags (); + } + +#if HAVE_TIFFIO_H + if (1 != lib->WriteScanline (_tiff, const_cast (line), _row, 1)) + { + throw std::ios_base::failure ("failure writing TIFF scanline"); + } + ++_row; +#endif /* HAVE_TIFFIO_H */ + return *this; + } + + void + tiffstream::next (void) + { +#if HAVE_TIFFIO_H + if (0 == _row) return; +#endif + + if (0 < _page) + { +#if HAVE_TIFFIO_H + if (1 != lib->WriteDirectory (_tiff)) + { + throw std::runtime_error ("failure writing TIFF directory"); + } +#endif + } + set_tags (); + } + + bool + tiffstream::is_usable (void) + { + if (lib) + { + return lib->is_usable; + } + + lib = new (std::nothrow) tiff_lib_handle (); + if (!lib) + { + return false; + } + + lib->is_usable = false; + lib->message = string (); + lib->lib = NULL; +#if HAVE_TIFFIO_H + try + { + imgstream::dlopen ("libtiff", validate); + } + catch (std::runtime_error& e) + { + lib->message = e.what (); + return lib->is_usable; + } +#endif /* HAVE_TIFFIO_H */ + + return lib->is_usable; + } + +#if HAVE_TIFFIO_H +#define funcsym(name) \ + lib->name = ((tiffstream::tiff_lib_handle::name##_f) \ + imgstream::dlsym (lib->lib, "TIFF"#name)); +#endif + + bool + tiffstream::validate (lt_dlhandle h) + { + if (!h) return false; + +#if HAVE_TIFFIO_H + lib->lib = h; + + funcsym (Open); + funcsym (FdOpen); + funcsym (Close); + funcsym (WriteDirectory); + funcsym (WriteScanline); + funcsym (Flush); + funcsym (SetField); + funcsym (SetErrorHandler); + funcsym (SetWarningHandler); + + lib->is_usable = (lib->Open + && lib->FdOpen + && lib->Close + && lib->WriteDirectory + && lib->WriteScanline + && lib->Flush + && lib->SetField + && lib->SetErrorHandler + && lib->SetWarningHandler); + + if (lib->is_usable) + { + lib->SetErrorHandler (handle_error); + lib->SetWarningHandler (handle_warning); + } +#endif /* HAVE_TIFFIO_H */ + + return lib->is_usable; + } + +#if HAVE_TIFFIO_H +#undef funcsym +#endif + + void + tiffstream::set_tags (void) + { + check_consistency (); + +#if HAVE_TIFFIO_H + lib->SetField (_tiff, TIFFTAG_SAMPLESPERPIXEL, (RGB == _cspc ? 3 : 1)); + + uint16 pm; + if (mono == _cspc) pm = PHOTOMETRIC_MINISWHITE; + if (grey == _cspc) pm = PHOTOMETRIC_MINISBLACK; + if (RGB == _cspc) pm = PHOTOMETRIC_RGB; + lib->SetField (_tiff, TIFFTAG_PHOTOMETRIC, pm); + + if (RGB == _cspc) + lib->SetField (_tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + + lib->SetField (_tiff, TIFFTAG_BITSPERSAMPLE, _bits); + + lib->SetField (_tiff, TIFFTAG_IMAGEWIDTH , _h_sz); + lib->SetField (_tiff, TIFFTAG_IMAGELENGTH, _v_sz); + lib->SetField (_tiff, TIFFTAG_ROWSPERSTRIP, 1); + + if (0 != _hres && 0 != _vres) + { + lib->SetField (_tiff, TIFFTAG_XRESOLUTION, float (_hres)); + lib->SetField (_tiff, TIFFTAG_YRESOLUTION, float (_vres)); + lib->SetField (_tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); + } + + lib->SetField (_tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + + _row = 0; +#endif /* HAVE_TIFFIO_H */ + ++_page; + + return; + } + + void + tiffstream::check_consistency (void) const + { + if (!(mono == _cspc || grey == _cspc || RGB == _cspc)) + { + throw std::logic_error ("unsupported colour space"); + } + if (!(1 == _bits || 8 == _bits)) + { + throw std::logic_error ("unsupported bit depth"); + } + } + + void + tiffstream::init (const string& name) + { + if (!is_usable ()) + { + throw std::runtime_error (lib->message); + } + +#if HAVE_TIFFIO_H + _row = 0; + // libtiff uses 'b' to signal big-endian, not binary as fopen()! + _tiff = lib->Open (name.c_str (), "w"); + if (!_tiff) throw std::bad_alloc (); +#endif + } + + tiffstream::tiff_lib_handle *tiffstream::lib = NULL; + + + // Definition of handlers and support functions. + + /*! \todo Implement when debugging framework has been worked out + */ + static void + handle_error (const char *module, const char *fmt, va_list ap) + { + } + + /*! \todo Implement when debugging framework has been worked out + */ + static void + handle_warning (const char *module, const char *fmt, va_list ap) + { + } + +} // namespace iscan diff --git a/lib/tiffstream.hh b/lib/tiffstream.hh new file mode 100644 index 0000000..657ca73 --- /dev/null +++ b/lib/tiffstream.hh @@ -0,0 +1,105 @@ +// tiffstream.hh -- image streams producing TIFF files +// Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other than esmod. + + +#ifndef iscan_tiffstream_hh_included +#define iscan_tiffstream_hh_included + +#ifndef __cplusplus +#error "This is a C++ header file; use a C++ compiler to compile it." +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imgstream.hh" + +#if HAVE_TIFFIO_H +#include +#include // for error and warning handlers +#endif + +namespace iscan +{ + using std::string; + + class tiffstream : public imgstream + { + public: + typedef basic_imgstream::byte_type byte_type; + typedef basic_imgstream::size_type size_type; + + tiffstream (FILE *fp, const string& name); + virtual ~tiffstream (void); + + virtual imgstream& write (const byte_type *line, size_type n); + + virtual void next (void); + + static bool is_usable (void); + + private: + void set_tags (void); + void check_consistency (void) const; + + void init (const string& name); + + FILE *_stream; + + static bool validate (lt_dlhandle h); + struct tiff_lib_handle + { + bool is_usable; + string message; + lt_dlhandle lib; + +#if HAVE_TIFFIO_H + fundecl (TIFF *, Open, const char *, const char *); + fundecl (TIFF *, FdOpen, const int, const char *, const char *); + fundecl (void, Close, TIFF *); + fundecl (int, WriteDirectory, TIFF *); + fundecl (int, WriteScanline, TIFF *, tdata_t, uint32, tsample_t); + fundecl (int, Flush, TIFF *); + fundecl (int, SetField, TIFF *, ttag_t, ...); + fundecl (TIFFErrorHandler, SetErrorHandler, TIFFErrorHandler); + fundecl (TIFFErrorHandler, SetWarningHandler, TIFFErrorHandler); +#endif /* HAVE_TIFFIO_H */ + }; + + static tiff_lib_handle *lib; + +#if HAVE_TIFFIO_H + TIFF *_tiff; + uint32 _row; +#endif + }; + +} // namespace iscan + +#endif // iscan_tiffstream_hh_included -- cgit v1.2.3