summaryrefslogtreecommitdiff
path: root/dviware/dvisvgm/src/GFReader.cpp
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /dviware/dvisvgm/src/GFReader.cpp
Initial commit
Diffstat (limited to 'dviware/dvisvgm/src/GFReader.cpp')
-rw-r--r--dviware/dvisvgm/src/GFReader.cpp359
1 files changed, 359 insertions, 0 deletions
diff --git a/dviware/dvisvgm/src/GFReader.cpp b/dviware/dvisvgm/src/GFReader.cpp
new file mode 100644
index 0000000000..3cba8e5069
--- /dev/null
+++ b/dviware/dvisvgm/src/GFReader.cpp
@@ -0,0 +1,359 @@
+/*************************************************************************
+** GFReader.cpp **
+** **
+** This file is part of dvisvgm -- a fast DVI to SVG converter **
+** Copyright (C) 2005-2019 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 <algorithm>
+#include <sstream>
+#include "FixWord.hpp"
+#include "GFReader.hpp"
+#include "Length.hpp"
+#include "SignalHandler.hpp"
+
+using namespace std;
+
+
+struct GFCommand {
+ void (GFReader::*method)(int);
+ int numBytes;
+};
+
+
+/** Converts a scaled value to double */
+static inline double scaled2double (int32_t scaled) {
+ return double(scaled)/(1 << 16);
+}
+
+
+GFReader::GFReader (istream &is) : _in(is), _insideCharDef(false), _penDown(false)
+{
+ _minX = _maxX = _minY = _maxY = _x = _y = 0;
+ _currentChar = 0;
+ _designSize = 0;
+ _hppp = _vppp = 0;
+ _checksum = 0;
+}
+
+
+uint32_t GFReader::readUnsigned (int bytes) {
+ uint32_t ret = 0;
+ for (int i=bytes-1; i >= 0 && !_in.eof(); i--) {
+ uint32_t b = _in.get();
+ ret |= b << (8*i);
+ }
+ return ret;
+}
+
+
+int32_t GFReader::readSigned (int bytes) {
+ uint32_t ret = _in.get();
+ if (ret & 128) // negative value?
+ ret |= 0xffffff00;
+ for (int i=bytes-2; i >= 0 && !_in.eof(); i--)
+ ret = (ret << 8) | _in.get();
+ return int32_t(ret);
+}
+
+
+string GFReader::readString (int bytes) {
+ bytes = max(0, bytes);
+ string str(bytes, '\0');
+ _in.read(&str[0], bytes);
+ return str;
+}
+
+
+/** Reads a single GF command from the current position of the input stream and calls the
+ * corresponding cmdFOO method.
+ * @return opcode of the executed command */
+int GFReader::executeCommand () {
+ SignalHandler::instance().check();
+ /* Each cmdFOO command reads the necessary number of bytes from the stream so executeCommand
+ doesn't need to know the exact GF command format. Some cmdFOO methods are used for multiple
+ GF commands because they only differ in the size of their parameters. */
+ static const GFCommand commands[] = {
+ {&GFReader::cmdPaint, 1}, {&GFReader::cmdPaint, 2}, {&GFReader::cmdPaint, 3}, // 64-66
+ {&GFReader::cmdBoc, 0}, {&GFReader::cmdBoc1, 0}, // 67-68
+ {&GFReader::cmdEoc, 0}, // 69
+ {&GFReader::cmdSkip, 0}, {&GFReader::cmdSkip, 1}, {&GFReader::cmdSkip, 2},{&GFReader::cmdSkip, 3}, // 70-73
+ {&GFReader::cmdXXX, 1}, {&GFReader::cmdXXX, 2}, {&GFReader::cmdXXX, 3}, {&GFReader::cmdXXX, 4}, // 239-242
+ {&GFReader::cmdYYY, 0}, // 243
+ {&GFReader::cmdNop, 0}, // 244
+ {&GFReader::cmdCharLoc, 0}, {&GFReader::cmdCharLoc0, 0}, // 245-246
+ {&GFReader::cmdPre, 0}, {&GFReader::cmdPost, 0}, {&GFReader::cmdPostPost, 0} // 247-249
+ };
+
+ int opcode = _in.get();
+ if (opcode < 0) // at end of file?
+ throw GFException("unexpected end of file");
+
+ if (opcode >= 0 && opcode <= 63)
+ cmdPaint0(opcode);
+ else if (opcode >= 74 && opcode <= 238)
+ cmdNewRow(opcode-74);
+ else if (opcode >= 250) {
+ ostringstream oss;
+ oss << "undefined GF command (opcode " << opcode << ")";
+ throw GFException(oss.str());
+ }
+ else {
+ int offset = opcode <= 73 ? 64 : 239-(73-64+1);
+ const GFCommand &cmd = commands[opcode-offset];
+ if (cmd.method)
+ (this->*cmd.method)(cmd.numBytes);
+ }
+ return opcode;
+}
+
+
+bool GFReader::executeChar (uint8_t c) {
+ _in.clear();
+ if (_charInfoMap.empty())
+ executePostamble(); // read character info
+ _in.clear();
+ auto it = _charInfoMap.find(c);
+ if (_in && it != _charInfoMap.end()) {
+ _in.seekg(it->second.location);
+ while (executeCommand() != 69); // execute all commands until eoc is reached
+ return true;
+ }
+ return false;
+}
+
+
+bool GFReader::executeAllChars () {
+ _in.clear();
+ if (_charInfoMap.empty())
+ executePostamble(); // read character info
+ _in.clear();
+ if (_in) {
+ _in.seekg(0);
+ while (executeCommand() != 248); // execute all commands until postamble is reached
+ return true;
+ }
+ return false;
+}
+
+
+bool GFReader::executePreamble () {
+ _in.clear();
+ if (!_in)
+ return false;
+ _in.seekg(0);
+ executeCommand();
+ return true;
+}
+
+
+bool GFReader::executePostamble () {
+ _in.clear();
+ if (!_in)
+ return false;
+ _in.seekg(-1, ios::end);
+ while (_in.peek() == 223) // skip fill bytes
+ _in.seekg(-1, ios::cur);
+ if (_in.peek() != 131)
+ throw GFException("invalid identification byte in postpost");
+ _in.seekg(-5, ios::cur); // now on postpost
+ if (_in.get() != 249)
+ throw GFException("invalid GF file");
+ uint32_t q = readUnsigned(4); // pointer to begin of postamble
+ _in.seekg(q); // now on begin of postamble
+ while (executeCommand() != 249); // execute all commands until postpost is reached
+ return true;
+}
+
+
+/** Returns the width of character c in PS point units */
+double GFReader::getCharWidth (uint32_t c) const {
+ auto it = _charInfoMap.find(c%256);
+ return it == _charInfoMap.end() ? 0 : it->second.width*getDesignSize()/(1<<24);
+}
+
+///////////////////////
+
+
+/** Reads the preamble. */
+void GFReader::cmdPre (int) {
+ uint32_t i = readUnsigned(1);
+ if (i != 131)
+ throw GFException("invalid identification byte in preamble");
+ else {
+ uint32_t k = readUnsigned(1);
+ string s = readString(k);
+ preamble(s);
+ }
+}
+
+
+/** Reads the postamble. */
+void GFReader::cmdPost (int) {
+ readUnsigned(4); // pointer to byte after final eoc
+ _designSize = double(FixWord(readUnsigned(4)))*Length::pt2bp; // design size of font in points
+ _checksum = readUnsigned(4); // checksum
+ _hppp = scaled2double(readUnsigned(4))/Length::pt2bp; // horizontal pixels per PS point
+ _vppp = scaled2double(readUnsigned(4))/Length::pt2bp; // vertical pixels per PS point
+ _in.seekg(16, ios::cur); // skip x and y bounds
+ postamble();
+}
+
+
+/** Reads trailing bytes at end of stream. */
+void GFReader::cmdPostPost (int) {
+ readUnsigned(4); // pointer to begin of postamble
+ uint32_t i = readUnsigned(1);
+ if (i != 131)
+ throw GFException("invalid identification byte in postpost");
+ while (readUnsigned(1) == 223); // skip fill bytes
+}
+
+
+/** Inverts "paint color" (black to white or vice versa) of n pixels
+ * and advances the cursor by n.
+ * @param[in] n number of pixels to be inverted */
+void GFReader::cmdPaint0 (int n) {
+ if (!_insideCharDef)
+ throw GFException("character-related command outside BOC and EOC");
+ if (_penDown) // set pixels?
+ _bitmap.setBits(_y, _x, n);
+ _x += n;
+ _penDown = !_penDown; // invert pen state
+}
+
+
+/** Inverts "paint color" (black to white or vice versa) of n pixels
+ * and advances the cursor by n. The number n of pixels is read from
+ * the input stream.
+ * @param[in] len size of n in bytes */
+void GFReader::cmdPaint (int len) {
+ uint32_t pixels = readUnsigned(len);
+ cmdPaint0(pixels);
+}
+
+
+/** Beginning of character (generic format). */
+void GFReader::cmdBoc (int) {
+ _currentChar = readSigned(4);
+ readSigned(4); // pointer to previous boc with same c mod 256
+ _minX = readSigned(4);
+ _maxX = readSigned(4);
+ _minY = readSigned(4);
+ _maxY = readSigned(4);
+ _x = _minX;
+ _y = _maxY;
+ _penDown = false;
+ _bitmap.resize(_minX, _maxX, _minY, _maxY);
+ _insideCharDef = true;
+ beginChar(_currentChar);
+}
+
+
+/** Beginning of character (compact format). */
+void GFReader::cmdBoc1 (int) {
+ _currentChar = readUnsigned(1);
+ uint32_t dx = readUnsigned(1);
+ _maxX = readUnsigned(1);
+ _minX = _maxX - dx;
+ uint32_t dy = readUnsigned(1);
+ _maxY = readUnsigned(1);
+ _minY = _maxY - dy;
+ _x = _minX;
+ _y = _maxY;
+ _penDown = false;
+ _bitmap.resize(_minX, _maxX, _minY, _maxY);
+ _insideCharDef = true;
+ beginChar(_currentChar);
+}
+
+
+/** End of character. */
+void GFReader::cmdEoc (int) {
+ if (!_insideCharDef)
+ throw GFException("misplaced EOC");
+ _insideCharDef = false;
+ endChar(_currentChar);
+}
+
+
+/** Moves cursor to the beginning of a following row and sets
+ * paint color to white.
+ * @param[in] len if 0: move to next row, otherwise: number of bytes to read.
+ * The read value denotes the number of rows to be skipped. */
+void GFReader::cmdSkip (int len) {
+ if (!_insideCharDef)
+ throw GFException("character-related command outside BOC and EOC");
+ if (len == 0)
+ _y--;
+ else
+ _y -= readUnsigned(len)+1;
+ _x = _minX;
+ _penDown = false;
+}
+
+
+/** Moves cursor to pixel number 'col' in the next row and sets
+ * the paint color to black.
+ * @param[in] col pixel/column number */
+void GFReader::cmdNewRow (int col) {
+ if (!_insideCharDef)
+ throw GFException("character-related command outside BOC and EOC");
+ _x = _minX + col ;
+ _y--;
+ _penDown = true;
+}
+
+
+void GFReader::cmdXXX (int len) {
+ uint32_t n = readUnsigned(len);
+ string str = readString(n);
+ special(str);
+}
+
+
+void GFReader::cmdYYY (int) {
+ int32_t y = readSigned(4);
+ numspecial(y);
+}
+
+
+/** Does nothing. */
+void GFReader::cmdNop (int) {
+}
+
+
+/** Reads character locator (part of postamble) */
+void GFReader::cmdCharLoc0 (int) {
+ uint8_t c = readUnsigned(1); // character code mod 256
+ uint8_t dm = readUnsigned(1); // = dx/65536
+ int32_t w = readSigned(4); // (1<<24)*characterWidth/designSize
+ int32_t p = readSigned(4); // pointer to begin of (last) character data
+ int32_t dx = 65536*dm; // horizontal escapement
+ int32_t dy = 0; // vertical escapement
+ _charInfoMap.emplace(c, CharInfo(dx, dy, w, p));
+}
+
+
+/** Reads character locator (part of postamble) */
+void GFReader::cmdCharLoc (int) {
+ uint8_t c = readUnsigned(1); // character code mod 256
+ int32_t dx = readSigned(4); // horizontal escapement (scaled pixel units)
+ int32_t dy = readSigned(4); // vertical escapement (scaled pixel units)
+ int32_t w = readSigned(4); // (1<<24)*characterWidth/designSize
+ int32_t p = readSigned(4); // pointer to begin of (last) character data
+ _charInfoMap.emplace(c, CharInfo(dx, dy, w, p));
+}