summaryrefslogtreecommitdiff
path: root/dviware/dvisvgm/src/DVIReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dviware/dvisvgm/src/DVIReader.cpp')
-rw-r--r--dviware/dvisvgm/src/DVIReader.cpp706
1 files changed, 706 insertions, 0 deletions
diff --git a/dviware/dvisvgm/src/DVIReader.cpp b/dviware/dvisvgm/src/DVIReader.cpp
new file mode 100644
index 0000000000..38862839a0
--- /dev/null
+++ b/dviware/dvisvgm/src/DVIReader.cpp
@@ -0,0 +1,706 @@
+/*************************************************************************
+** DVIReader.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 <cstdarg>
+#include <fstream>
+#include <sstream>
+#include "Color.hpp"
+#include "DVIActions.hpp"
+#include "DVIReader.hpp"
+#include "Font.hpp"
+#include "FontManager.hpp"
+#include "HashFunction.hpp"
+#include "VectorStream.hpp"
+
+using namespace std;
+
+DVIReader::DVIReader (istream &is) : BasicDVIReader(is)
+{
+ _inPage = false;
+ _dvi2bp = 0.0;
+ _inPostamble = false;
+ _currFontNum = 0;
+ _currPageNum = 0;
+ _mag = 1;
+ executePreamble();
+ collectBopOffsets();
+ executePostamble();
+}
+
+
+int DVIReader::executeCommand () {
+ int opcode = BasicDVIReader::executeCommand();
+ return opcode;
+}
+
+
+/** Executes all DVI commands read from the input stream. */
+void DVIReader::executeAll () {
+ int opcode = 0;
+ while (!eof() && opcode >= 0) {
+ try {
+ opcode = executeCommand();
+ }
+ catch (const InvalidDVIFileException &e) {
+ // end of stream reached
+ opcode = -1;
+ }
+ }
+}
+
+
+/** Reads and executes the commands of a single page.
+ * This methods stops reading after the page's eop command has been executed.
+ * @param[in] n number of page to be executed (1-based)
+ * @returns true if page was read successfully */
+bool DVIReader::executePage (unsigned n) {
+ clearStream(); // reset all status bits
+ if (!isStreamValid())
+ throw DVIException("invalid DVI file");
+ if (n < 1 || n > numberOfPages())
+ return false;
+
+ seek(_bopOffsets[n-1]); // goto bop of n-th page
+ _inPostamble = false; // not in postamble
+ _currPageNum = n;
+ while (executeCommand() != OP_EOP);
+ return true;
+}
+
+
+void DVIReader::executePreamble () {
+ clearStream();
+ if (isStreamValid()) {
+ seek(0);
+ if (readByte() == OP_PRE) {
+ cmdPre(0);
+ return;
+ }
+ }
+ throw DVIException("invalid DVI file");
+}
+
+
+/** Moves stream pointer to begin of postamble */
+void DVIReader::goToPostamble () {
+ clearStream();
+ if (!isStreamValid())
+ throw DVIException("invalid DVI file");
+
+ seek(-1, ios::end); // stream pointer to last byte
+ int count=0;
+ while (peek() == DVI_FILL) { // skip fill bytes
+ seek(-1, ios::cur);
+ count++;
+ }
+ if (count < 4) // the standard requires at least 4 trailing fill bytes
+ throw DVIException("missing fill bytes at end of file");
+
+ seek(-4, ios::cur); // now at first byte of q (pointer to begin of postamble)
+ uint32_t q = readUnsigned(4); // pointer to begin of postamble
+ seek(q); // now at begin of postamble
+}
+
+
+/** Reads and executes the commands of the postamble. */
+void DVIReader::executePostamble () {
+ goToPostamble();
+ while (executeCommand() != OP_POSTPOST); // executes all commands until post_post (= 249) is reached
+}
+
+
+/** Collects and records the file offsets of all bop commands. */
+void DVIReader::collectBopOffsets () {
+ goToPostamble();
+ _bopOffsets.push_back(tell()); // also add offset of postamble
+ readByte(); // skip post command
+ uint32_t offset = readUnsigned(4); // offset of final bop
+ while (int32_t(offset) != -1) { // not yet on first bop?
+ _bopOffsets.push_back(offset); // record offset
+ seek(offset); // now on previous bop
+ if (readByte() != OP_BOP)
+ throw DVIException("bop offset at "+to_string(offset)+" doesn't point to bop command" );
+ seek(40, ios::cur); // skip the 10 \count values => now on offset of previous bop
+ uint32_t prevOffset = readUnsigned(4);
+ if ((prevOffset >= offset && int32_t(prevOffset) != -1))
+ throw DVIException("invalid bop offset at "+to_string(tell()-static_cast<streamoff>(4)));
+ offset = prevOffset;
+ }
+ reverse(_bopOffsets.begin(), _bopOffsets.end());
+}
+
+
+/** Computes a hash value for a given page. The hash algorithm is selected by
+ * a HashFunction object which will also contain the resulting hash value if
+ * this function returns true.
+ * @param[in] pageno number of page to process (1-based)
+ * @param[in,out] hashFunc hash function to use
+ * @return true on success, hashFunc contains the resulting hash value */
+bool DVIReader::computePageHash (size_t pageno, HashFunction &hashFunc) {
+ if (pageno == 0 || pageno > numberOfPages())
+ return false;
+
+ hashFunc.reset();
+ clearStream();
+ seek(_bopOffsets[pageno-1]+45); // now on first command after bop of selected page
+ const size_t BUFSIZE = 4096;
+ char buf[BUFSIZE];
+ size_t numBytes = numberOfPageBytes(pageno-1)-46; // number of bytes excluding bop and eop
+ while (numBytes > 0) {
+ getInputStream().read(buf, min(numBytes, BUFSIZE));
+ hashFunc.update(buf, getInputStream().gcount());
+ numBytes -= getInputStream().gcount();
+ }
+ return true;
+}
+
+
+/////////////////////////////////////
+
+/** Reads and executes DVI preamble command.
+ * Format: pre ver[1] num[4] den[4] mag[4] cmtlen[1] cmt[cmtlen] */
+void DVIReader::cmdPre (int) {
+ uint8_t id = readUnsigned(1);
+ setDVIVersion(DVIVersion(id)); // identification number
+ uint32_t numer = readUnsigned(4); // numerator units of measurement
+ uint32_t denom = readUnsigned(4); // denominator units of measurement
+ if (denom == 0)
+ throw DVIException("denominator of measurement unit is zero");
+ _mag = readUnsigned(4); // magnification
+ uint32_t k = readUnsigned(1); // length of following comment
+ string comment = readString(k);
+ // 1 dviunit * num/den == multiples of 0.0000001m
+ // 1 dviunit * _dvibp: length of 1 dviunit in PS points * _mag/1000
+ _dvi2bp = numer/254000.0*72.0/denom*_mag/1000.0;
+ dviPre(id, numer, denom, _mag, comment);
+}
+
+
+/** Reads and executes DVI postamble command.
+ * Format: post p[4] num[4] den[4] mag[4] ph[4] pw[4] sd[2] np[2] */
+void DVIReader::cmdPost (int) {
+ uint32_t prevBopOffset = readUnsigned(4);
+ uint32_t numer = readUnsigned(4);
+ uint32_t denom = readUnsigned(4);
+ if (denom == 0)
+ throw DVIException("denominator of measurement unit is zero");
+ _mag = readUnsigned(4);
+ uint32_t pageHeight = readUnsigned(4); // height of tallest page in dvi units
+ uint32_t pageWidth = readUnsigned(4); // width of widest page in dvi units
+ uint16_t stackDepth = readUnsigned(2); // max. stack depth required
+ uint16_t numPages = readUnsigned(2);
+ if (numPages != (numberOfPages() & 0xffff))
+ throw DVIException("page count in postamble doesn't match actual number of pages");
+
+ // 1 dviunit * num/den == multiples of 0.0000001m
+ // 1 dviunit * _dvi2bp: length of 1 dviunit in PS points * _mag/1000
+ _dvi2bp = numer/254000.0*72.0/denom*_mag/1000.0;
+ _inPostamble = true;
+ dviPost(stackDepth, numPages, pageWidth*_dvi2bp, pageHeight*_dvi2bp, _mag, numer, denom, prevBopOffset);
+}
+
+
+/** Reads and executes DVI post_post command.
+ * Format: post_post q[4] i[1] 223[>=4] */
+void DVIReader::cmdPostPost (int) {
+ _inPostamble = false;
+ uint32_t postOffset = readUnsigned(4); // pointer to begin of postamble
+ uint8_t id = readUnsigned(1);
+ setDVIVersion(DVIVersion(id)); // identification byte
+ while (readUnsigned(1) == DVI_FILL); // skip fill bytes (223), eof bit should be set now
+ dviPostPost(DVIVersion(id), postOffset);
+}
+
+
+/** Reads and executes Begin-Of-Page command.
+ * Format: bop c0[+4] ... c9[+4] p[+4] */
+void DVIReader::cmdBop (int) {
+ vector<int32_t> c(10);
+ for (int i=0; i < 10; i++)
+ c[i] = readSigned(4);
+ int32_t prevBopOffset = readSigned(4); // pointer to peceeding bop (-1 in case of first page)
+ _dviState.reset(); // set all DVI registers to 0
+ while (!_stateStack.empty())
+ _stateStack.pop();
+ _currFontNum = 0;
+ _inPage = true;
+ dviBop(c, prevBopOffset);
+}
+
+
+/** Reads and executes End-Of-Page command. */
+void DVIReader::cmdEop (int) {
+ if (!_stateStack.empty())
+ throw DVIException("stack not empty at end of page");
+ _inPage = false;
+ dviEop();
+}
+
+
+/** Reads and executes push command. */
+void DVIReader::cmdPush (int) {
+ _stateStack.push(_dviState);
+ dviPush();
+}
+
+
+/** Reads and executes pop command (restores pushed position information). */
+void DVIReader::cmdPop (int) {
+ if (_stateStack.empty())
+ throw DVIException("stack empty at pop command");
+ _dviState = _stateStack.top();
+ _stateStack.pop();
+ dviPop();
+}
+
+
+/** Helper function that handles charaters from virtual fonts (VF).
+ * It is called by the cmdSetChar and cmdPutChar methods.
+ * @param[in] font current font (corresponding to _currFontNum)
+ * @param[in] c character to typeset */
+void DVIReader::putVFChar (Font *font, uint32_t c) {
+ if (auto vf = dynamic_cast<VirtualFont*>(font)) { // is current font a virtual font?
+ if (const vector<uint8_t> *dvi = vf->getDVI(c)) { // try to get DVI snippet that represents character c
+ FontManager &fm = FontManager::instance();
+ DVIState savedState = _dviState; // save current cursor position
+ _dviState.x = _dviState.y = _dviState.w = _dviState.z = 0;
+ int savedFontNum = _currFontNum; // save current font number
+ fm.enterVF(vf); // enter VF font number context
+ setFont(fm.vfFirstFontNum(vf), SetFontMode::VF_ENTER);
+ double savedScale = _dvi2bp;
+ // DVI units in virtual fonts are multiples of 1^(-20) times the scaled size of the VF
+ _dvi2bp = vf->scaledSize()/(1 << 20);
+ VectorInputStream<uint8_t> vis(*dvi);
+ istream &is = replaceStream(vis);
+ try {
+ executeAll(); // execute DVI fragment
+ }
+ catch (const DVIException &e) {
+ // Message::estream(true) << "invalid dvi in vf: " << e.getMessage() << endl; // @@
+ }
+ replaceStream(is); // restore previous input stream
+ _dvi2bp = savedScale; // restore previous scale factor
+ fm.leaveVF(); // restore previous font number context
+ setFont(savedFontNum, SetFontMode::VF_LEAVE); // restore previous font number
+ _dviState = savedState; // restore previous cursor position
+ }
+ }
+}
+
+
+/** Reads and executes set_char_x command. Puts a character at the current
+ * position and advances the cursor.
+ * @param[in] c character to set
+ * @throw DVIException if method is called ouside a bop/eop pair */
+void DVIReader::cmdSetChar0 (int c) {
+ if (!_inPage)
+ throw DVIException("setchar outside of page");
+ Font *font = FontManager::instance().getFont(_currFontNum);
+ dviSetChar0(c, font); // template method that may trigger further actions
+ putVFChar(font, c); // further character processing if current font is a virtual font
+ moveRight(font->charWidth(c)*font->scaleFactor()*_mag/1000.0, MoveMode::SETCHAR);
+}
+
+
+/** Reads and executes setx command. Puts a character at the current
+ * position and advances the cursor.
+ * @param[in] len number of parameter bytes (possible values: 1-4)
+ * @throw DVIException if method is called ouside a bop/eop pair */
+void DVIReader::cmdSetChar (int len) {
+ if (!_inPage)
+ throw DVIException("setchar outside of page");
+ // According to the dvi specification all character codes are unsigned
+ // except len == 4. At the moment all char codes are treated as unsigned...
+ uint32_t c = readUnsigned(len); // if len == 4 c may be signed
+ Font *font = FontManager::instance().getFont(_currFontNum);
+ dviSetChar(c, font); // template method that may trigger further actions
+ putVFChar(font, c); // further character processing if current font is a virtual font
+ moveRight(font->charWidth(c)*font->scaleFactor()*_mag/1000.0, MoveMode::SETCHAR);
+}
+
+
+/** Reads and executes putx command. Puts a character at the current
+ * position but doesn't change the cursor position.
+ * @param[in] len number of parameter bytes (possible values: 1-4)
+ * @throw DVIException if method is called ouside a bop/eop pair */
+void DVIReader::cmdPutChar (int len) {
+ if (!_inPage)
+ throw DVIException("putchar outside of page");
+ // According to the dvi specification all character codes are unsigned
+ // except len == 4. At the moment all char codes are treated as unsigned...
+ int32_t c = readUnsigned(len);
+ Font *font = FontManager::instance().getFont(_currFontNum);
+ dviPutChar(c, font);
+ putVFChar(font, c);
+}
+
+
+/** Reads and executes set_rule command. Puts a solid rectangle at the current
+ * position and updates the cursor position.
+ * Format: set_rule h[+4] w[+4]
+ * @throw DVIException if method is called ouside a bop/eop pair */
+void DVIReader::cmdSetRule (int) {
+ if (!_inPage)
+ throw DVIException("set_rule outside of page");
+ double height = _dvi2bp*readSigned(4);
+ double width = _dvi2bp*readSigned(4);
+ dviSetRule(height, width);
+ moveRight(width, MoveMode::CHANGEPOS);
+}
+
+
+/** Reads and executes set_rule command. Puts a solid rectangle at the current
+ * position but leaves the cursor position unchanged.
+ * Format: put_rule h[+4] w[+4]
+ * @throw DVIException if method is called ouside a bop/eop pair */
+void DVIReader::cmdPutRule (int) {
+ if (!_inPage)
+ throw DVIException("put_rule outside of page");
+ double height = _dvi2bp*readSigned(4);
+ double width = _dvi2bp*readSigned(4);
+ dviPutRule(height, width);
+}
+
+
+void DVIReader::moveRight (double dx, MoveMode) {
+ switch (_dviState.d) {
+ case WritingMode::LR: _dviState.h += dx; break;
+ case WritingMode::TB: _dviState.v += dx; break;
+ case WritingMode::BT: _dviState.v -= dx; break;
+ }
+}
+
+
+void DVIReader::moveDown (double dy, MoveMode) {
+ switch (_dviState.d) {
+ case WritingMode::LR: _dviState.v += dy; break;
+ case WritingMode::TB: _dviState.h -= dy; break;
+ case WritingMode::BT: _dviState.h += dy; break;
+ }
+}
+
+
+void DVIReader::cmdRight (int len) {
+ double dx = _dvi2bp*readSigned(len);
+ moveRight(dx, MoveMode::CHANGEPOS);
+ dviRight(dx);
+}
+
+
+void DVIReader::cmdDown (int len) {
+ double dy = _dvi2bp*readSigned(len);
+ moveDown(dy, MoveMode::CHANGEPOS);
+ dviDown(dy);
+}
+
+
+void DVIReader::cmdNop (int) {dviNop();}
+void DVIReader::cmdX0 (int) {moveRight(_dviState.x, MoveMode::CHANGEPOS); dviX0();}
+void DVIReader::cmdY0 (int) {moveDown(_dviState.y, MoveMode::CHANGEPOS); dviY0();}
+void DVIReader::cmdW0 (int) {moveRight(_dviState.w, MoveMode::CHANGEPOS); dviW0();}
+void DVIReader::cmdZ0 (int) {moveDown(_dviState.z, MoveMode::CHANGEPOS); dviZ0();}
+
+
+void DVIReader::cmdX (int len) {
+ double dx = _dvi2bp*readSigned(len);
+ _dviState.x = dx;
+ moveRight(dx, MoveMode::CHANGEPOS);
+ dviX(dx);
+}
+
+
+void DVIReader::cmdY (int len) {
+ double dy = _dvi2bp*readSigned(len);
+ _dviState.y = dy;
+ moveDown(dy, MoveMode::CHANGEPOS);
+ dviY(dy);
+}
+
+
+void DVIReader::cmdW (int len) {
+ double dx = _dvi2bp*readSigned(len);
+ _dviState.w = dx;
+ moveRight(dx, MoveMode::CHANGEPOS);
+ dviW(dx);
+}
+
+
+void DVIReader::cmdZ (int len) {
+ double dy = _dvi2bp*readSigned(len);
+ _dviState.z = dy;
+ moveDown(dy, MoveMode::CHANGEPOS);
+ dviZ(dy);
+}
+
+
+/** Sets the text orientation (horizontal, vertical).
+ * This command is only available in DVI version 3 (created by pTeX) */
+void DVIReader::cmdDir (int) {
+ uint8_t wmode = readUnsigned(1);
+ if (wmode == 4) // yoko mode (4) equals default LR mode (0)
+ wmode = 0;
+ if (wmode == 2 || wmode > 3) {
+ ostringstream oss;
+ oss << "invalid writing mode value " << wmode << " (0, 1, 3, or 4 expected)";
+ throw DVIException(oss.str());
+ }
+ _dviState.d = (WritingMode)wmode;
+ dviDir(_dviState.d);
+}
+
+
+void DVIReader::cmdXXX (int len) {
+ if (!_inPage)
+ throw DVIException("special outside of page");
+ uint32_t numBytes = readUnsigned(len);
+ string str = readString(numBytes);
+ dviXXX(str);
+}
+
+
+/** Change the current font.
+ * @param[in] fontnum local number of font to select
+ * @param[in] mode info on command that triggered the font change
+ * @throw DVIException if font number is undefined */
+void DVIReader::setFont (int fontnum, SetFontMode mode) {
+ if (const Font *font = FontManager::instance().getFont(fontnum)) {
+ _currFontNum = fontnum;
+ dviFontNum(uint32_t(fontnum), mode, font);
+ }
+ else {
+ ostringstream oss;
+ oss << "undefined font number " << fontnum;
+ throw DVIException(oss.str());
+ }
+}
+
+
+/** Selects a previously defined font by its number.
+ * @param[in] fontnum font number
+ * @throw DVIException if font number is undefined */
+void DVIReader::cmdFontNum0 (int fontnum) {
+ setFont(fontnum, SetFontMode::SF_SHORT);
+}
+
+
+/** Selects a previously defined font.
+ * @param[in] len size of font number variable (in bytes)
+ * @throw DVIException if font number is undefined */
+void DVIReader::cmdFontNum (int len) {
+ uint32_t fontnum = readUnsigned(len);
+ setFont(fontnum, SetFontMode::SF_LONG);
+}
+
+
+/** Helper function to handle a font definition.
+ * @param[in] fontnum local font number
+ * @param[in] name font name
+ * @param[in] cs checksum to be compared with TFM checksum
+ * @param[in] ds design size in PS point units
+ * @param[in] ss scaled size in PS point units */
+const Font* DVIReader::defineFont (uint32_t fontnum, const string &name, uint32_t cs, double ds, double ss) {
+ FontManager &fm = FontManager::instance();
+ Font *font = fm.getFont(fontnum);
+ if (!font) {
+ int id = fm.registerFont(fontnum, name, cs, ds, ss);
+ font = fm.getFontById(id);
+ if (auto vf = dynamic_cast<VirtualFont*>(font)) {
+ // read vf file, register its font and character definitions
+ fm.enterVF(vf);
+ ifstream ifs(vf->path(), ios::binary);
+ VFReader vfReader(ifs);
+ vfReader.replaceActions(this);
+ vfReader.executeAll();
+ fm.leaveVF();
+ }
+ }
+ return font;
+}
+
+
+/** Defines a new font.
+ * @param[in] len size of font number variable (in bytes) */
+void DVIReader::cmdFontDef (int len) {
+ uint32_t fontnum = readUnsigned(len); // font number
+ uint32_t checksum = readUnsigned(4); // font checksum (to be compared with corresponding TFM checksum)
+ uint32_t ssize = readUnsigned(4); // scaled size of font in DVI units
+ uint32_t dsize = readUnsigned(4); // design size of font in DVI units
+ uint32_t pathlen = readUnsigned(1); // length of font path
+ uint32_t namelen = readUnsigned(1); // length of font name
+ string path = readString(pathlen); // path to font file
+ string fontname = readString(namelen);
+ const Font *font = defineFont(fontnum, fontname, checksum, dsize*_dvi2bp, ssize*_dvi2bp);
+ dviFontDef(fontnum, checksum, font);
+}
+
+
+/** This template method is called by the VFReader after reading a font definition from a VF file.
+ * @param[in] fontnum local font number
+ * @param[in] path path to font file
+ * @param[in] name font name
+ * @param[in] checksum checksum to be compared with TFM checksum
+ * @param[in] dsize design size in PS point units
+ * @param[in] ssize scaled size in PS point units */
+void DVIReader::defineVFFont (uint32_t fontnum, const string &path, const string &name, uint32_t checksum, double dsize, double ssize) {
+ if (const VirtualFont *vf = FontManager::instance().getVF())
+ defineFont(fontnum, name, checksum, dsize, ssize * vf->scaleFactor());
+}
+
+
+/** This template method is called by the VFReader after reading a character definition from a VF file.
+ * @param[in] c character number
+ * @param[in] dvi DVI fragment describing the character */
+void DVIReader::defineVFChar (uint32_t c, vector<uint8_t> &&dvi) {
+ FontManager::instance().assignVFChar(c, std::move(dvi));
+}
+
+
+/** XDV extension: includes image or pdf file.
+ * parameters: box[1] matrix[4][6] p[2] len[2] path[l] */
+void DVIReader::cmdXPic (int) {
+ uint8_t box = readUnsigned(1); // box
+ vector<int32_t> matrix(6);
+ for (int i=0; i < 6; i++) // matrix
+ matrix[i] = readSigned(4);
+ int16_t page = readSigned(2); // page number
+ uint16_t len = readUnsigned(2);
+ string path = readString(len); // path to image/pdf file
+ dviXPic(box, matrix, page, path);
+}
+
+
+/** XDV extension: defines a native font */
+void DVIReader::cmdXFontDef (int) {
+ int32_t fontnum = readSigned(4);
+ double ptsize = _dvi2bp*readUnsigned(4);
+ uint16_t flags = readUnsigned(2);
+ uint8_t psname_len = readUnsigned(1);
+ uint8_t fmname_len = getDVIVersion() == DVI_XDV5 ? readUnsigned(1) : 0; // length of family name
+ uint8_t stname_len = getDVIVersion() == DVI_XDV5 ? readUnsigned(1) : 0; // length of style name
+ string fontname = readString(psname_len);
+ uint32_t fontIndex=0;
+ if (getDVIVersion() == DVI_XDV5)
+ seek(fmname_len+stname_len, ios::cur);
+ else
+ fontIndex = readUnsigned(4);
+ FontStyle style;
+ Color color;
+ if (flags & 0x0100) { // vertical?
+ }
+ if (flags & 0x0200) { // colored?
+ // The font color must not interfere with color specials. If the font color is not black,
+ // all color specials should be ignored, i.e. glyphs of a non-black fonts have a fixed color
+ // that can't be changed by color specials.
+ uint32_t rgba = readUnsigned(4);
+ color.setRGB(uint8_t(rgba >> 24), uint8_t((rgba >> 16) & 0xff), uint8_t((rgba >> 8) & 0xff));
+ }
+ if (flags & 0x1000) // extend?
+ style.extend = _dvi2bp*readSigned(4);
+ if (flags & 0x2000) // slant?
+ style.slant = _dvi2bp*readSigned(4);
+ if (flags & 0x4000) // embolden?
+ style.bold = _dvi2bp*readSigned(4);
+ if ((flags & 0x0800) && (getDVIVersion() == DVI_XDV5)) { // variations?
+ uint16_t num_variations = readSigned(2);
+ for (int i=0; i < num_variations; i++)
+ readUnsigned(4);
+ }
+ const Font *font = FontManager::instance().getFont(fontnum);
+ if (!font) {
+ FontManager::instance().registerFont(fontnum, fontname, fontIndex, ptsize, style, color);
+ font = FontManager::instance().getFont(fontnum);
+ }
+ dviXFontDef(fontnum, dynamic_cast<const NativeFont*>(font));
+}
+
+
+/** XDV extension: prints an array of characters where each character
+ * can take independent x and y coordinates.
+ * parameters: w[4] n[2] (dx,dy)[(4+4)n] glyphs[2n] */
+void DVIReader::cmdXGlyphArray (int) {
+ vector<double> dx, dy;
+ vector<uint16_t> glyphs;
+ double width = putGlyphArray(false, dx, dy, glyphs);
+ if (Font *font = FontManager::instance().getFont(_currFontNum))
+ dviXGlyphArray(dx, dy, glyphs, *font);
+ else
+ throw DVIException("missing setfont prior to xglypharray");
+ moveRight(width, MoveMode::SETCHAR);
+}
+
+
+/** XDV extension: prints an array/string of characters where each character
+ * can take independent x coordinates whereas all share a single y coordinate.
+ * parameters: w[4] n[2] dx[4n] dy[4] glyphs[2n] */
+void DVIReader::cmdXGlyphString (int) {
+ vector<double> dx, dy;
+ vector<uint16_t> glyphs;
+ double width = putGlyphArray(true, dx, dy, glyphs);
+ if (Font *font = FontManager::instance().getFont(_currFontNum))
+ dviXGlyphString(dx, glyphs, *font);
+ else
+ throw DVIException("missing setfont prior to xglyphstring");
+ moveRight(width, MoveMode::SETCHAR);
+}
+
+
+/** XDV extension: Same as cmdXGlyphArray plus a leading array of UTF-16 characters
+ * that specify the "actual text" represented by the glyphs to be printed. It usually
+ * contains the text with special characters (like ligatures) expanded so that it
+ * can be used for text search, plain text copy & paste etc. This XDV command was
+ * introduced with XeTeX 0.99995 and can be triggered by <tt>\\XeTeXgenerateactualtext1</tt>.
+ * parameters: l[2] chars[2l] w[4] n[2] (dx,dy)[8n] glyphs[2n] */
+void DVIReader::cmdXTextAndGlyphs (int) {
+ uint16_t l = readUnsigned(2); // number of chars
+ vector<uint16_t> chars(l);
+ for (int i=0; i < l; i++)
+ chars[i] = readUnsigned(2);
+ vector<double> x, y;
+ vector<uint16_t> glyphs;
+ double width = putGlyphArray(false, x, y, glyphs);
+ if (Font *font = FontManager::instance().getFont(_currFontNum))
+ dviXTextAndGlyphs(x, y, chars, glyphs, *font);
+ else
+ throw DVIException("missing setfont prior to xtextandglyphs");
+ moveRight(width, MoveMode::SETCHAR);
+}
+
+
+/** Implements the common functionality of cmdXGlyphArray, cmdXGlyphString, and cmdXTextAndGlyphs.
+ * @param[in] xonly indicates if the characters share a single y coordinate (xonly==true)
+ * @param[out] dx relative horizontal positions of each glyph
+ * @param[out] dy relative vertical positions of each glyph
+ * @param[out] glyphs FreeType indices of the glyphs to typeset
+ * @return total width of the glyph array */
+double DVIReader::putGlyphArray (bool xonly, vector<double> &dx, vector<double> &dy, vector<uint16_t> &glyphs) {
+ double strwidth = _dvi2bp*readSigned(4);
+ uint16_t num_glyphs = readUnsigned(2);
+ dx.resize(num_glyphs);
+ dy.resize(num_glyphs);
+ glyphs.resize(num_glyphs);
+ for (int i=0; i < num_glyphs; i++) {
+ dx[i] = readSigned(4)*_dvi2bp;
+ dy[i] = xonly ? 0 : readSigned(4)*_dvi2bp;
+ }
+ for (int i=0; i < num_glyphs; i++)
+ glyphs[i] = readUnsigned(2);
+ return strwidth;
+}