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