aboutsummaryrefslogtreecommitdiff
path: root/lib/pdf
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2023-01-06 10:02:49 +0200
committerIgor Pashev <pashev.igor@gmail.com>2023-01-06 10:04:59 +0200
commit1145733c29db0a678537ce99ff60e21613f622a8 (patch)
tree63d5d6c324629d4eef1354db3c97f857d6016a34 /lib/pdf
downloadiscan-1145733c29db0a678537ce99ff60e21613f622a8.tar.gz
Import iscan 2.30.4-2
Diffstat (limited to 'lib/pdf')
-rw-r--r--lib/pdf/Makefile.am44
-rw-r--r--lib/pdf/array.cc115
-rw-r--r--lib/pdf/array.hh87
-rw-r--r--lib/pdf/dictionary.cc123
-rw-r--r--lib/pdf/dictionary.hh93
-rw-r--r--lib/pdf/object.cc103
-rw-r--r--lib/pdf/object.hh120
-rw-r--r--lib/pdf/primitive.cc100
-rw-r--r--lib/pdf/primitive.hh92
-rw-r--r--lib/pdf/writer.cc236
-rw-r--r--lib/pdf/writer.hh165
11 files changed, 1278 insertions, 0 deletions
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 <config.h>
+#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 <config.h>
+#endif
+
+#include "object.hh"
+#include "primitive.hh"
+
+#include <vector>
+
+namespace iscan
+{
+
+namespace pdf
+{
+
+/*! Defines a PDF array object [p 58].
+ */
+class array : public object
+{
+private:
+ typedef std::vector<object *> 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 <config.h>
+#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 <config.h>
+#endif
+
+#include "object.hh"
+#include "primitive.hh"
+
+#include <map>
+
+namespace iscan
+{
+
+namespace pdf
+{
+
+/*! Defines a pdf dictionary object [p 59]
+ */
+class dictionary : public object
+{
+private:
+ typedef std::map<const char *, object *> 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 <config.h>
+#endif
+
+#include "object.hh"
+
+#include <stdexcept>
+
+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 <config.h>
+#endif
+
+#include <cstdlib>
+#include <cstdio>
+
+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 <config.h>
+#endif
+
+#include "primitive.hh"
+
+#include <sstream>
+
+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 <config.h>
+#endif
+
+#include "object.hh"
+
+#include <string>
+
+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 <config.h>
+#endif
+
+#include "writer.hh"
+
+#include <sstream>
+#include <stdexcept>
+
+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 <config.h>
+#endif
+
+#include "object.hh"
+#include "primitive.hh"
+#include "dictionary.hh"
+
+#include <map>
+
+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<size_t, size_t> 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