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