1 /*************************************************************************
2 ** dvisvgm.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 <clipper.hpp>
23 #include <fstream>
24 #include <iostream>
25 #include <sstream>
26 #include <string>
27 #include "gzstream.h"
28 #include "CommandLine.h"
29 #include "DVIToSVG.h"
30 #include "DVIToSVGActions.h"
31 #include "EPSToSVG.h"
32 #include "FileFinder.h"
33 #include "FilePath.h"
34 #include "FileSystem.h"
35 #include "Font.h"
36 #include "FontCache.h"
37 #include "FontEngine.h"
38 #include "FontMap.h"
39 #include "Ghostscript.h"
40 #include "HtmlSpecialHandler.h"
41 #include "InputReader.h"
42 #include "Message.h"
43 #include "PageSize.h"
44 #include "PSInterpreter.h"
45 #include "PsSpecialHandler.h"
46 #include "SignalHandler.h"
47 #include "SpecialManager.h"
48 #include "SVGOutput.h"
49 #include "System.h"
50
51 #ifdef __MSVC__
52 #include <potracelib.h>
53 #else
54 extern "C" {
55 #include <potracelib.h>
56 }
57 #endif
58
59 using namespace std;
60
61
62 ////////////////////////////////////////////////////////////////////////////////
63
show_help(const CommandLine & cmd)64 static void show_help (const CommandLine &cmd) {
65 cout << PACKAGE_STRING "\n\n";
66 cmd.help(cmd.help_arg());
67 cout << "\nCopyright (C) 2005-2015 Martin Gieseking <martin.gieseking@uos.de> \n\n";
68 }
69
70
remove_path(string fname)71 static string remove_path (string fname) {
72 fname = FileSystem::adaptPathSeperators(fname);
73 size_t slashpos = fname.rfind('/');
74 if (slashpos == string::npos)
75 return fname;
76 return fname.substr(slashpos+1);
77 }
78
79
ensure_suffix(string fname,bool eps)80 static string ensure_suffix (string fname, bool eps) {
81 size_t dotpos = remove_path(fname).rfind('.');
82 if (dotpos == string::npos)
83 fname += (eps ? ".eps" : ".dvi");
84 return fname;
85 }
86
87
get_transformation_string(const CommandLine & args)88 static string get_transformation_string (const CommandLine &args) {
89 ostringstream oss;
90 if (args.rotate_given())
91 oss << 'R' << args.rotate_arg() << ",w/2,h/2";
92 if (args.translate_given())
93 oss << 'T' << args.translate_arg();
94 if (args.scale_given())
95 oss << 'S' << args.scale_arg();
96 if (args.transform_given())
97 oss << args.transform_arg();
98 return oss.str();
99 }
100
101
set_libgs(CommandLine & args)102 static void set_libgs (CommandLine &args) {
103 #if !defined(DISABLE_GS) && !defined(HAVE_LIBGS)
104 if (args.libgs_given())
105 Ghostscript::LIBGS_NAME = args.libgs_arg();
106 else if (getenv("LIBGS"))
107 Ghostscript::LIBGS_NAME = getenv("LIBGS");
108 #endif
109 }
110
111
set_cache_dir(const CommandLine & args)112 static bool set_cache_dir (const CommandLine &args) {
113 if (args.cache_given() && !args.cache_arg().empty()) {
114 if (args.cache_arg() == "none")
115 PhysicalFont::CACHE_PATH = 0;
116 else if (FileSystem::exists(args.cache_arg().c_str()))
117 PhysicalFont::CACHE_PATH = args.cache_arg().c_str();
118 else
119 Message::wstream(true) << "cache directory '" << args.cache_arg() << "' does not exist (caching disabled)\n";
120 }
121 else if (const char *userdir = FileSystem::userdir()) {
122 static string cachepath = userdir + string("/.dvisvgm/cache");
123 if (!FileSystem::exists(cachepath.c_str()))
124 FileSystem::mkdir(cachepath.c_str());
125 PhysicalFont::CACHE_PATH = cachepath.c_str();
126 }
127 if (args.cache_given() && args.cache_arg().empty()) {
128 cout << "cache directory: " << (PhysicalFont::CACHE_PATH ? PhysicalFont::CACHE_PATH : "(none)") << '\n';
129 FontCache::fontinfo(PhysicalFont::CACHE_PATH, cout, true);
130 return false;
131 }
132 return true;
133 }
134
135
check_bbox(const string & bboxstr)136 static bool check_bbox (const string &bboxstr) {
137 const char *formats[] = {"none", "min", "dvi", 0};
138 for (const char **p=formats; *p; ++p)
139 if (bboxstr == *p)
140 return true;
141 if (isalpha(bboxstr[0])) {
142 try {
143 PageSize size(bboxstr);
144 return true;
145 }
146 catch (const PageSizeException &e) {
147 Message::estream(true) << "invalid bounding box format '" << bboxstr << "'\n";
148 return false;
149 }
150 }
151 try {
152 BoundingBox bbox;
153 bbox.set(bboxstr);
154 return true;
155 }
156 catch (const MessageException &e) {
157 Message::estream(true) << e.what() << '\n';
158 return false;
159 }
160 }
161
162
print_version(bool extended)163 static void print_version (bool extended) {
164 ostringstream oss;
165 oss << PACKAGE_STRING;
166 if (extended) {
167 if (strlen(TARGET_SYSTEM) > 0)
168 oss << " (" TARGET_SYSTEM ")";
169 int len = oss.str().length();
170 oss << "\n" << string(len, '-') << "\n"
171 "clipper: " << CLIPPER_VERSION "\n"
172 "freetype: " << FontEngine::version() << "\n";
173
174 Ghostscript gs;
175 string gsver = gs.revision(true);
176 if (!gsver.empty())
177 oss << "Ghostscript: " << gsver + "\n";
178 oss <<
179 #ifdef MIKTEX
180 "MiKTeX: " << FileFinder::version() << "\n"
181 #else
182 "kpathsea: " << FileFinder::version() << "\n"
183 #endif
184 "potrace: " << (strchr(potrace_version(), ' ') ? strchr(potrace_version(), ' ')+1 : "unknown") << "\n"
185 "zlib: " << zlibVersion();
186 }
187 cout << oss.str() << endl;
188 }
189
190
init_fontmap(const CommandLine & args)191 static void init_fontmap (const CommandLine &args) {
192 const char *mapseq = args.fontmap_given() ? args.fontmap_arg().c_str() : 0;
193 bool additional = mapseq && strchr("+-=", *mapseq);
194 if (!mapseq || additional) {
195 const char *mapfiles[] = {"ps2pk.map", "dvipdfm.map", "psfonts.map", 0};
196 bool found = false;
197 for (const char **p=mapfiles; *p && !found; p++)
198 found = FontMap::instance().read(*p);
199 if (!found)
200 Message::wstream(true) << "none of the default map files could be found";
201 }
202 if (mapseq)
203 FontMap::instance().read(mapseq);
204 }
205
206
main(int argc,char * argv[])207 int main (int argc, char *argv[]) {
208 CommandLine args;
209 args.parse(argc, argv);
210 if (args.error())
211 return 1;
212
213 Message::COLORIZE = args.color_given();
214
215 try {
216 FileFinder::init(argv[0], "dvisvgm", !args.no_mktexmf_given());
217 }
218 catch (MessageException &e) {
219 Message::estream(true) << e.what() << '\n';
220 return 0;
221 }
222
223 set_libgs(args);
224 if (args.version_given()) {
225 print_version(args.version_arg());
226 return 0;
227 }
228 if (args.list_specials_given()) {
229 DVIToSVG::setProcessSpecials();
230 SpecialManager::instance().writeHandlerInfo(cout);
231 return 0;
232 }
233
234 if (!set_cache_dir(args))
235 return 0;
236
237 if (argc == 1 || args.help_given()) {
238 show_help(args);
239 return 0;
240 }
241
242 if (argc > 1 && args.numFiles() < 1) {
243 Message::estream(true) << "no input file given\n";
244 return 1;
245 }
246
247 if (args.stdout_given() && args.zip_given()) {
248 Message::estream(true) << "writing SVGZ files to stdout is not supported\n";
249 return 1;
250 }
251
252 if (!check_bbox(args.bbox_arg()))
253 return 1;
254
255 if (args.progress_given()) {
256 DVIReader::COMPUTE_PROGRESS = args.progress_given();
257 SpecialActions::PROGRESSBAR_DELAY = args.progress_arg();
258 }
259 SVGTree::CREATE_STYLE = !args.no_styles_given();
260 SVGTree::USE_FONTS = !args.no_fonts_given();
261 SVGTree::CREATE_USE_ELEMENTS = args.no_fonts_arg() < 1;
262 SVGTree::ZOOM_FACTOR = args.zoom_arg();
263 SVGTree::RELATIVE_PATH_CMDS = args.relative_given();
264 SVGTree::MERGE_CHARS = !args.no_merge_given();
265 DVIToSVG::TRACE_MODE = args.trace_all_given() ? (args.trace_all_arg() ? 'a' : 'm') : 0;
266 Message::LEVEL = args.verbosity_arg();
267 PhysicalFont::EXACT_BBOX = args.exact_given();
268 PhysicalFont::KEEP_TEMP_FILES = args.keep_given();
269 PhysicalFont::METAFONT_MAG = max(1.0, args.mag_arg());
270 XMLString::DECIMAL_PLACES = max(0, min(6, args.precision_arg()));
271 if (!HtmlSpecialHandler::setLinkMarker(args.linkmark_arg()))
272 Message::wstream(true) << "invalid argument '"+args.linkmark_arg()+"' supplied for option --linkmark\n";
273 double start_time = System::time();
274 bool eps_given=false;
275 #ifndef DISABLE_GS
276 eps_given = args.eps_given();
277 PsSpecialHandler::COMPUTE_CLIPPATHS_INTERSECTIONS = args.clipjoin_given();
278 PsSpecialHandler::SHADING_SEGMENT_OVERLAP = args.grad_overlap_given();
279 PsSpecialHandler::SHADING_SEGMENT_SIZE = max(1, args.grad_segments_arg());
280 PsSpecialHandler::SHADING_SIMPLIFY_DELTA = args.grad_simplify_arg();
281 #endif
282 string inputfile = ensure_suffix(args.file(0), eps_given);
283 ifstream ifs(inputfile.c_str(), ios::binary|ios::in);
284 if (!ifs) {
285 Message::estream(true) << "can't open file '" << inputfile << "' for reading\n";
286 return 0;
287 }
288 try {
289 SVGOutput out(args.stdout_given() ? 0 : inputfile.c_str(), args.output_arg(), args.zip_given() ? args.zip_arg() : 0);
290 SignalHandler::instance().start();
291 #ifndef DISABLE_GS
292 if (args.eps_given()) {
293 EPSToSVG eps2svg(inputfile, out);
294 eps2svg.convert();
295 Message::mstream().indent(0);
296 Message::mstream(false, Message::MC_PAGE_NUMBER)
297 << "file converted in " << (System::time()-start_time) << " seconds\n";
298 }
299 else
300 #endif
301 {
302 init_fontmap(args);
303 DVIToSVG dvi2svg(ifs, out);
304 const char *ignore_specials = args.no_specials_given() ? (args.no_specials_arg().empty() ? "*" : args.no_specials_arg().c_str()) : 0;
305 dvi2svg.setProcessSpecials(ignore_specials, true);
306 dvi2svg.setPageTransformation(get_transformation_string(args));
307 dvi2svg.setPageSize(args.bbox_arg());
308
309 pair<int,int> pageinfo;
310 dvi2svg.convert(args.page_arg(), &pageinfo);
311 Message::mstream().indent(0);
312 Message::mstream(false, Message::MC_PAGE_NUMBER) << "\n" << pageinfo.first << " of " << pageinfo.second << " page";
313 if (pageinfo.second > 1)
314 Message::mstream(false, Message::MC_PAGE_NUMBER) << 's';
315 Message::mstream(false, Message::MC_PAGE_NUMBER) << " converted in " << (System::time()-start_time) << " seconds\n";
316 }
317 }
318 catch (DVIException &e) {
319 Message::estream() << "\nDVI error: " << e.what() << '\n';
320 }
321 catch (PSException &e) {
322 Message::estream() << "\nPostScript error: " << e.what() << '\n';
323 }
324 catch (SignalException &e) {
325 Message::wstream().clearline();
326 Message::wstream(true) << "execution interrupted by user\n";
327 }
328 catch (MessageException &e) {
329 Message::estream(true) << e.what() << '\n';
330 }
331 FileFinder::finish();
332 return 0;
333 }
334
335