1 /*************************************************************************
2 ** DVIReader.cpp                                                        **
3 **                                                                      **
4 ** This file is part of dvisvgm -- the DVI to SVG converter             **
5 ** Copyright (C) 2005-2015 Martin Gieseking <martin.gieseking@uos.de>   **
6 **                                                                      **
7 ** This program is free software; you can redistribute it and/or        **
8 ** modify it under the terms of the GNU General Public License as       **
9 ** published by the Free Software Foundation; either version 3 of       **
10 ** the License, or (at your option) any later version.                  **
11 **                                                                      **
12 ** This program is distributed in the hope that it will be useful, but  **
13 ** WITHOUT ANY WARRANTY; without even the implied warranty of           **
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the         **
15 ** GNU General Public License for more details.                         **
16 **                                                                      **
17 ** You should have received a copy of the GNU General Public License    **
18 ** along with this program; if not, see <http://www.gnu.org/licenses/>. **
19 *************************************************************************/
20 
21 #include <config.h>
22 #include <algorithm>
23 #include <cstdarg>
24 #include <fstream>
25 #include <iostream>
26 #include <sstream>
27 #include "Color.h"
28 #include "DVIActions.h"
29 #include "DVIReader.h"
30 #include "Font.h"
31 #include "FontManager.h"
32 #include "Message.h"
33 #include "SignalHandler.h"
34 #include "VectorStream.h"
35 #include "macros.h"
36 #include "types.h"
37 
38 
39 using namespace std;
40 
41 bool DVIReader::COMPUTE_PROGRESS = false;
42 
43 
DVIReader(istream & is,DVIActions * a)44 DVIReader::DVIReader (istream &is, DVIActions *a) : BasicDVIReader(is), _actions(a)
45 {
46 	_inPage = false;
47 	_pageHeight = _pageWidth = 0;
48 	_dvi2bp = 0.0;
49 	_tx = _ty = 0;    // no cursor translation
50 	_prevYPos = numeric_limits<double>::min();
51 	_inPostamble = false;
52 	_currFontNum = 0;
53 	_currPageNum = 0;
54 	_pagePos = 0;
55 	_mag = 1;
56 	executePreamble();
57 	collectBopOffsets();
58 	executePostamble();
59 }
60 
61 
replaceActions(DVIActions * a)62 DVIActions* DVIReader::replaceActions (DVIActions *a) {
63 	DVIActions *prev_actions = _actions;
64 	_actions = a;
65 	return prev_actions;
66 }
67 
68 
69 /** Reads a single DVI command from the current position of the input stream and calls the
70  *  corresponding cmdFOO method.
71  *  @return opcode of the command executed */
executeCommand()72 int DVIReader::executeCommand () {
73 	SignalHandler::instance().check();
74 	CommandHandler handler;
75 	int param; // parameter of handler
76 	const streampos cmdpos = tell();
77 	int opcode = evalCommand(handler, param);
78 	(this->*handler)(param);
79 	if (_dviState.v+_ty != _prevYPos) {
80 		_tx = _ty = 0;
81 		_prevYPos = _dviState.v;
82 	}
83 	if (COMPUTE_PROGRESS && _inPage && _actions) {
84 		size_t pagelen = numberOfPageBytes(_currPageNum-1);
85 		// ensure progress() is called at 0%
86 		if (opcode == 139)  // bop?
87 			_actions->progress(0, pagelen);
88 		// ensure progress() is called at 100%
89 		if (peek() == 140)  // eop reached?
90 			_pagePos = pagelen;
91 		else
92 			_pagePos += tell()-cmdpos;
93 		_actions->progress(_pagePos, pagelen);
94 	}
95 	return opcode;
96 }
97 
98 
99 /** Executes all DVI commands read from the input stream. */
executeAll()100 void DVIReader::executeAll () {
101 	int opcode = 0;
102 	while (!eof() && opcode >= 0) {
103 		try {
104 			opcode = executeCommand();
105 		}
106 		catch (const InvalidDVIFileException &e) {
107 			// end of stream reached
108 			opcode = -1;
109 		}
110 	}
111 }
112 
113 
114 /** Reads and executes the commands of a single page.
115  *  This methods stops reading after the page's eop command has been executed.
116  *  @param[in] n number of page to be executed
117  *  @returns true if page was read successfully */
executePage(unsigned n)118 bool DVIReader::executePage (unsigned n) {
119 	clearStream();    // reset all status bits
120 	if (!isStreamValid())
121 		throw DVIException("invalid DVI file");
122 	if (n < 1 || n > numberOfPages())
123 		return false;
124 
125 	seek(_bopOffsets[n-1]);           // goto bop of n-th page
126 	_inPostamble = false;             // not in postamble
127 	_currPageNum = n;
128 	while (executeCommand() != 140);  // 140 == eop
129 	return true;
130 }
131 
132 
executePreamble()133 void DVIReader::executePreamble () {
134 	clearStream();
135 	if (isStreamValid()) {
136 		seek(0);
137 		if (readByte() == 247) {
138 			cmdPre(0);
139 			return;
140 		}
141 	}
142 	throw DVIException("invalid DVI file");
143 }
144 
145 
146 /** Moves stream pointer to begin of postamble */
to_postamble(StreamReader & reader)147 static void to_postamble (StreamReader &reader) {
148 	reader.clearStream();
149 	if (!reader.isStreamValid())
150 		throw DVIException("invalid DVI file");
151 
152 	reader.seek(-1, ios::end);          // stream pointer to last byte
153 	int count=0;
154 	while (reader.peek() == 223) {
155 		reader.seek(-1, ios::cur);       // skip fill bytes
156 		count++;
157 	}
158 	if (count < 4)  // the standard requires at least 4 trailing fill bytes
159 		throw DVIException("missing fill bytes at end of file");
160 
161 	reader.seek(-4, ios::cur);          // now on first byte of q (pointer to begin of postamble)
162 	UInt32 q = reader.readUnsigned(4);  // pointer to begin of postamble
163 	reader.seek(q);                     // now on begin of postamble
164 }
165 
166 
167 /** Reads and executes the commands of the postamble. */
executePostamble()168 void DVIReader::executePostamble () {
169 	to_postamble(*this);
170 	while (executeCommand() != 249);  // executes all commands until post_post (= 249) is reached
171 }
172 
173 
174 /** Collects and records the file offsets of all bop commands. */
collectBopOffsets()175 void DVIReader::collectBopOffsets () {
176 	to_postamble(*this);
177 	_bopOffsets.push_back(tell());     // also add offset of postamble
178 	readByte();                        // skip post command
179 	UInt32 offset = readUnsigned(4);   // offset of final bop
180 	while ((Int32)offset > 0) {        // not yet on first bop?
181 		_bopOffsets.push_back(offset);  // record offset
182 		seek(offset+41);                // skip bop command and the 10 \count values => now on offset of previous bop
183 		offset = readUnsigned(4);
184 	}
185 	reverse(_bopOffsets.begin(), _bopOffsets.end());
186 }
187 
188 
189 /** Returns the current x coordinate in PS point units.
190  *  This is the horizontal position where the next output would be placed. */
getXPos() const191 double DVIReader::getXPos () const {
192 	return _dviState.h+_tx;
193 }
194 
195 
196 /** Returns the current y coordinate in PS point units.
197  *  This is the vertical position where the next output would be placed. */
getYPos() const198 double DVIReader::getYPos () const {
199 	return _dviState.v+_ty;
200 }
201 
202 
203 /////////////////////////////////////
204 
205 /** Reads and executes DVI preamble command.
206  *  Format: pre ver[1] num[4] den[4] mag[4] cmtlen[1] cmt[cmtlen] */
cmdPre(int)207 void DVIReader::cmdPre (int) {
208 	setDVIFormat((DVIFormat)readUnsigned(1)); // identification number
209 	UInt32 num = readUnsigned(4);  // numerator units of measurement
210 	UInt32 den = readUnsigned(4);  // denominator units of measurement
211 	if (den == 0)
212 		throw DVIException("denominator of measurement unit is zero");
213 	_mag = readUnsigned(4);        // magnification
214 	UInt32 k   = readUnsigned(1);  // length of following comment
215 	string cmt = readString(k);    // comment
216 	// 1 dviunit * num/den == multiples of 0.0000001m
217 	// 1 dviunit * _dvibp: length of 1 dviunit in PS points * _mag/1000
218 	_dvi2bp = num/254000.0*72.0/den*_mag/1000.0;
219 	if (_actions)
220 		_actions->preamble(cmt);
221 }
222 
223 
224 /** Reads and executes DVI postamble command.
225  *  Format: post p[4] num[4] den[4] mag[4] ph[4] pw[4] sd[2] np[2] */
cmdPost(int)226 void DVIReader::cmdPost (int) {
227 	readUnsigned(4);  // skip pointer to previous bop
228 	UInt32 num = readUnsigned(4);
229 	UInt32 den = readUnsigned(4);
230 	if (den == 0)
231 		throw DVIException("denominator of measurement unit is zero");
232 	_mag = readUnsigned(4);
233 	_pageHeight = readUnsigned(4); // height of tallest page in dvi units
234 	_pageWidth  = readUnsigned(4); // width of widest page in dvi units
235 	readUnsigned(2);               // skip max. stack depth
236 	if (readUnsigned(2) != numberOfPages())
237 		throw DVIException("page count entry doesn't match actual number of pages");
238 
239 	// 1 dviunit * num/den == multiples of 0.0000001m
240 	// 1 dviunit * _dvi2bp: length of 1 dviunit in PS points * _mag/1000
241 	_dvi2bp = num/254000.0*72.0/den*_mag/1000.0;
242 	_pageHeight *= _dvi2bp;   // to pt units
243 	_pageWidth *= _dvi2bp;
244 	_inPostamble = true;
245 	if (_actions)
246 		_actions->postamble();
247 }
248 
249 
250 /** Reads and executes DVI post_post command.
251  *  Format: post_post q[4] i[1] 223[>=4] */
cmdPostPost(int)252 void DVIReader::cmdPostPost (int) {
253 	_inPostamble = false;
254 	readUnsigned(4);   // pointer to begin of postamble
255 	setDVIFormat((DVIFormat)readUnsigned(1));  // identification byte
256 	while (readUnsigned(1) == 223);  // skip fill bytes (223), eof bit should be set now
257 }
258 
259 
260 /** Reads and executes Begin-Of-Page command.
261  *  Format: bop c0[+4] ... c9[+4] p[+4] */
cmdBop(int)262 void DVIReader::cmdBop (int) {
263 	Int32 c[10];
264 	for (int i=0; i < 10; i++)
265 		c[i] = readSigned(4);
266 	readSigned(4);        // pointer to peceeding bop (-1 in case of first page)
267 	_dviState.reset();    // set all DVI registers to 0
268 	while (!_stateStack.empty())
269 		_stateStack.pop();
270 	_currFontNum = 0;
271 	_inPage = true;
272 	_pagePos = 0;
273 	beginPage(_currPageNum, c);
274 	if (_actions)
275 		_actions->beginPage(_currPageNum, c);
276 }
277 
278 
279 /** Reads and executes End-Of-Page command. */
cmdEop(int)280 void DVIReader::cmdEop (int) {
281 	if (!_stateStack.empty())
282 		throw DVIException("stack not empty at end of page");
283 	_inPage = false;
284 	endPage(_currPageNum);
285 	if (_actions)
286 		_actions->endPage(_currPageNum);
287 }
288 
289 
290 /** Reads and executes push command. */
cmdPush(int)291 void DVIReader::cmdPush (int) {
292 	_stateStack.push(_dviState);
293 }
294 
295 
296 /** Reads and executes pop command (restores pushed position information). */
cmdPop(int)297 void DVIReader::cmdPop (int) {
298 	if (_stateStack.empty())
299 		throw DVIException("stack empty at pop command");
300 	else {
301 		DVIState prevState = _dviState;
302 		_dviState = _stateStack.top();
303 		_stateStack.pop();
304 		if (_actions) {
305 			if (prevState.h != _dviState.h)
306 				_actions->moveToX(_dviState.h + _tx);
307 			if (prevState.v != _dviState.v)
308 				_actions->moveToY(_dviState.v + _ty);
309 			if (prevState.d != _dviState.d)
310 				_actions->setTextOrientation(_dviState.d != WMODE_LR);
311 		}
312 	}
313 }
314 
315 
316 /** Helper function that actually sets/puts a charater. It is called by the
317  *  cmdSetChar and cmdPutChar methods.
318  * @param[in] c character to typeset
319  * @param[in] moveCursor if true, register h is increased by the character width
320  * @throw DVIException if method is called ouside a bop/eop pair */
putChar(UInt32 c,bool moveCursor)321 void DVIReader::putChar (UInt32 c, bool moveCursor) {
322 	if (!_inPage)
323 		throw DVIException("set_char/put_char outside of page");
324 
325 	FontManager &fm = FontManager::instance();
326 	Font *font = fm.getFont(_currFontNum);
327 	if (!font)
328 		throw DVIException("no font selected");
329 
330 	if (VirtualFont *vf = dynamic_cast<VirtualFont*>(font)) {    // is current font a virtual font?
331 		vector<UInt8> *dvi = const_cast<vector<UInt8>*>(vf->getDVI(c)); // get DVI snippet that describes character c
332 		if (dvi) {
333 			DVIState pos = _dviState;        // save current cursor position
334 			_dviState.x = _dviState.y = _dviState.w = _dviState.z = 0;
335 			int save_fontnum = _currFontNum; // save current font number
336 			fm.enterVF(vf);                  // new font number context
337 			cmdFontNum0(fm.vfFirstFontNum(vf));
338 			double save_scale = _dvi2bp;
339 			// DVI units in virtual fonts are multiples of 1^(-20) times the scaled size of the VF
340 			_dvi2bp = vf->scaledSize()/(1 << 20);
341 
342 			VectorInputStream<UInt8> vis(*dvi);
343 			istream &is = replaceStream(vis);
344 			try {
345 				executeAll();            // execute DVI fragment
346 			}
347 			catch (const DVIException &e) {
348 				//					Message::estream(true) << "invalid dvi in vf: " << e.getMessage() << endl; // @@
349 			}
350 			replaceStream(is);          // restore previous input stream
351 			_dvi2bp = save_scale;       // restore previous scale factor
352 			fm.leaveVF();               // restore previous font number context
353 			cmdFontNum0(save_fontnum);  // restore previous font number
354 			_dviState = pos;            // restore previous cursor position
355 		}
356 	}
357 	else if (_actions)
358 		_actions->setChar(_dviState.h+_tx, _dviState.v+_ty, c, _dviState.d != WMODE_LR, font);
359 
360 	if (moveCursor) {
361 		double dist = font->charWidth(c) * font->scaleFactor() * _mag/1000.0;
362 		switch (_dviState.d) {
363 			case WMODE_LR: _dviState.h += dist; break;
364 			case WMODE_TB: _dviState.v += dist; break;
365 			case WMODE_BT: _dviState.v -= dist; break;
366 		}
367 	}
368 }
369 
370 
371 /** Reads and executes set_char_x command. Puts a character at the current
372  *  position and advances the cursor.
373  *  @param[in] c character to set
374  *  @throw DVIException if method is called ouside a bop/eop pair */
cmdSetChar0(int c)375 void DVIReader::cmdSetChar0 (int c) {
376 	putChar(c, true);
377 }
378 
379 
380 /** Reads and executes setx command. Puts a character at the current
381  *  position and advances the cursor.
382  *  @param[in] len number of parameter bytes (possible values: 1-4)
383  *  @throw DVIException if method is called ouside a bop/eop pair */
cmdSetChar(int len)384 void DVIReader::cmdSetChar (int len) {
385 	// According to the dvi specification all character codes are unsigned
386 	// except len == 4. At the moment all char codes are treated as unsigned...
387 	UInt32 c = readUnsigned(len); // if len == 4 c may be signed
388 	putChar(c, true);
389 }
390 
391 
392 /** Reads and executes putx command. Puts a character at the current
393  *  position but doesn't change the cursor position.
394  *  @param[in] len number of parameter bytes (possible values: 1-4)
395  *  @throw DVIException if method is called ouside a bop/eop pair */
cmdPutChar(int len)396 void DVIReader::cmdPutChar (int len) {
397 	// According to the dvi specification all character codes are unsigned
398 	// except len == 4. At the moment all char codes are treated as unsigned...
399 	Int32 c = readUnsigned(len);
400 	putChar(c, false);
401 }
402 
403 
404 /** Reads and executes set_rule command. Puts a solid rectangle at the current
405  *  position and updates the cursor position.
406  *  Format: set_rule h[+4] w[+4]
407  *  @throw DVIException if method is called ouside a bop/eop pair */
cmdSetRule(int)408 void DVIReader::cmdSetRule (int) {
409 	if (!_inPage)
410 		throw DVIException("set_rule outside of page");
411 	double height = _dvi2bp*readSigned(4);
412 	double width  = _dvi2bp*readSigned(4);
413 	if (_actions && height > 0 && width > 0)
414 		_actions->setRule(_dviState.h+_tx, _dviState.v+_ty, height, width);
415 	moveRight(width);
416 }
417 
418 
419 /** Reads and executes set_rule command. Puts a solid rectangle at the current
420  *  position but leaves the cursor position unchanged.
421  *  Format: put_rule h[+4] w[+4]
422  *  @throw DVIException if method is called ouside a bop/eop pair */
cmdPutRule(int)423 void DVIReader::cmdPutRule (int) {
424 	if (!_inPage)
425 		throw DVIException("put_rule outside of page");
426 	double height = _dvi2bp*readSigned(4);
427 	double width  = _dvi2bp*readSigned(4);
428 	if (_actions && height > 0 && width > 0)
429 		_actions->setRule(_dviState.h+_tx, _dviState.v+_ty, height, width);
430 }
431 
432 
moveRight(double dx)433 void DVIReader::moveRight (double dx) {
434 	switch (_dviState.d) {
435 		case WMODE_LR: _dviState.h += dx; break;
436 		case WMODE_TB: _dviState.v += dx; break;
437 		case WMODE_BT: _dviState.v -= dx; break;
438 	}
439 	if (_actions) {
440 		if (_dviState.d == WMODE_LR)
441 			_actions->moveToX(_dviState.h+_tx);
442 		else
443 			_actions->moveToY(_dviState.v+_ty);
444 	}
445 }
446 
447 
moveDown(double dy)448 void DVIReader::moveDown (double dy) {
449 	switch (_dviState.d) {
450 		case WMODE_LR: _dviState.v += dy; break;
451 		case WMODE_TB: _dviState.h -= dy; break;
452 		case WMODE_BT: _dviState.h += dy; break;
453 	}
454 	if (_actions) {
455 		if (_dviState.d == WMODE_LR)
456 			_actions->moveToY(_dviState.v+_ty);
457 		else
458 			_actions->moveToX(_dviState.h+_tx);
459 	}
460 }
461 
462 
cmdRight(int len)463 void DVIReader::cmdRight (int len) {moveRight(_dvi2bp*readSigned(len));}
cmdDown(int len)464 void DVIReader::cmdDown (int len)  {moveDown(_dvi2bp*readSigned(len));}
cmdX0(int)465 void DVIReader::cmdX0 (int)        {moveRight(_dviState.x);}
cmdY0(int)466 void DVIReader::cmdY0 (int)        {moveDown(_dviState.y);}
cmdW0(int)467 void DVIReader::cmdW0 (int)        {moveRight(_dviState.w);}
cmdZ0(int)468 void DVIReader::cmdZ0 (int)        {moveDown(_dviState.z);}
cmdX(int len)469 void DVIReader::cmdX (int len)     {_dviState.x = _dvi2bp*readSigned(len); cmdX0(0);}
cmdY(int len)470 void DVIReader::cmdY (int len)     {_dviState.y = _dvi2bp*readSigned(len); cmdY0(0);}
cmdW(int len)471 void DVIReader::cmdW (int len)     {_dviState.w = _dvi2bp*readSigned(len); cmdW0(0);}
cmdZ(int len)472 void DVIReader::cmdZ (int len)     {_dviState.z = _dvi2bp*readSigned(len); cmdZ0(0);}
cmdNop(int)473 void DVIReader::cmdNop (int)       {}
474 
475 
476 /** Sets the text orientation (horizontal, vertical).
477  *  This command is only available in DVI files of format 3 (created by pTeX) */
cmdDir(int)478 void DVIReader::cmdDir (int) {
479 	UInt8 wmode = readUnsigned(1);
480 	if (wmode == 4)  // yoko mode (4) equals default LR mode (0)
481 		wmode = 0;
482 	if (wmode == 2 || wmode > 3) {
483 		ostringstream oss;
484 		oss << "invalid writing mode value " << wmode << " (0, 1, or 3 expected)";
485 		throw DVIException(oss.str());
486 	}
487 	_dviState.d = (WritingMode)wmode;
488 	if (_actions)
489 		_actions->setTextOrientation(_dviState.d != WMODE_LR);
490 }
491 
492 
cmdXXX(int len)493 void DVIReader::cmdXXX (int len) {
494 	if (!_inPage)
495 		throw DVIException("special outside of page");
496 	UInt32 numBytes = readUnsigned(len);
497 	string s = readString(numBytes);
498 	if (_actions)
499 		_actions->special(s, _dvi2bp);
500 }
501 
502 
503 /** Selects a previously defined font by its number.
504  * @param[in] num font number
505  * @throw DVIException if font number is undefined */
cmdFontNum0(int num)506 void DVIReader::cmdFontNum0 (int num) {
507 	if (Font *font = FontManager::instance().getFont(num)) {
508 		_currFontNum = num;
509 		if (_actions && !dynamic_cast<VirtualFont*>(font))
510 			_actions->setFont(FontManager::instance().fontID(num), font);  // all fonts get a recomputed ID
511 	}
512 	else {
513 		ostringstream oss;
514 		oss << "undefined font number " << num;
515 		throw DVIException(oss.str());
516 	}
517 }
518 
519 
520 /** Selects a previously defined font.
521  * @param[in] len size of font number variable (in bytes)
522  * @throw DVIException if font number is undefined */
cmdFontNum(int len)523 void DVIReader::cmdFontNum (int len) {
524 	UInt32 num = readUnsigned(len);
525 	cmdFontNum0(num);
526 }
527 
528 
529 /** Helper function to handle a font definition.
530  *  @param[in] fontnum local font number
531  *  @param[in] name font name
532  *  @param[in] cs checksum to be compared with TFM checksum
533  *  @param[in] ds design size in PS point units
534  *  @param[in] ss scaled size in PS point units */
defineFont(UInt32 fontnum,const string & name,UInt32 cs,double ds,double ss)535 void DVIReader::defineFont (UInt32 fontnum, const string &name, UInt32 cs, double ds, double ss) {
536 	if (!_inPostamble)  // only process font definitions collected in the postamble; skip all others
537 		return;
538 
539 	FontManager &fm = FontManager::instance();
540 	int id = fm.registerFont(fontnum, name, cs, ds, ss);
541 	Font *font = fm.getFontById(id);
542 	if (VirtualFont *vf = dynamic_cast<VirtualFont*>(font)) {
543 		// read vf file, register its font and character definitions
544 		fm.enterVF(vf);
545 		ifstream ifs(vf->path(), ios::binary);
546 		VFReader vfReader(ifs);
547 		vfReader.replaceActions(this);
548 		vfReader.executeAll();
549 		fm.leaveVF();
550 	}
551 	if (_actions)
552 		_actions->defineFont(id, font);
553 }
554 
555 
556 /** Defines a new font.
557  * @param[in] len size of font number variable (in bytes) */
cmdFontDef(int len)558 void DVIReader::cmdFontDef (int len) {
559 	UInt32 fontnum  = readUnsigned(len);   // font number
560 	UInt32 checksum = readUnsigned(4);     // font checksum (to be compared with corresponding TFM checksum)
561 	UInt32 ssize    = readUnsigned(4);     // scaled size of font in DVI units
562 	UInt32 dsize    = readUnsigned(4);     // design size of font in DVI units
563 	UInt32 pathlen  = readUnsigned(1);     // length of font path
564 	UInt32 namelen  = readUnsigned(1);     // length of font name
565 	readString(pathlen);                   // skip font path
566 	string fontname = readString(namelen);
567 
568 	defineFont(fontnum, fontname, checksum, dsize*_dvi2bp, ssize*_dvi2bp);
569 }
570 
571 
572 /** This template method is called by the VFReader after reading a font definition from a VF file.
573  *  @param[in] fontnum local font number
574  *  @param[in] path path to font file
575  *  @param[in] name font name
576  *  @param[in] checksum checksum to be compared with TFM checksum
577  *  @param[in] dsize design size in PS point units
578  *  @param[in] ssize scaled size in PS point units */
defineVFFont(UInt32 fontnum,string path,string name,UInt32 checksum,double dsize,double ssize)579 void DVIReader::defineVFFont (UInt32 fontnum, string path, string name, UInt32 checksum, double dsize, double ssize) {
580 	if (VirtualFont *vf = FontManager::instance().getVF())
581 		defineFont(fontnum, name, checksum, dsize, ssize * vf->scaleFactor());
582 }
583 
584 
585 /** This template method is called by the VFReader after reading a character definition from a VF file.
586  *  @param[in] c character number
587  *  @param[in] dvi DVI fragment describing the character */
defineVFChar(UInt32 c,vector<UInt8> * dvi)588 void DVIReader::defineVFChar (UInt32 c, vector<UInt8> *dvi) {
589 	FontManager::instance().assignVfChar(c, dvi);
590 }
591 
592 
593 /** XDV extension: includes image or pdf file.
594  *  parameters: box[1] matrix[4][6] p[2] len[2] path[l] */
cmdXPic(int)595 void DVIReader::cmdXPic (int) {
596 	// just skip the parameters
597 	readUnsigned(1);           // box
598 	for (int i=0; i < 6; i++)  // matrix
599 		readSigned(4);
600 	readSigned(2);             // page number
601 	UInt16 len = readUnsigned(2);
602 	readString(len);           // path to image/pdf file
603 }
604 
605 
606 /** XDV extension: defines a native font */
cmdXFontDef(int)607 void DVIReader::cmdXFontDef (int) {
608 	Int32 fontnum = readSigned(4);
609 	double ptsize = _dvi2bp*readUnsigned(4);
610 	UInt16 flags = readUnsigned(2);
611 	UInt8 psname_len = readUnsigned(1);
612 	UInt8 fmname_len = readUnsigned(1);
613 	UInt8 stname_len = readUnsigned(1);
614 	string fontname = readString(psname_len);
615 	readString(fmname_len);
616 	readString(stname_len);
617 	FontStyle style;
618 	Color color;
619 	if (flags & 0x0100) { // vertical?
620 	}
621 	if (flags & 0x0200) { // colored?
622 		// The font color must not interfere with color specials. If the font color is not black,
623 		// all color specials should be ignored, i.e. glyphs of a non-black fonts have a fixed color
624 		// that can't be changed by color specials.
625 		UInt32 rgba = readUnsigned(4);
626 		color.setRGB(UInt8(rgba >> 24), UInt8((rgba >> 16) & 0xff), UInt8((rgba >> 8) & 0xff));
627 	}
628 	if (flags & 0x1000)   // extend?
629 		style.extend = _dvi2bp*readSigned(4);
630 	if (flags & 0x2000)   // slant?
631 		style.slant = _dvi2bp*readSigned(4);
632 	if (flags & 0x4000)   // embolden?
633 		style.bold = _dvi2bp*readSigned(4);
634 	if (flags & 0x0800) { // variations?
635 		UInt16 num_variations = readSigned(2);
636 		for (int i=0; i < num_variations; i++)
637 			readUnsigned(4);
638 	}
639 	if (_inPage)
640 		FontManager::instance().registerFont(fontnum, fontname, ptsize, style, color);
641 }
642 
643 
644 /** XDV extension: prints an array of characters where each character
645  *  can take independent x and y coordinates.
646  *  parameters: w[4] n[2] x[4][n] y[4][n] c[2][n] */
cmdXGlyphA(int)647 void DVIReader::cmdXGlyphA (int) {
648 	putGlyphArray(false);
649 }
650 
651 /** XDV extension: prints an array/string of characters where each character
652  *  can take independent x coordinates whereas all share a single y coordinate.
653  *  parameters: w[4] n[2] x[4][n] y[4] c[2][n] */
cmdXGlyphS(int)654 void DVIReader::cmdXGlyphS (int) {
655 	putGlyphArray(true);
656 }
657 
658 
659 /** Implements the common functionality of cmdXGlyphA and cmdXGlyphS.
660  *  @param[in] xonly indicates if the characters share a single y coordinate (xonly==true) */
putGlyphArray(bool xonly)661 void DVIReader::putGlyphArray (bool xonly) {
662 	double strwidth = _dvi2bp*readSigned(4);
663 	UInt16 num_glyphs = readUnsigned(2);
664 	vector<Int32> x(num_glyphs);
665 	vector<Int32> y(num_glyphs);
666 	for (int i=0; i < num_glyphs; i++) {
667 		x[i] = readSigned(4);
668 		y[i] = xonly ? 0 : readSigned(4);
669 	}
670 	if (!_actions)
671 		seek(2*num_glyphs, ios::cur);
672 	else {
673 		if (Font *font = FontManager::instance().getFont(_currFontNum)) {
674 			for (int i=0; i < num_glyphs; i++) {
675 				UInt16 glyph_index = readUnsigned(2);
676 				double xx = _dviState.h + x[i]*_dvi2bp + _tx;
677 				double yy = _dviState.v + y[i]*_dvi2bp + _ty;
678 				_actions->setChar(xx, yy, glyph_index, false, font);
679 			}
680 		}
681 	}
682 	moveRight(strwidth);
683 }
684