/*************************************************************************
** EPSFile.cpp                                                          **
**                                                                      **
** This file is part of dvisvgm -- the DVI to SVG converter             **
** Copyright (C) 2005-2013 Martin Gieseking <martin.gieseking@uos.de>   **
**                                                                      **
** This program 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 3 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 should have received a copy of the GNU General Public License    **
** along with this program; if not, see <http://www.gnu.org/licenses/>. **
*************************************************************************/

#include <cstring>
#include <istream>
#include <limits>
#include "EPSFile.h"
#include "InputBuffer.h"
#include "InputReader.h"

using namespace std;


/** Reads a little-endian 32-bit integer from the given input stream. */
static UInt32 getUInt32 (istream &is) {
	UInt32 value=0;
	char buf[4];
	is.read(buf, 4);
	for (int i=0; i < 4; i++)
		value |= ((buf[i] & 255) << (8*i));
	return value;
}


static size_t getline (istream &is, char *line, size_t n) {
	char buf[512];
	is.get(buf, min(n, (size_t)512)-1);
	n = is.gcount();
	size_t linelen=0;
	for (size_t i=0; i < n; i++)
		if (isprint(buf[i]))
			line[linelen++] = buf[i];
	line[linelen] = 0;
	if (is.peek() == '\n')
		is.get();
	else
		is.ignore(numeric_limits<size_t>::max(), '\n');
	return linelen;
}


EPSFile::EPSFile (const std::string& fname) : _ifs(fname.c_str(), ios::binary), _headerValid(false), _offset(0), _pslength(0)
{
	if (_ifs) {
		if (getUInt32(_ifs) != 0xC6D3D0C5)     // no binary header present?
			_ifs.seekg(0, ios_base::beg);       // go back to the first byte
		else {
			_offset = getUInt32(_ifs);          // stream offset where PS part of the file begins
			_pslength = getUInt32(_ifs);        // length of PS section in bytes
			_ifs.seekg(_offset, ios_base::beg); // continue reading at the beginning of the PS section
		}
		string str;
		str += _ifs.get();
		str += _ifs.get();
		_headerValid = (str == "%!");
		_ifs.seekg(0, ios_base::beg);
	}
}


/** Returns an input stream for the EPS file. The stream pointer is automatically moved
 *  to the beginning of the ASCII (PostScript) part of the file. */
istream& EPSFile::istream () const {
	_ifs.clear();
	_ifs.seekg(_offset, ios_base::beg);
	return _ifs;
}


/** Extracts the bounding box information from the DSC header/footer (if present)
 *  @param[out] the extracted bounding box
 *  @return true if %%BoundingBox data could be read successfully */
bool EPSFile::bbox (BoundingBox &box) const {
	std::istream &is = EPSFile::istream();
	if (is) {
		char buf[64];
		while (is) {
			size_t linelen = getline(is, buf, 64);
			if (strncmp(buf, "%%BoundingBox:", 14) == 0) {
				CharInputBuffer ib(buf, linelen);
				BufferInputReader ir(ib);
				ir.skip(14);
				ir.skipSpace();
				if (!ir.check("(atend)", true)) {
					int val[4];
					for (int i=0; i < 4; i++) {
						ir.skipSpace();
						ir.parseInt(val[i]);
					}
					box = BoundingBox(val[0], val[1], val[2], val[3]);
					return true;
				}
			}
		}
	}
	return false;
}
