diff options
Diffstat (limited to 'lib/pdfstream.cc')
-rw-r--r-- | lib/pdfstream.cc | 351 |
1 files changed, 351 insertions, 0 deletions
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 <sstream> + +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 |