]> git.lyx.org Git - lyx.git/blob - src/insets/ExternalTransforms.cpp
Move protectArgument function to lstrings
[lyx.git] / src / insets / ExternalTransforms.cpp
1 /**
2  * \file ExternalTransforms.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "ExternalTransforms.h"
14
15 #include "support/convert.h"
16 #include "support/debug.h"
17 #include "support/lstrings.h"
18 #include "support/lyxlib.h" // float_equal
19 #include "support/Translator.h"
20
21 #include "support/regex.h"
22
23 #include <cmath> // abs
24 #include <sstream>
25
26 using namespace std;
27 using namespace lyx::support;
28
29 namespace lyx {
30 namespace external {
31
32
33 string const ExtraData::get(string const & id) const
34 {
35         map<string, string>::const_iterator it = data_.find(id);
36         return it == data_.end() ? string() : it->second;
37 }
38
39
40 void ExtraData::set(string const & id, string const & data)
41 {
42         data_[id] = data;
43 }
44
45
46 bool ResizeData::no_resize() const
47 {
48         return !usingScale() && width.zero() && height.zero();
49 }
50
51
52 bool ResizeData::usingScale() const
53 {
54         return (!scale.empty() && !float_equal(convert<double>(scale), 0.0, 0.05));
55 }
56
57
58 bool RotationData::no_rotation() const
59 {
60         return (angle.empty() || abs(convert<double>(angle)) < 0.1);
61 }
62
63
64 string const RotationData::adjAngle() const
65 {
66         // Ensure that angle lies in the range -360 < angle < 360
67         double rotAngle = convert<double>(angle);
68         if (abs(rotAngle) > 360.0) {
69                 rotAngle -= 360.0 * floor(rotAngle / 360.0);
70                 return convert<string>(rotAngle);
71         }
72         return angle;
73 }
74
75
76 namespace {
77
78 typedef Translator<RotationData::OriginType, string> OriginTranslator;
79 OriginTranslator const & originTranslator();
80
81 } // namespace anon
82
83
84 void RotationData::origin(string const & o)
85 {
86         origin_ = originTranslator().find(o);
87 }
88
89
90 string const RotationData::originString() const
91 {
92         return originTranslator().find(origin_);
93 }
94
95
96 string const ResizeLatexCommand::front_impl() const
97 {
98         if (data.no_resize())
99                 return string();
100
101         ostringstream os;
102         if (data.usingScale()) {
103                 double const scl = convert<double>(data.scale) / 100.0;
104                 os << "\\scalebox{" << scl << "}[" << scl << "]{";
105         } else {
106                 string width  = "!";
107                 string height = "!";
108                 if (data.keepAspectRatio) {
109                         if (data.width.inPixels(10) > data.height.inPixels(10))
110                                 width = data.width.asLatexString();
111                         else
112                                 height = data.height.asLatexString();
113                 } else {
114                         if (!data.width.zero())
115                                 width = data.width.asLatexString();
116                         if (!data.height.zero())
117                                 height = data.height.asLatexString();
118                 }
119
120                 os << "\\resizebox{"
121                    << width << "}{"
122                    << height << "}{";
123         }
124         return os.str();
125 }
126
127
128 string const ResizeLatexCommand::back_impl() const
129 {
130         if (data.no_resize())
131                 return string();
132
133         return "}";
134 }
135
136
137 namespace {
138
139 ostream & operator<<(ostream & os, RotationData::OriginType type)
140 {
141         switch (type) {
142         case RotationData::DEFAULT:
143         case RotationData::CENTER:
144                 break;
145         case RotationData::TOPLEFT:
146         case RotationData::TOPCENTER:
147         case RotationData::TOPRIGHT:
148                 os << 't';
149                 break;
150         case RotationData::BOTTOMLEFT:
151         case RotationData::BOTTOMCENTER:
152         case RotationData::BOTTOMRIGHT:
153                 os << 'b';
154                 break;
155         case RotationData::BASELINELEFT:
156         case RotationData::BASELINECENTER:
157         case RotationData::BASELINERIGHT:
158                 os << 'B';
159                 break;
160         }
161
162         switch (type) {
163         case RotationData::DEFAULT:
164                 break;
165         case RotationData::TOPLEFT:
166         case RotationData::BOTTOMLEFT:
167         case RotationData::BASELINELEFT:
168                 os << 'l';
169                 break;
170         case RotationData::CENTER:
171         case RotationData::TOPCENTER:
172         case RotationData::BOTTOMCENTER:
173         case RotationData::BASELINECENTER:
174                 os << 'c';
175                 break;
176         case RotationData::TOPRIGHT:
177         case RotationData::BOTTOMRIGHT:
178         case RotationData::BASELINERIGHT:
179                 os << 'r';
180                 break;
181         }
182
183         return os;
184 }
185
186 } // namespace anon
187
188
189 string const RotationLatexCommand::front_impl() const
190 {
191         if (data.no_rotation())
192                 return string();
193
194         ostringstream os;
195         os << "\\rotatebox";
196
197         if (data.origin() != RotationData::DEFAULT)
198                 os << "[origin=" << data.origin() << ']';
199
200         os << '{' << data.angle << "}{";
201         return os.str();
202 }
203
204
205 string const RotationLatexCommand::back_impl() const
206 {
207         if (data.no_rotation())
208                 return string();
209
210         return "}";
211 }
212
213
214 string const  ClipLatexOption::option_impl() const
215 {
216         if (!data.clip || data.bbox.empty())
217                 return string();
218
219         ostringstream os;
220         if (!data.bbox.empty())
221                 os << "bb=" << data.bbox.xl.asLatexString() << ' '
222                    << data.bbox.yb.asLatexString() << ' '
223                    << data.bbox.xr.asLatexString() << ' '
224                    << data.bbox.yt.asLatexString() << ',';
225         if (data.clip)
226                 os << "clip,";
227         return os.str();
228 }
229
230
231 string const ResizeLatexOption::option_impl() const
232 {
233         if (data.no_resize())
234                 return string();
235
236         ostringstream os;
237         if (data.usingScale()) {
238                 double const scl = convert<double>(data.scale);
239                 if (!float_equal(scl, 100.0, 0.05))
240                         os << "scale=" << scl / 100.0 << ',';
241                 return os.str();
242         }
243
244         if (!data.width.zero())
245                 os << "width=" << data.width.asLatexString() << ',';
246         if (!data.height.zero())
247                 os << "height=" << data.height.asLatexString() << ',';
248         if (data.keepAspectRatio)
249                 os << "keepaspectratio,";
250
251         return os.str();
252 }
253
254
255 string const RotationLatexOption ::option_impl() const
256 {
257         if (data.no_rotation())
258                 return string();
259
260         ostringstream os;
261         os << "angle=" << data.angle << ',';
262
263         if (data.origin() != RotationData::DEFAULT)
264                 os << "origin=" << data.origin() << ',';
265
266         return os.str();
267 }
268
269
270 string const sanitizeDocBookOption(string const & input)
271 {
272         return input;
273 }
274
275
276 string const sanitizeLatexOption(string const & input)
277 {
278         string::const_iterator begin = input.begin();
279         string::const_iterator end   = input.end();
280         string::const_iterator it = begin;
281
282         // Strip any leading commas
283         // "[,,,,foo..." -> "foo..." ("foo..." may be empty)
284         string output;
285         lyx::smatch what;
286         static lyx::regex const front("^( *\\[,*)(.*)$");
287
288         regex_match(it, end, what, front);
289         if (!what[0].matched) {
290                 lyxerr << "Unable to sanitize LaTeX \"Option\": "
291                        << input << '\n';
292                 return string();
293         }
294         it =  what[1].second;
295
296         // Replace any consecutive commas with a single one
297         // "foo,,,,bar" -> "foo,bar"
298         // with iterator now pointing to 'b'
299         static lyx::regex const commas("([^,]*)(,,*)(.*)$");
300         for (; it != end;) {
301                 regex_match(it, end, what, commas);
302                 if (!what[0].matched) {
303                         output += string(it, end);
304                         break;
305                 }
306                 output += what.str(1) + ",";
307                 it = what[3].first;
308         }
309
310         // Strip any trailing commas
311         // "...foo,,,]" -> "...foo" ("...foo,,," may be empty)
312         static lyx::regex const back("^(.*[^,])?,*\\] *$");
313         // false positive from coverity
314         // coverity[CHECKED_RETURN]
315         regex_match(output, what, back);
316         if (!what[0].matched) {
317                 lyxerr << "Unable to sanitize LaTeX \"Option\": "
318                        << output << '\n';
319                 return string();
320         }
321         output = what.str(1);
322
323         // Remove any surrounding whitespace
324         output = trim(output);
325
326         // If the thing is empty, leave it so, else wrap it in square brackets.
327         return output.empty() ? output : "[" + output + "]";
328 }
329
330
331 namespace {
332
333 template <typename Factory, typename Data, typename Transformer>
334 void extractIt(boost::any const & any_factory,
335                Data const & data, Transformer & transformer)
336 {
337         if (any_factory.type() != typeid(Factory))
338                 return;
339
340         Factory factory = boost::any_cast<Factory>(any_factory);
341         if (factory)
342                 transformer = factory(data);
343 }
344
345 } // namespace anon
346
347
348 TransformCommand::ptr_type
349 TransformStore::getCommandTransformer(RotationData const & data) const
350 {
351         TransformCommand::ptr_type ptr;
352         if (id == Rotate)
353                 extractIt<RotationCommandFactory>(any_factory, data, ptr);
354         return ptr;
355 }
356
357
358 TransformCommand::ptr_type
359 TransformStore::getCommandTransformer(ResizeData const & data) const
360 {
361         TransformCommand::ptr_type ptr;
362         if (id == Resize)
363                 extractIt<ResizeCommandFactory>(any_factory, data, ptr);
364         return ptr;
365 }
366
367
368 TransformOption::ptr_type
369 TransformStore::getOptionTransformer(RotationData const & data) const
370 {
371         TransformOption::ptr_type ptr;
372         if (id == Rotate)
373                 extractIt<RotationOptionFactory>(any_factory, data, ptr);
374         return ptr;
375 }
376
377
378 TransformOption::ptr_type
379 TransformStore::getOptionTransformer(ResizeData const & data) const
380 {
381         TransformOption::ptr_type ptr;
382         if (id == Resize)
383                 extractIt<ResizeOptionFactory>(any_factory, data, ptr);
384         return ptr;
385 }
386
387
388 TransformOption::ptr_type
389 TransformStore::getOptionTransformer(ClipData const & data) const
390 {
391         TransformOption::ptr_type ptr;
392         if (id == Clip)
393                 extractIt<ClipOptionFactory>(any_factory, data, ptr);
394         return ptr;
395 }
396
397
398
399 TransformOption::ptr_type
400 TransformStore::getOptionTransformer(string const & data) const
401 {
402         TransformOption::ptr_type ptr;
403         if (id == Extra)
404                 extractIt<ExtraOptionFactory>(any_factory, data, ptr);
405         return ptr;
406 }
407
408
409 namespace {
410
411 OriginTranslator const initOriginTranslator()
412 {
413         OriginTranslator translator(RotationData::DEFAULT, "default");
414         translator.addPair(RotationData::TOPLEFT,        "topleft");
415         translator.addPair(RotationData::BOTTOMLEFT,     "bottomleft");
416         translator.addPair(RotationData::BASELINELEFT,   "baselineleft");
417         translator.addPair(RotationData::CENTER,         "center");
418         translator.addPair(RotationData::TOPCENTER,      "topcenter");
419         translator.addPair(RotationData::BOTTOMCENTER,   "bottomcenter");
420         translator.addPair(RotationData::BASELINECENTER, "baselinecenter");
421         translator.addPair(RotationData::TOPRIGHT,       "topright");
422         translator.addPair(RotationData::BOTTOMRIGHT,    "bottomright");
423         translator.addPair(RotationData::BASELINERIGHT,  "baselineright");
424         return translator;
425 }
426
427
428 OriginTranslator const & originTranslator()
429 {
430         static OriginTranslator const translator = initOriginTranslator();
431         return translator;
432 }
433
434 } // namespace anon
435
436 } // namespace external
437 } // namespace lyx