]> git.lyx.org Git - lyx.git/blob - src/insets/ExternalTransforms.C
convert graphics for .tex export, fixes bug 1231
[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/translator.h"
20
21 #include <boost/regex.hpp>
22 #include "support/std_sstream.h"
23 #include <cmath> // std::abs
24
25 using lyx::support::float_equal;
26
27 using std::string;
28
29
30 namespace lyx {
31 namespace external {
32
33 string const ExtraData::get(string const & id) const
34 {
35         std::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 !float_equal(scale, 0.0, 0.05);
55 }
56
57
58 bool RotationData::no_rotation() const
59 {
60         return (std::abs(angle()) < 0.1);
61 }
62
63
64 void RotationData::angle(double a)
65 {
66         // Ensure that angle_ lies in the range -360 < angle_ < 360.
67         int const multiples = int(a) / 360;
68         angle_ = a - (multiples * 360);
69 }
70
71
72 namespace {
73
74 typedef Translator<RotationData::OriginType, string> OriginTranslator;
75 OriginTranslator const & originTranslator();
76
77 } // namespace anon
78
79
80 void RotationData::origin(string const & o)
81 {
82         origin_ = originTranslator().find(o);
83 }
84
85
86 string const RotationData::originString() const
87 {
88         return originTranslator().find(origin_);
89 }
90
91
92 string const ResizeLatexCommand::front_impl() const
93 {
94         if (data.no_resize())
95                 return string();
96
97         std::ostringstream os;
98         if (data.usingScale()) {
99                 double const scl = data.scale / 100.0;
100                 os << "\\scalebox{" << scl << "}{" << scl << "}{";
101         } else {
102                 string width  = "!";
103                 string height = "!";
104                 if (data.keepAspectRatio) {
105                         if (data.width.inPixels(10) > data.height.inPixels(10))
106                                 width = data.width.asLatexString();
107                         else
108                                 height = data.height.asLatexString();
109                 } else {
110                         if (!data.width.zero())
111                                 width = data.width.asLatexString();
112                         if (!data.height.zero())
113                                 height = data.height.asLatexString();
114                 }
115
116                 os << "\\resizebox{"
117                    << width << "}{"
118                    << height << "}{";
119         }
120         return os.str();
121 }
122
123
124 string const ResizeLatexCommand::back_impl() const
125 {
126         if (data.no_resize())
127                 return string();
128
129         return "}";
130 }
131
132
133 namespace {
134
135 std::ostream & operator<<(std::ostream & os, RotationData::OriginType type)
136 {
137         switch (type) {
138         case RotationData::DEFAULT:
139         case RotationData::CENTER:
140                 break;
141         case RotationData::TOPLEFT:
142         case RotationData::TOPCENTER:
143         case RotationData::TOPRIGHT:
144                 os << 't';
145                 break;
146         case RotationData::BOTTOMLEFT:
147         case RotationData::BOTTOMCENTER:
148         case RotationData::BOTTOMRIGHT:
149                 os << 'b';
150                 break;
151         case RotationData::BASELINELEFT:
152         case RotationData::BASELINECENTER:
153         case RotationData::BASELINERIGHT:
154                 os << 'B';
155                 break;
156         }
157
158         switch (type) {
159         case RotationData::DEFAULT:
160                 break;
161         case RotationData::TOPLEFT:
162         case RotationData::BOTTOMLEFT:
163         case RotationData::BASELINELEFT:
164                 os << 'l';
165                 break;
166         case RotationData::CENTER:
167         case RotationData::TOPCENTER:
168         case RotationData::BOTTOMCENTER:
169         case RotationData::BASELINECENTER:
170                 os << 'c';
171                 break;
172         case RotationData::TOPRIGHT:
173         case RotationData::BOTTOMRIGHT:
174         case RotationData::BASELINERIGHT:
175                 os << 'r';
176                 break;
177         }
178
179         return os;
180 }
181
182 } // namespace anon
183
184
185 string const RotationLatexCommand::front_impl() const
186 {
187         if (data.no_rotation())
188                 return string();
189
190         std::ostringstream os;
191         os << "\\rotatebox";
192
193         if (data.origin() != RotationData::DEFAULT)
194                 os << "[origin=" << data.origin() << ']';
195
196         os << '{' << data.angle() << "}{";
197         return os.str();
198 }
199
200
201 string const RotationLatexCommand::back_impl() const
202 {
203         if (data.no_rotation())
204                 return string();
205
206         return "}";
207 }
208
209
210 string const  ClipLatexOption::option_impl() const
211 {
212         if (!data.clip || data.bbox.empty())
213                 return string();
214
215         std::ostringstream os;
216         if (!data.bbox.empty())
217                 os << "bb=" << data.bbox << ',';
218         if (data.clip)
219                 os << "clip,";
220         return os.str();
221 }
222
223
224 string const ResizeLatexOption::option_impl() const
225 {
226         if (data.no_resize())
227                 return string();
228
229         std::ostringstream os;
230         if (data.usingScale()) {
231                 if (!float_equal(data.scale, 100.0, 0.05))
232                         os << "scale=" << data.scale / 100.0 << ',';
233                 return os.str();
234         }
235
236         if (!data.width.zero())
237                 os << "width=" << data.width.asLatexString() << ',';
238         if (!data.height.zero())
239                 os << "height=" << data.height.asLatexString() << ',';
240         if (data.keepAspectRatio)
241                 os << "keepaspectratio,";
242
243         return os.str();
244 }
245
246
247 string const RotationLatexOption ::option_impl() const
248 {
249         if (data.no_rotation())
250                 return string();
251
252         std::ostringstream os;
253         os << "angle=" << data.angle() << ',';
254
255         if (data.origin() != RotationData::DEFAULT)
256                 os << "origin=" << data.origin() << ',';
257
258         return os.str();
259 }
260
261
262 string const sanitizeDocBookOption(string const & input)
263 {
264         return input;
265 }
266
267
268 string const sanitizeLatexOption(string const & input)
269 {
270         string::const_iterator begin = input.begin();
271         string::const_iterator end   = input.end();
272         string::const_iterator it = begin;
273
274         // Strip any leading commas
275         // "[,,,,foo..." -> "foo..."
276         string output;
277         boost::smatch what;
278         static boost::regex const front("^( *[[],*)(.*)$");
279
280         regex_match(it, end, what, front, boost::match_partial);
281         if (!what[0].matched) {
282                 lyxerr << "Unable to sanitize LaTeX \"Option\": "
283                        << output << '\n';
284                 return string();
285         }
286         it =  what[1].second;
287
288         // Replace any consecutive commas with a single one
289         // "foo,,,,bar" -> "foo,bar"
290         // with iterator now pointing to 'b'
291         static boost::regex const commas("([^,]*)(,,*)(.*)$");
292         for (; it != end;) {
293                 regex_match(it, end, what, commas, boost::match_partial);
294                 if (!what[0].matched) {
295                         output += string(it, end);
296                         break;
297                 }
298                 output += what.str(1) + ",";
299                 it = what[3].first;
300         }
301
302         // Strip any trailing commas
303         // "...foo,,,]" -> "...foo"
304         static boost::regex const back("^(.*[^,])(,*[]] *)$");
305         regex_match(output, what, back);
306         if (!what[0].matched) {
307                 lyxerr << "Unable to sanitize LaTeX \"Option\": "
308                        << output << '\n';
309                 return string();
310         }
311         output = what.str(1);
312
313         // Remove any surrounding whitespace
314         output = lyx::support::trim(output);
315
316         // If the thing is empty, leave it so, else wrap it in square brackets.
317         return output.empty() ? output : "[" + output + "]";
318 }
319
320
321 string const sanitizeLinuxDocOption(string const & input)
322 {
323         return input;
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