1 /*************************************************************************
2 ** MapLine.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 <cstring>
23 #include <sstream>
24 #include "InputBuffer.h"
25 #include "InputReader.h"
26 #include "MapLine.h"
27 #include "Subfont.h"
28
29 using namespace std;
30
31
32 /** Constructs a MapLine object by parsing a single mapline from the given stream. */
MapLine(istream & is)33 MapLine::MapLine (istream &is)
34 : _sfd(0), _fontindex(0), _slant(0), _bold(0), _extend(1)
35 {
36 char buf[256];
37 is.getline(buf, 256);
38 parse(buf);
39 }
40
41
42 // Some of the following functions have been derived from the dvipdfmx source file fontmap.c:
43 // http://cvs.ktug.or.kr/viewcvs/dvipdfmx/src/fontmap.c?revision=1.43&view=markup
44
45
46 /** Returns true if the given string is in dvips mapline format, and false if it's in dvipdfm format.
47 @param[in] line string to check */
isDVIPSFormat(const char * line) const48 bool MapLine::isDVIPSFormat (const char *line) const {
49 if (strchr(line, '"') || strchr(line, '<')) // these chars are only present in dvips maps
50 return true;
51 char prevchar = ' ';
52 int entry_count=0;
53 for (const char *p=line; *p; ++p) {
54 if (isspace(prevchar)) {
55 if (*p == '-') // options starting with '-' are only present in dvipdfm map files
56 return false;
57 if (!isspace(*p))
58 entry_count++;
59 }
60 prevchar = *p;
61 }
62 // tfm_name and ps_name only => dvips map
63 return entry_count == 2;
64 }
65
66
67 /** Separates main font name and subfont definition name from a given combined name.
68 * Example: "basename@sfdname@10" => {"basename10", "sfdname"}
69 * @param[in,out] fontname complete fontname; after separation: main fontname only
70 * @param[out] sfdname name of subfont definition
71 * @return true on success */
split_fontname(string & fontname,string & sfdname)72 static bool split_fontname (string &fontname, string &sfdname) {
73 size_t pos1; // index of first '@'
74 if ((pos1 = fontname.find('@')) != string::npos && pos1 > 0) {
75 size_t pos2; // index of second '@'
76 if ((pos2 = fontname.find('@', pos1+1)) != string::npos && pos2 > pos1+1) {
77 sfdname = fontname.substr(pos1+1, pos2-pos1-1);
78 fontname = fontname.substr(0, pos1) + fontname.substr(pos2+1);
79 return true;
80 }
81 }
82 return false;
83 }
84
85
86 /** Parses a single mapline and stores the scanned data in member variables.
87 * The line may either be given in dvips or dvipdfmx mapfile format.
88 * @param[in] line the mapline */
parse(const char * line)89 void MapLine::parse (const char *line) {
90 CharInputBuffer ib(line, strlen(line));
91 BufferInputReader ir(ib);
92 _texname = ir.getString();
93 string sfdname;
94 split_fontname(_texname, sfdname);
95 if (!sfdname.empty())
96 _sfd = SubfontDefinition::lookup(sfdname);
97 if (isDVIPSFormat(line))
98 parseDVIPSLine(ir);
99 else
100 parseDVIPDFMLine(ir);
101 }
102
103
104 /** Parses a single line in dvips mapfile format.
105 * @param[in] ir the input stream must be assigned to this reader */
parseDVIPSLine(InputReader & ir)106 void MapLine::parseDVIPSLine (InputReader &ir) {
107 ir.skipSpace();
108 if (ir.peek() != '<' && ir.peek() != '"')
109 _psname = ir.getString();
110 ir.skipSpace();
111 while (ir.peek() == '<' || ir.peek() == '"') {
112 if (ir.peek() == '<') {
113 ir.get();
114 if (ir.peek() == '[')
115 ir.get();
116 string name = ir.getString();
117 if (name.length() > 4 && name.substr(name.length()-4) == ".enc")
118 _encname = name.substr(0, name.length()-4);
119 else
120 _fontfname = name;
121 }
122 else { // ir.peek() == '"' => list of PS font operators
123 string options = ir.getQuotedString('"');
124 StringInputBuffer sib(options);
125 BufferInputReader sir(sib);
126 while (!sir.eof()) {
127 double number;
128 if (sir.parseDouble(number)) {
129 // operator with preceding numeric parameter (value opstr)
130 string opstr = sir.getString();
131 if (opstr == "SlantFont")
132 _slant = number;
133 else if (opstr == "ExtendFont")
134 _extend = number;
135 }
136 else {
137 // operator without parameter => skip for now
138 sir.getString();
139 }
140 }
141 }
142 ir.skipSpace();
143 }
144 }
145
146
throw_number_expected(char opt,bool integer_only=false)147 static void throw_number_expected (char opt, bool integer_only=false) {
148 ostringstream oss;
149 oss << "option -" << opt << ": " << (integer_only ? "integer" : "floating point") << " value expected";
150 throw MapLineException(oss.str());
151 }
152
153
154 /** Parses a single line in dvipdfmx mapfile format.
155 * @param[in] ir the input stream must be assigned to this reader */
parseDVIPDFMLine(InputReader & ir)156 void MapLine::parseDVIPDFMLine (InputReader &ir) {
157 ir.skipSpace();
158 if (ir.peek() != '-') {
159 _encname = ir.getString();
160 if (_encname == "default" || _encname == "none")
161 _encname.clear();
162 }
163 ir.skipSpace();
164 if (ir.peek() != '-')
165 _fontfname = ir.getString();
166 if (!_fontfname.empty()) {
167 parseFilenameOptions(_fontfname);
168 }
169 ir.skipSpace();
170 while (ir.peek() == '-') {
171 ir.get();
172 char option = ir.get();
173 if (!isprint(option))
174 throw MapLineException("option character expected");
175 ir.skipSpace();
176 switch (option) {
177 case 's': // slant
178 if (!ir.parseDouble(_slant))
179 throw_number_expected('s');
180 break;
181 case 'e': // extend
182 if (!ir.parseDouble(_extend))
183 throw_number_expected('e');
184 break;
185 case 'b': // bold
186 if (!ir.parseDouble(_bold))
187 throw_number_expected('b');
188 break;
189 case 'r': //remap (deprecated)
190 break;
191 case 'i': // ttc index
192 if (!ir.parseInt(_fontindex, false))
193 throw_number_expected('i', true);
194 break;
195 case 'p': // UCS plane
196 int dummy;
197 if (!ir.parseInt(dummy, false))
198 throw_number_expected('p', true);
199 break;
200 case 'u': // to unicode
201 ir.getString();
202 break;
203 case 'v': // stemV
204 int stemv;
205 if (!ir.parseInt(stemv, true))
206 throw_number_expected('v', true);
207 break;
208 case 'm': // map single chars
209 ir.skipUntil("-");
210 break;
211 case 'w': // writing mode (horizontal=0, vertical=1)
212 int vertical;
213 if (!ir.parseInt(vertical, false))
214 throw_number_expected('w', true);
215 break;
216 default:
217 ostringstream oss;
218 oss << "invalid option: -" << option;
219 throw MapLineException(oss.str());
220 }
221 ir.skipSpace();
222 }
223 }
224
225
226 /** [:INDEX:][!]FONTNAME[/CSI][,VARIANT] */
parseFilenameOptions(string fname)227 void MapLine::parseFilenameOptions (string fname) {
228 _fontfname = fname;
229 StringInputBuffer ib(fname);
230 BufferInputReader ir(ib);
231 if (ir.peek() == ':' && isdigit(ir.peek(1))) { // index given?
232 ir.get();
233 _fontindex = ir.getInt(); // font index of file with multiple fonts
234 if (ir.peek() == ':')
235 ir.get();
236 else
237 _fontindex = 0;
238 }
239 if (ir.peek() == '!') // no embedding
240 ir.get();
241
242 bool csi_given=false, style_given=false;
243 int pos;
244 if ((pos = ir.find('/')) >= 0) { // csi delimiter
245 csi_given = true;
246 _fontfname = ir.getString(pos);
247 }
248 else if ((pos = ir.find(',')) >= 0) {
249 style_given = true;
250 _fontfname = ir.getString(pos);
251 }
252 else
253 _fontfname = ir.getString();
254
255 if (csi_given) {
256 if ((pos = ir.find(',')) >= 0) {
257 style_given = true;
258 ir.getString(pos); // charcoll
259 }
260 else if (ir.eof())
261 throw MapLineException("CSI specifier expected");
262 else
263 ir.getString(); // charcoll
264 }
265 if (style_given) {
266 ir.get(); // skip ','
267 if (ir.check("BoldItalic")) {
268 }
269 else if (ir.check("Bold")) {
270 }
271 else if (ir.check("Italic")) {
272 }
273 if (!ir.eof())
274 throw MapLineException("invalid style given");
275 }
276 }
277