]> git.lyx.org Git - lyx.git/blob - src/insets/ExternalTransforms.cpp
Graphics cleanup: get rid of Previews.{h,cpp}
[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 << ',';
222         if (data.clip)
223                 os << "clip,";
224         return os.str();
225 }
226
227
228 string const ResizeLatexOption::option_impl() const
229 {
230         if (data.no_resize())
231                 return string();
232
233         ostringstream os;
234         if (data.usingScale()) {
235                 double const scl = convert<double>(data.scale);
236                 if (!float_equal(scl, 100.0, 0.05))
237                         os << "scale=" << scl / 100.0 << ',';
238                 return os.str();
239         }
240
241         if (!data.width.zero())
242                 os << "width=" << data.width.asLatexString() << ',';
243         if (!data.height.zero())
244                 os << "height=" << data.height.asLatexString() << ',';
245         if (data.keepAspectRatio)
246                 os << "keepaspectratio,";
247
248         return os.str();
249 }
250
251
252 string const RotationLatexOption ::option_impl() const
253 {
254         if (data.no_rotation())
255                 return string();
256
257         ostringstream os;
258         os << "angle=" << data.angle << ',';
259
260         if (data.origin() != RotationData::DEFAULT)
261                 os << "origin=" << data.origin() << ',';
262
263         return os.str();
264 }
265
266
267 string const sanitizeDocBookOption(string const & input)
268 {
269         return input;
270 }
271
272
273 string const sanitizeLatexOption(string const & input)
274 {
275         string::const_iterator begin = input.begin();
276         string::const_iterator end   = input.end();
277         string::const_iterator it = begin;
278
279         // Strip any leading commas
280         // "[,,,,foo..." -> "foo..." ("foo..." may be empty)
281         string output;
282         lyx::smatch what;
283         static lyx::regex const front("^( *[[],*)(.*)$");
284
285         regex_match(it, end, what, front, regex_constants::match_partial);
286         if (!what[0].matched) {
287                 lyxerr << "Unable to sanitize LaTeX \"Option\": "
288                        << input << '\n';
289                 return string();
290         }
291         it =  what[1].second;
292
293         // Replace any consecutive commas with a single one
294         // "foo,,,,bar" -> "foo,bar"
295         // with iterator now pointing to 'b'
296         static lyx::regex const commas("([^,]*)(,,*)(.*)$");
297         for (; it != end;) {
298                 regex_match(it, end, what, commas, regex_constants::match_partial);
299                 if (!what[0].matched) {
300                         output += string(it, end);
301                         break;
302                 }
303                 output += what.str(1) + ",";
304                 it = what[3].first;
305         }
306
307         // Strip any trailing commas
308         // "...foo,,,]" -> "...foo" ("...foo,,," may be empty)
309         static lyx::regex const back("^(.*[^,])?,*[]] *$");
310         regex_match(output, what, back);
311         if (!what[0].matched) {
312                 lyxerr << "Unable to sanitize LaTeX \"Option\": "
313                        << output << '\n';
314                 return string();
315         }
316         output = what.str(1);
317
318         // Remove any surrounding whitespace
319         output = trim(output);
320
321         // If the thing is empty, leave it so, else wrap it in square brackets.
322         return output.empty() ? output : "[" + output + "]";
323 }
324
325
326 namespace {
327
328 template <typename Factory, typename Data, typename Transformer>
329 void extractIt(boost::any const & any_factory,
330                Data const & data, Transformer & transformer)
331 {
332         if (any_factory.type() != typeid(Factory))
333                 return;
334
335         Factory factory = boost::any_cast<Factory>(any_factory);
336         if (!factory.empty())
337                 transformer = factory(data);
338 }
339
340 } // namespace anon
341
342
343 TransformCommand::ptr_type
344 TransformStore::getCommandTransformer(RotationData const & data) const
345 {
346         TransformCommand::ptr_type ptr;
347         if (id == Rotate)
348                 extractIt<RotationCommandFactory>(any_factory, data, ptr);
349         return ptr;
350 }
351
352
353 TransformCommand::ptr_type
354 TransformStore::getCommandTransformer(ResizeData const & data) const
355 {
356         TransformCommand::ptr_type ptr;
357         if (id == Resize)
358                 extractIt<ResizeCommandFactory>(any_factory, data, ptr);
359         return ptr;
360 }
361
362
363 TransformOption::ptr_type
364 TransformStore::getOptionTransformer(RotationData const & data) const
365 {
366         TransformOption::ptr_type ptr;
367         if (id == Rotate)
368                 extractIt<RotationOptionFactory>(any_factory, data, ptr);
369         return ptr;
370 }
371
372
373 TransformOption::ptr_type
374 TransformStore::getOptionTransformer(ResizeData const & data) const
375 {
376         TransformOption::ptr_type ptr;
377         if (id == Resize)
378                 extractIt<ResizeOptionFactory>(any_factory, data, ptr);
379         return ptr;
380 }
381
382
383 TransformOption::ptr_type
384 TransformStore::getOptionTransformer(ClipData const & data) const
385 {
386         TransformOption::ptr_type ptr;
387         if (id == Clip)
388                 extractIt<ClipOptionFactory>(any_factory, data, ptr);
389         return ptr;
390 }
391
392
393
394 TransformOption::ptr_type
395 TransformStore::getOptionTransformer(string const & data) const
396 {
397         TransformOption::ptr_type ptr;
398         if (id == Extra)
399                 extractIt<ExtraOptionFactory>(any_factory, data, ptr);
400         return ptr;
401 }
402
403
404 namespace {
405
406 OriginTranslator const initOriginTranslator()
407 {
408         OriginTranslator translator(RotationData::DEFAULT, "default");
409         translator.addPair(RotationData::TOPLEFT,        "topleft");
410         translator.addPair(RotationData::BOTTOMLEFT,     "bottomleft");
411         translator.addPair(RotationData::BASELINELEFT,   "baselineleft");
412         translator.addPair(RotationData::CENTER,         "center");
413         translator.addPair(RotationData::TOPCENTER,      "topcenter");
414         translator.addPair(RotationData::BOTTOMCENTER,   "bottomcenter");
415         translator.addPair(RotationData::BASELINECENTER, "baselinecenter");
416         translator.addPair(RotationData::TOPRIGHT,       "topright");
417         translator.addPair(RotationData::BOTTOMRIGHT,    "bottomright");
418         translator.addPair(RotationData::BASELINERIGHT,  "baselineright");
419         return translator;
420 }
421
422
423 OriginTranslator const & originTranslator()
424 {
425         static OriginTranslator const translator = initOriginTranslator();
426         return translator;
427 }
428
429 } // namespace anon
430
431 } // namespace external
432 } // namespace lyx