1 /*************************************************************************
2 ** SpecialManager.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 <iomanip>
23 #include <sstream>
24 #include "SpecialActions.h"
25 #include "SpecialHandler.h"
26 #include "SpecialManager.h"
27 #include "PsSpecialHandler.h"
28 #include "macros.h"
29 
30 using namespace std;
31 
32 double SpecialActions::PROGRESSBAR_DELAY=1000;  // initial delay in seconds (values >= 1000 disable the progressbar)
33 
34 
~SpecialManager()35 SpecialManager::~SpecialManager () {
36 	unregisterHandlers();
37 }
38 
39 
instance()40 SpecialManager& SpecialManager::instance() {
41 	static SpecialManager sm;
42 	return sm;
43 }
44 
45 
46 /** Remove all registered handlers. */
unregisterHandlers()47 void SpecialManager::unregisterHandlers () {
48 	FORALL(_pool, vector<SpecialHandler*>::iterator, it)
49 		delete *it;
50 	_pool.clear();
51 	_handlers.clear();
52 	_endPageListeners.clear();
53 	_positionListeners.clear();
54 }
55 
56 
57 /** Registers a single special handler. This method doesn't check if a
58  *  handler of the same class is already registered.
59  *  @param[in] handler pointer to handler to be registered */
registerHandler(SpecialHandler * handler)60 void SpecialManager::registerHandler (SpecialHandler *handler) {
61 	if (handler) {
62 		// get array of prefixes this handler is responsible for
63 		_pool.push_back(handler);
64 		for (const char **p=handler->prefixes(); *p; ++p)
65 			_handlers[*p] = handler;
66 		if (DVIPreprocessingListener *listener = dynamic_cast<DVIPreprocessingListener*>(handler))
67 			_preprocListeners.push_back(listener);
68 		if (DVIEndPageListener *listener = dynamic_cast<DVIEndPageListener*>(handler))
69 			_endPageListeners.push_back(listener);
70 		if (DVIPositionListener *listener = dynamic_cast<DVIPositionListener*>(handler))
71 			_positionListeners.push_back(listener);
72 	}
73 }
74 
75 
76 /** Registers several special handlers at once.
77  *  If ignorelist == 0, all given handlers are registered. To exclude selected sets of
78  *  specials, the corresponding names can be given separated by non alpha-numeric characters,
79  *  e.g. "color, ps, em" or "color: ps em" etc.
80  *  @param[in] handlers pointer to zero-terminated array of handlers to be registered
81  *  @param[in] ignorelist list of special names to be ignored */
registerHandlers(SpecialHandler ** handlers,const char * ignorelist)82 void SpecialManager::registerHandlers (SpecialHandler **handlers, const char *ignorelist) {
83 	if (handlers) {
84 		string ign = ignorelist ? ignorelist : "";
85 		FORALL(ign, string::iterator, it)
86 			if (!isalnum(*it))
87 				*it = '%';
88 		ign = "%"+ign+"%";
89 
90 		for (; *handlers; handlers++) {
91 			if (!(*handlers)->name() || ign.find("%"+string((*handlers)->name())+"%") == string::npos)
92 				registerHandler(*handlers);
93 			else
94 				delete *handlers;
95 		}
96 	}
97 }
98 
99 
100 /** Looks for an appropriate handler for a given special prefix.
101  *  @param[in] prefix the special prefix, e.g. "color" or "em"
102  *  @return in case of success: pointer to handler, 0 otherwise */
findHandler(const string & prefix) const103 SpecialHandler* SpecialManager::findHandler (const string &prefix) const {
104 	ConstIterator it = _handlers.find(prefix);
105 	if (it != _handlers.end())
106 		return it->second;
107 	return 0;
108 }
109 
110 
extract_prefix(istream & is)111 static string extract_prefix (istream &is) {
112 	int c;
113 	string prefix;
114 	while (isalnum(c=is.get()))
115 		prefix += c;
116 	if (ispunct(c)) // also add seperation character to identifying prefix
117 		prefix += c;
118 	if (prefix == "ps:" && is.peek() == ':')
119 		prefix += is.get();
120 	return prefix;
121 }
122 
123 
preprocess(const string & special,SpecialActions * actions) const124 void SpecialManager::preprocess (const string &special, SpecialActions *actions) const {
125 	istringstream iss(special);
126 	string prefix = extract_prefix(iss);
127 	if (SpecialHandler *handler = findHandler(prefix))
128 		handler->preprocess(prefix.c_str(), iss, actions);
129 }
130 
131 
132 /** Executes a special command.
133  *  @param[in] special the special expression
134  *  @param[in] dvi2bp factor to convert DVI units to PS points
135  *  @param[in] actions actions the special handlers can perform
136  *  @return true if the special could be processed successfully
137  *  @throw SpecialException in case of errors during special processing */
process(const string & special,double dvi2bp,SpecialActions * actions) const138 bool SpecialManager::process (const string &special, double dvi2bp, SpecialActions *actions) const {
139 	istringstream iss(special);
140 	string prefix = extract_prefix(iss);
141 	bool success=false;
142 	if (SpecialHandler *handler = findHandler(prefix)) {
143 		handler->setDviScaleFactor(dvi2bp);
144 		success = handler->process(prefix.c_str(), iss, actions);
145 	}
146 	return success;
147 }
148 
149 
notifyPreprocessingFinished() const150 void SpecialManager::notifyPreprocessingFinished () const {
151 	FORALL(_preprocListeners, vector<DVIPreprocessingListener*>::const_iterator, it)
152 		(*it)->dviPreprocessingFinished();
153 }
154 
155 
notifyEndPage(unsigned pageno) const156 void SpecialManager::notifyEndPage (unsigned pageno) const {
157 	FORALL(_endPageListeners, vector<DVIEndPageListener*>::const_iterator, it)
158 		(*it)->dviEndPage(pageno);
159 }
160 
161 
notifyPositionChange(double x,double y) const162 void SpecialManager::notifyPositionChange (double x, double y) const {
163 	FORALL(_positionListeners, vector<DVIPositionListener*>::const_iterator, it)
164 		(*it)->dviMovedTo(x, y);
165 }
166 
167 
writeHandlerInfo(ostream & os) const168 void SpecialManager::writeHandlerInfo (ostream &os) const {
169 	ios::fmtflags osflags(os.flags());
170 	typedef map<string, SpecialHandler*> SortMap;
171 	SortMap m;
172 	FORALL(_handlers, ConstIterator, it)
173 		if (it->second->name())
174 			m[it->second->name()] = it->second;
175 	FORALL(m, SortMap::iterator, it) {
176 		os << setw(10) << left << it->second->name() << ' ';
177 		if (it->second->info())
178 			os << it->second->info();
179 		os << endl;
180 	}
181 	os.flags(osflags);  // restore format flags
182 }
183 
184