aboutsummaryrefslogtreecommitdiff
path: root/lib/pdf/writer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pdf/writer.cc')
-rw-r--r--lib/pdf/writer.cc236
1 files changed, 236 insertions, 0 deletions
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