summaryrefslogtreecommitdiff
path: root/dviware/dvisvgm/src/MetafontWrapper.cpp
blob: 7788bef3d9c5b6ce5afeeb09f5355e2438b2200e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*************************************************************************
** MetafontWrapper.cpp                                                  **
**                                                                      **
** This file is part of dvisvgm -- a fast DVI to SVG converter          **
** Copyright (C) 2005-2021 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 <cctype>
#include <fstream>
#include <sstream>
#include "FileSystem.hpp"
#include "FileFinder.hpp"
#include "Message.hpp"
#include "MetafontWrapper.hpp"
#include "Process.hpp"
#include "XMLString.hpp"

using namespace std;


MetafontWrapper::MetafontWrapper (string fname, string dir)
	: _fontname(std::move(fname)), _dir(std::move(dir))
{
	// ensure that folder paths ends with slash
	if (_dir.empty())
		_dir = "./";
	else if (_dir != "/" && _dir.back() != '/')
		_dir += '/';
}


/** Calls Metafont and evaluates the logfile. If a gf file was successfully
 *  generated the dpi value is stripped from the filename
 *  (e.g. cmr10.600gf => cmr10.gf). This makes life easier...
 *  @param[in] mode Metafont mode, e.g. "ljfour"
 *  @param[in] mag magnification factor
 *  @return true on success */
bool MetafontWrapper::call (const string &mode, double mag) {
	if (!FileFinder::instance().lookup(_fontname+".mf"))
		return false;     // mf file not available => no need to call the "slow" Metafont
	FileSystem::remove(_fontname+".gf");

	string mfName = "mf";  // file name of Metafont executable
#ifndef MIKTEX
	if (const char *mfnowinPath = FileFinder::instance().lookupExecutable("mf-nowin", true))
		mfName = mfnowinPath;
	else
#endif
		if (const char *mfPath = FileFinder::instance().lookupExecutable(mfName, true))
			mfName = mfPath;
#ifdef _WIN32
		else {
			Message::estream(true) << "can't run Metafont (mf.exe and mf-nowin.exe not found)\n";
			return false;
		}
#endif
	ostringstream oss;
	oss << "\"\\mode=" << mode  << ";"  // set MF mode, e.g. 'proof', 'ljfour' or 'localfont'
		"mode_setup;"                    // initialize MF variables
		"mag:=" << mag << ";"            // set magnification factor
		"show pixels_per_inch*mag;"      // print character resolution to stdout
		"batchmode;"                     // don't halt on errors and don't print informational messages
		"input " << _fontname << "\"";   // load font description
	Message::mstream(false, Message::MC_STATE) << "\nrunning Metafont for " << _fontname << '\n';
	Process mf_process(mfName, oss.str());
	string mf_messages;
	mf_process.run(_dir, &mf_messages);

	int resolution = getResolution(mf_messages);

	// compose expected name of GF file (see Metafont Book, p. 324)
	string gfname = _dir + _fontname + ".";
	if (resolution > 0)
		gfname += XMLString(resolution);
	gfname += "gf";
	FileSystem::rename(gfname, _dir+_fontname+".gf");  // remove resolution value from filename
	return FileSystem::exists(_dir+_fontname+".gf");
}


/** Returns the resolution applied to a GF file generated by Metafont. Since the resolution
 *  is part of the filename suffix, we need this value in order to address the file.
 *  @param[in] mfMessage output written to stdout by Metafont
 *  @return the resolution (>0 on success, 0 otherwise) */
int MetafontWrapper::getResolution (const string &mfMessage) const {
	int res = 0;
	char buf[256];
	if (!mfMessage.empty()) {
		// try get resolution value written to stdout by above MF command
		istringstream iss(mfMessage);
		while (iss) {
			iss.getline(buf, sizeof(buf));
			string line = buf;
			if (line.substr(0, 3) == ">> ") {
				res = stoi(line.substr(3));
				break;
			}
		}
	}
	// couldn't read resolution from stdout, try to get it from log file
	if (res == 0) {
		ifstream ifs(_dir+_fontname+".log");
		while (ifs) {
			ifs.getline(buf, sizeof(buf));
			string line = buf;
			if (line.substr(0, 18) == "Output written on ") {
				line = line.substr(18);
				size_t pos = line.find(' ');
				line = line.substr(0, pos);
				pos = line.rfind('.');
				if (pos != string::npos && line.substr(line.length()-2) == "gf") {
					line.pop_back();
					line.pop_back();
					try {
						res = stoi(line.substr(pos+1));
					}
					catch (...) {
					}
				}
			}
		}
	}
	return res;
}


/** Calls Metafont if output files (tfm and gf) don't already exist.
 *  @param[in] mode Metafont mode to be used (e.g. 'ljfour')
 *  @param[in] mag magnification factor
 *  @return true on success */
bool MetafontWrapper::make (const string &mode, double mag) {
	ifstream tfm(_dir+_fontname+".tfm");
	ifstream gf(_dir+_fontname+".gf");
	if (gf && tfm) // @@ distinguish between gf and tfm
		return true;
	return call(mode, mag);
}


bool MetafontWrapper::success () const {
	ifstream tfm(_dir+_fontname+".tfm");
	ifstream gf(_dir+_fontname+".gf");
	return tfm && gf;
}