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