]> git.lyx.org Git - lyx.git/blob - src/insets/ExternalTransforms.C
19670f087f33bf92c599f5b2e47531739e5b16ef
[lyx.git] / src / insets / ExternalTransforms.C
1 /**
2  * \file ExternalTransforms.C
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 "debug.h"
16
17 #include "support/lstrings.h"
18 #include "support/lyxlib.h" // float_equal
19 #include "support/tostr.h"
20 #include "support/translator.h"
21
22 #include <boost/regex.hpp>
23
24 #include <cmath> // std::abs
25 #include <sstream>
26
27 using lyx::support::float_equal;
28 using lyx::support::strToDbl;
29
30 using std::string;
31
32
33 namespace lyx {
34 namespace external {
35
36 string const ExtraData::get(string const & id) const
37 {
38         std::map<string, string>::const_iterator it = data_.find(id);
39         return it == data_.end() ? string() : it->second;
40 }
41
42
43 void ExtraData::set(string const & id, string const & data)
44 {
45         data_[id] = data;
46 }
47
48
49 bool ResizeData::no_resize() const
50 {
51         return !usingScale() && width.zero() && height.zero();
52 }
53
54
55 bool ResizeData::usingScale() const
56 {
57         return (!scale.empty() && !float_equal(strToDbl(scale), 0.0, 0.05));
58 }
59
60
61 bool RotationData::no_rotation() const
62 {
63         return (angle.empty() || std::abs(strToDbl(angle)) < 0.1);
64 }
65
66
67 string const RotationData::adjAngle() const
68 {
69         // Ensure that angle lies in the range -360 < angle < 360
70         double rotAngle = strToDbl(angle);
71         if (std::abs(rotAngle) > 360.0) {
72                 rotAngle -= 360.0 * floor(rotAngle / 360.0);
73                 return convert<string>(rotAngle);
74         }
75         return angle;
76 }
77
78
79 namespace {
80
81 typedef Translator<RotationData::OriginType, string> OriginTranslator;
82 OriginTranslator const & originTranslator();
83
84 } // namespace anon
85
86
87 void RotationData::origin(string const & o)
88 {
89         origin_ = originTranslator().find(o);
90 }
91
92
93 string const RotationData::originString() const
94 {
95         return originTranslator().find(origin_);
96 }
97
98
99 string const ResizeLatexCommand::front_impl() const
100 {
101         if (data.no_resize())
102                 return string();
103
104         std::ostringstream os;
105         if (data.usingScale()) {
106                 double const scl = strToDbl(data.scale) / 100.0;
107                 os << "\\scalebox{" << scl << "}[" << scl << "]{";
108         } else {
109                 string width  = "!";
110                 string height = "!";
111                 if (data.keepAspectRatio) {
112                         if (data.width.inPixels(10) > data.height.inPixels(10))
113                                 width = data.width.asLatexString();
114                         else
115                                 height = data.height.asLatexString();
116                 } else {
117                         if (!data.width.zero())
118                                 width = data.width.asLatexString();
119                         if (!data.height.zero())
120                                 height = data.height.asLatexString();
121                 }
122
123                 os << "\\resizebox{"
124                    << width << "}{"
125                    << height << "}{";
126         }
127         return os.str();
128 }
129
130
131 string const ResizeLatexCommand::back_impl() const
132 {
133         if (data.no_resize())
134                 return string();
135
136         return "}";
137 }
138
139
140 namespace {
141
142 std::ostream & operator<<(std::ostream & os, RotationData::OriginType type)
143 {
144         switch (type) {
145         case RotationData::DEFAULT:
146         case RotationData::CENTER:
147                 break;
148         case RotationData::TOPLEFT:
149         case RotationData::TOPCENTER:
150         case RotationData::TOPRIGHT:
151                 os << 't';
152                 break;
153         case RotationData::BOTTOMLEFT:
154         case RotationData::BOTTOMCENTER:
155         case RotationData::BOTTOMRIGHT:
156                 os << 'b';
157                 break;
158         case RotationData::BASELINELEFT:
159         case RotationData::BASELINECENTER:
160         case RotationData::BASELINERIGHT:
161                 os << 'B';
162                 break;
163         }
164
165         switch (type) {
166         case RotationData::DEFAULT:
167                 break;
168         case RotationData::TOPLEFT:
169         case RotationData::BOTTOMLEFT:
170         case RotationData::BASELINELEFT:
171                 os << 'l';
172                 break;
173         case RotationData::CENTER:
174         case RotationData::TOPCENTER:
175         case RotationData::BOTTOMCENTER:
176         case RotationData::BASELINECENTER:
177                 os << 'c';
178                 break;
179         case RotationData::TOPRIGHT:
180         case RotationData::BOTTOMRIGHT:
181         case RotationData::BASELINERIGHT:
182                 os << 'r';
183                 break;
184         }
185
186         return os;
187 }
188
189 } // namespace anon
190
191
192 string const RotationLatexCommand::front_impl() const
193 {
194         if (data.no_rotation())
195                 return string();
196
197         std::ostringstream os;
198         os << "\\rotatebox";
199
200         if (data.origin() != RotationData::DEFAULT)
201                 os << "[origin=" << data.origin() << ']';
202
203         os << '{' << data.angle << "}{";
204         return os.str();
205 }
206
207
208 string const RotationLatexCommand::back_impl() const
209 {
210         if (data.no_rotation())
211                 return string();
212
213         return "}";
214 }
215
216
217 string const  ClipLatexOption::option_impl() const
218 {
219         if (!data.clip || data.bbox.empty())
220                 return string();
221
222         std::ostringstream os;
223         if (!data.bbox.empty())
224                 os << "bb=" << data.bbox << ',';
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         std::ostringstream os;
237         if (data.usingScale()) {
238                 double scl = strToDbl(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         std::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         boost::smatch what;
286         static boost::regex const front("^( *[[],*)(.*)$");
287
288         regex_match(it, end, what, front, boost::match_partial);
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 boost::regex const commas("([^,]*)(,,*)(.*)$");
300         for (; it != end;) {
301                 regex_match(it, end, what, commas, boost::match_partial);
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 boost::regex const back("^(.*[^,])?,*[]] *$");
313         regex_match(output, what, back);
314         if (!what[0].matched) {
315                 lyxerr << "Unable to sanitize LaTeX \"Option\": "
316                        << output << '\n';
317                 return string();
318         }
319         output = what.str(1);
320
321         // Remove any surrounding whitespace
322         output = lyx::support::trim(output);
323
324         // If the thing is empty, leave it so, else wrap it in square brackets.
325         return output.empty() ? output : "[" + output + "]";
326 }
327
328
329 string const sanitizeLinuxDocOption(string const & input)
330 {
331         return input;
332 }
333
334
335 namespace {
336
337 template <typename Factory, typename Data, typename Transformer>
338 void extractIt(boost::any const & any_factory,
339                Data const & data, Transformer & transformer)
340 {
341         if (any_factory.type() != typeid(Factory))
342                 return;
343
344         Factory factory = boost::any_cast<Factory>(any_factory);
345         if (!factory.empty())
346                 transformer = factory(data);
347 }
348
349 } // namespace anon
350
351
352 TransformCommand::ptr_type
353 TransformStore::getCommandTransformer(RotationData const & data) const
354 {
355         TransformCommand::ptr_type ptr;
356         if (id == Rotate)
357                 extractIt<RotationCommandFactory>(any_factory, data, ptr);
358         return ptr;
359 }
360
361
362 TransformCommand::ptr_type
363 TransformStore::getCommandTransformer(ResizeData const & data) const
364 {
365         TransformCommand::ptr_type ptr;
366         if (id == Resize)
367                 extractIt<ResizeCommandFactory>(any_factory, data, ptr);
368         return ptr;
369 }
370
371
372 TransformOption::ptr_type
373 TransformStore::getOptionTransformer(RotationData const & data) const
374 {
375         TransformOption::ptr_type ptr;
376         if (id == Rotate)
377                 extractIt<RotationOptionFactory>(any_factory, data, ptr);
378         return ptr;
379 }
380
381
382 TransformOption::ptr_type
383 TransformStore::getOptionTransformer(ResizeData const & data) const
384 {
385         TransformOption::ptr_type ptr;
386         if (id == Resize)
387                 extractIt<ResizeOptionFactory>(any_factory, data, ptr);
388         return ptr;
389 }
390
391
392 TransformOption::ptr_type
393 TransformStore::getOptionTransformer(ClipData const & data) const
394 {
395         TransformOption::ptr_type ptr;
396         if (id == Clip)
397                 extractIt<ClipOptionFactory>(any_factory, data, ptr);
398         return ptr;
399 }
400
401
402
403 TransformOption::ptr_type
404 TransformStore::getOptionTransformer(string const & data) const
405 {
406         TransformOption::ptr_type ptr;
407         if (id == Extra)
408                 extractIt<ExtraOptionFactory>(any_factory, data, ptr);
409         return ptr;
410 }
411
412
413 namespace {
414
415 OriginTranslator const initOriginTranslator()
416 {
417         OriginTranslator translator(RotationData::DEFAULT, "default");
418         translator.addPair(RotationData::TOPLEFT,        "topleft");
419         translator.addPair(RotationData::BOTTOMLEFT,     "bottomleft");
420         translator.addPair(RotationData::BASELINELEFT,   "baselineleft");
421         translator.addPair(RotationData::CENTER,         "center");
422         translator.addPair(RotationData::TOPCENTER,      "topcenter");
423         translator.addPair(RotationData::BOTTOMCENTER,   "bottomcenter");
424         translator.addPair(RotationData::BASELINECENTER, "baselinecenter");
425         translator.addPair(RotationData::TOPRIGHT,       "topright");
426         translator.addPair(RotationData::BOTTOMRIGHT,    "bottomright");
427         translator.addPair(RotationData::BASELINERIGHT,  "baselineright");
428         return translator;
429 }
430
431
432 OriginTranslator const & originTranslator()
433 {
434         static OriginTranslator const translator = initOriginTranslator();
435         return translator;
436 }
437
438 } // namespace anon
439
440 } // namespace external
441 } // namespace lyx