1 /*************************************************************************
2 ** VFReader.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 <sstream>
23 #include "Font.h"
24 #include "VFActions.h"
25 #include "VFReader.h"
26 #include "macros.h"
27 
28 using namespace std;
29 
30 
31 /** Converts a TFM fix point value to double (PS point units). */
fix2double(FixWord fix)32 static inline double fix2double (FixWord fix) {
33 	const double pt2bp = 72/72.27;
34 	return double(fix)/(1 << 20)*pt2bp;
35 }
36 
37 
VFReader(istream & is)38 VFReader::VFReader (istream &is)
39 	: StreamReader(is), _actions(0), _designSize(0) {
40 }
41 
42 
~VFReader()43 VFReader::~VFReader () {
44 }
45 
46 
replaceActions(VFActions * a)47 VFActions* VFReader::replaceActions (VFActions *a) {
48 	VFActions *ret = _actions;
49 	_actions = a;
50 	return ret;
51 }
52 
53 
54 /** Reads a single VF command from the current position of the input stream and calls the
55  *  corresponding cmdFOO method. The execution can be influenced by a function of type ApproveOpcode.
56  *  It takes an opcode and returns true if the command is supposed to be executed.
57  *  @param[in] approve function to approve invocation of the action assigned to command
58  *  @return opcode of the executed command */
executeCommand(ApproveAction approve)59 int VFReader::executeCommand (ApproveAction approve) {
60 	int opcode = readByte();
61 	if (!isStreamValid() || opcode < 0)  // at end of file?
62 		throw VFException("invalid VF file");
63 
64 	bool approved = !approve || approve(opcode);
65 	VFActions *actions = _actions;
66 	if (!approved)
67 		replaceActions(0);  // disable actions
68 
69 	if (opcode <= 241)     // short character definition?
70 		cmdShortChar(opcode);
71 	else if (opcode >= 243 && opcode <= 246)   // font definition?
72 		cmdFontDef(opcode-243+1);
73 	else {
74 		switch (opcode) {
75 			case 242: cmdLongChar(); break;  // long character definition
76 			case 247: cmdPre();      break;  // preamble
77 			case 248: cmdPost();     break;  // postamble
78 			default : {                      // invalid opcode
79 				replaceActions(actions);      // reenable actions
80 				ostringstream oss;
81 				oss << "undefined VF command (opcode " << opcode << ')';
82 				throw VFException(oss.str());
83 			}
84 		}
85 	}
86 	replaceActions(actions); // reenable actions
87 	return opcode;
88 }
89 
90 
executeAll()91 bool VFReader::executeAll () {
92 	clearStream();  // reset all status bits
93 	if (!isStreamValid())
94 		return false;
95 	seek(0);  // move file pointer to first byte of the input stream
96 	while (!eof() && executeCommand() != 248); // stop reading after post (248)
97 	return true;
98 }
99 
100 
101 /// Returns true if op indicates the preamble or a font definition
is_pre_or_fontdef(int op)102 static bool is_pre_or_fontdef (int op) {return op > 242;}
is_chardef(int op)103 static bool is_chardef (int op)        {return op < 243;}
104 
105 
executePreambleAndFontDefs()106 bool VFReader::executePreambleAndFontDefs () {
107 	clearStream();
108 	if (!isStreamValid())
109 		return false;
110 	seek(0);  // move file pointer to first byte of the input stream
111 	while (!eof() && executeCommand(is_pre_or_fontdef) > 242); // stop reading after last font definition
112 	return true;
113 }
114 
115 
executeCharDefs()116 bool VFReader::executeCharDefs () {
117 	clearStream();
118 	if (!isStreamValid())
119 		return false;
120 	seek(0);
121 	while (!eof() && executeCommand(is_chardef) < 243); // stop reading after last char definition
122 	return true;
123 }
124 
125 //////////////////////////////////////////////////////////////////////////////
126 
127 /** Reads and executes DVI preamble command. */
cmdPre()128 void VFReader::cmdPre () {
129 	UInt32 i   = readUnsigned(1);  // identification number (should be 2)
130 	UInt32 k   = readUnsigned(1);  // length of following comment
131 	string cmt = readString(k);    // comment
132 	UInt32 cs  = readUnsigned(4);  // check sum to be compared with TFM cecksum
133 	UInt32 ds  = readUnsigned(4);  // design size (same as TFM design size) (fix_word)
134 	_designSize = fix2double(ds);
135 	if (i != 202)
136 		throw VFException("invalid identification value in preamble");
137 	if (_actions)
138 		_actions->preamble(cmt, cs, ds);
139 }
140 
141 
cmdPost()142 void VFReader::cmdPost () {
143 	while ((readUnsigned(1)) == 248); // skip fill bytes
144 	if (_actions)
145 		_actions->postamble();
146 }
147 
148 
cmdLongChar()149 void VFReader::cmdLongChar () {
150 	UInt32 pl  = readUnsigned(4);      // packet length (length of DVI subroutine)
151 	if (!_actions)
152 		seek(8+pl, ios::cur);           // skip remaining char definition bytes
153 	else {
154 		UInt32 cc  = readUnsigned(4);   // character code
155 		readUnsigned(4);                // character width from corresponding TFM file
156 		vector<UInt8> *dvi = new vector<UInt8>(pl); // DVI subroutine
157 		readBytes(pl, *dvi);
158 		_actions->defineVFChar(cc, dvi); // call template method for user actions
159 	}
160 }
161 
162 
163 /** Reads and executes short_char_x command.
164  *  @param[in] pl packet length (length of DVI subroutine) */
cmdShortChar(int pl)165 void VFReader::cmdShortChar (int pl) {
166 	if (!_actions)
167 		seek(4+pl, ios::cur);     // skip char definition bytes
168 	else {
169 		UInt32 cc  = readUnsigned(1);   // character code
170 		readUnsigned(3);                // character width from corresponding TFM file
171 		vector<UInt8> *dvi = new vector<UInt8>(pl); // DVI subroutine
172 		readBytes(pl, *dvi);
173 		_actions->defineVFChar(cc, dvi); // call template method for user actions
174 	}
175 }
176 
177 
cmdFontDef(int len)178 void VFReader::cmdFontDef (int len) {
179 	UInt32 fontnum  = readUnsigned(len);   // font number
180 	UInt32 checksum = readUnsigned(4);     // font checksum (to be compared with corresponding TFM checksum)
181 	UInt32 ssize    = readUnsigned(4);     // scaled size of font relative to design size (fix_word)
182 	UInt32 dsize    = readUnsigned(4);     // design size of font (same as TFM design size) (fix_word)
183 	UInt32 pathlen  = readUnsigned(1);     // length of font path
184 	UInt32 namelen  = readUnsigned(1);     // length of font name
185 	string fontpath = readString(pathlen);
186 	string fontname = readString(namelen);
187 	if (_actions) {
188 		double ss = fix2double(ssize);
189 		double ds = fix2double(dsize);
190 		_actions->defineVFFont(fontnum, fontpath, fontname, checksum, ds, ss*_designSize);
191 	}
192 }
193