]> git.lyx.org Git - lyx.git/blob - src/Length.cpp
GuiBox.cpp: fix logic
[lyx.git] / src / Length.cpp
1 /**
2  * \file Length.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Matthias Ettrich
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author Dekel Tsur
12  *
13  * Full author contact details are available in file CREDITS.
14  */
15
16 #include <config.h>
17
18 #include "Length.h"
19 #include "LyXRC.h"
20 #include "MetricsInfo.h"
21
22 #include "frontends/FontMetrics.h"
23
24 #include "support/docstream.h"
25 #include "support/lstrings.h"
26
27 #include <sstream>
28 #include <iomanip>
29
30 using namespace std;
31
32 namespace lyx {
33
34
35 /////////////////////////////////////////////////////////////////////
36 //
37 // Length
38 //
39 /////////////////////////////////////////////////////////////////////
40
41 Length::Length()
42         : val_(0), unit_(Length::UNIT_NONE)
43 {}
44
45
46 Length::Length(double v, Length::UNIT u)
47         : val_(v), unit_(u)
48 {}
49
50
51 Length::Length(string const & data)
52         : val_(0), unit_(Length::PT)
53 {
54         Length tmp;
55
56         if (!isValidLength(data, &tmp))
57                 return; // should raise an exception
58
59         val_  = tmp.val_;
60         unit_ = tmp.unit_;
61 }
62
63
64 string const Length::asString() const
65 {
66         ostringstream os;
67         if (unit_ != UNIT_NONE)
68                 os << val_ << unit_name[unit_]; // setw?
69         return os.str();
70 }
71
72
73 docstring const Length::asDocstring() const
74 {
75         odocstringstream os;
76         if (unit_ != UNIT_NONE)
77                 os << val_ << unit_name[unit_]; // setw?
78         return os.str();
79 }
80
81
82 string const Length::asLatexString() const
83 {
84         ostringstream os;
85         // Do not allow scientific notation (e.g. 1.2e+03), since this is not valid
86         // LaTeX (bug 9416)
87         switch (unit_) {
88         case PTW:
89                 os << support::formatFPNumber(val_ / 100.0) << "\\textwidth";
90                 break;
91         case PCW:
92                 os << support::formatFPNumber(val_ / 100.0) << "\\columnwidth";
93                 break;
94         case PPW:
95                 os << support::formatFPNumber(val_ / 100.0) << "\\paperwidth";
96                 break;
97         case PLW:
98                 os << support::formatFPNumber(val_ / 100.0) << "\\linewidth";
99                 break;
100         case PTH:
101                 os << support::formatFPNumber(val_ / 100.0) << "\\textheight";
102                 break;
103         case PPH:
104                 os << support::formatFPNumber(val_ / 100.0) << "\\paperheight";
105                 break;
106         case UNIT_NONE:
107                 break;
108         default:
109                 os << support::formatFPNumber(val_) << unit_name[unit_];
110           break;
111         }
112         return os.str();
113 }
114
115
116 string const Length::asHTMLString() const
117 {
118         ostringstream os;
119         switch (unit_) {
120         case PT:
121         case BP:
122         case DD:
123                 // close enough
124                 os << val_ << "pt";
125                 break;
126         case MM:
127         case CM:
128         case PC:
129         case IN:
130         case EX:
131         case EM:
132                 os << val_ << unit_name[unit_];
133                 break;
134         case CC:
135                 os << val_/12.0 << "pt";
136                 break;
137         case MU:
138                 os << val_/18.0 << "em";
139                 break;
140         case PTW:
141         case PPW:
142         case PLW:
143         case PCW:
144         case PTH:
145         case PPH:
146                 // what it's a percentage of probably won't make sense for HTML,
147                 // so we'll assume people have chosen these appropriately
148                 os << val_ << '%';
149                 break;
150         case SP:
151         case UNIT_NONE:
152                 break;
153         }
154         return os.str();
155 }
156
157
158 double Length::value() const
159 {
160         return val_;
161 }
162
163
164 Length::UNIT Length::unit() const
165 {
166         return unit_;
167 }
168
169
170 void Length::value(double v)
171 {
172         val_ = v;
173 }
174
175
176 void Length::unit(Length::UNIT u)
177 {
178         unit_ = u;
179 }
180
181
182 bool Length::zero() const
183 {
184         return val_ == 0.0;
185 }
186
187
188 bool Length::empty() const
189 {
190         return unit_ == Length::UNIT_NONE;
191 }
192
193
194 int Length::inPixels(int text_width, int em_width_base) const
195 {
196         // Zoom factor specified by user in percent
197         double const zoom = lyxrc.zoom / 100.0; // [percent]
198
199         // DPI setting for monitor: pixels/inch
200         double const dpi = lyxrc.dpi; // screen resolution [pixels/inch]
201
202         double const em_width = (em_width_base > 0)
203                 ? em_width_base
204                 : 10*(dpi/72.27)*zoom;
205         // A different estimate for em_width is
206         // theFontMetrics(FontInfo(sane_font)).em()
207         // but this estimate might not be more accurate as the screen font
208         // is different then the latex font.
209
210         // Pixel values are scaled so that the ratio
211         // between lengths and font sizes on the screen
212         // is the same as on paper.
213
214         double result = 0.0;
215
216         switch (unit_) {
217         case Length::SP:
218                 // Scaled point: sp = 1/65536 pt
219                 result = zoom * dpi * val_
220                         / (72.27 * 65536); // 4736286.7
221                 break;
222         case Length::PT:
223                 // Point: 1 pt = 1/72.27 inch
224                 result = zoom * dpi * val_
225                         / 72.27; // 72.27
226                 break;
227         case Length::BP:
228                 // Big point: 1 bp = 1/72 inch
229                 result = zoom * dpi * val_
230                         / 72; // 72
231                 break;
232         case Length::DD:
233                 // Didot: 1157dd = 1238 pt?
234                 result = zoom * dpi * val_
235                         / (72.27 / (0.376 * 2.845)); // 67.559735
236                 break;
237         case Length::MM:
238                 // Millimeter: 1 mm = 1/25.4 inch
239                 result = zoom * dpi * val_
240                         / 25.4; // 25.4
241                 break;
242         case Length::PC:
243                 // Pica: 1 pc = 12 pt
244                 result = zoom * dpi * val_
245                         / (72.27 / 12); // 6.0225
246                 break;
247         case Length::CC:
248                 // Cicero: 1 cc = 12 dd
249                 result = zoom * dpi * val_
250                         / (72.27 / (12 * 0.376 * 2.845)); // 5.6299779
251                 break;
252         case Length::CM:
253                 // Centimeter: 1 cm = 1/2.54 inch
254                 result = zoom * dpi * val_
255                         / 2.54; // 2.54
256                 break;
257         case Length::IN:
258                 // Inch
259                 result = zoom * dpi * val_;
260                 break;
261         case Length::EX:
262                 // Ex: The height of an "x"
263                 // 0.4305 is the ration between 1ex and 1em in cmr10
264                 result = val_ * em_width * 0.4305;
265                 break;
266         case Length::EM:
267                 // Em: The width of an "m"
268                 result = val_ * em_width;
269                 break;
270         case Length::MU:
271                 // math unit = 1/18em
272                 result = val_ * em_width / 18;
273                 break;
274         case Length::PCW: // Always % of workarea
275         case Length::PTW:
276         case Length::PLW:
277                 result = val_ * text_width / 100;
278                 break;
279         case Length::PPW:
280                 // paperwidth/textwidth is 1.7 for A4 paper with default margins
281                 result = val_ * text_width * 1.7 / 100;
282                 break;
283         case Length::PTH:
284                 result = val_ * text_width * 1.787 / 100;
285                 break;
286         case Length::PPH:
287                 result = val_ * text_width * 2.2 / 100;
288                 break;
289         case Length::UNIT_NONE:
290                 result = 0;  // this cannot happen
291                 break;
292         }
293         return static_cast<int>(result + ((result >= 0) ? 0.5 : -0.5));
294 }
295
296
297 int Length::inPixels(MetricsBase const & base) const
298 {
299         return inPixels(base.textwidth, theFontMetrics(base.font).em());
300 }
301
302
303 int Length::inBP() const
304 {
305         // return any Length value as a one with
306         // the PostScript point, called bp (big points)
307         double result = 0.0;
308         switch (unit_) {
309         case Length::CM:
310                 // 1bp = 0.2835cm
311                 result = val_ * 28.346;
312                 break;
313         case Length::MM:
314                 // 1bp = 0.02835mm
315                 result = val_ * 2.8346;
316                 break;
317         case Length::IN:
318                 // 1pt = 1/72in
319                 result = val_ * 72.0;
320                 break;
321         default:
322                 // no other than bp possible
323                 result = val_;
324                 break;
325         }
326         return static_cast<int>(result + 0.5);
327 }
328
329
330 Length::UNIT Length::defaultUnit()
331 {
332         return lyxrc.default_length_unit;
333 }
334
335
336
337 bool operator==(Length const & l1, Length const & l2)
338 {
339         return l1.value() == l2.value() && l1.unit() == l2.unit();
340 }
341
342
343 bool operator!=(Length const & l1, Length const & l2)
344 {
345         return !(l1 == l2);
346 }
347
348
349 /////////////////////////////////////////////////////////////////////
350 //
351 // GlueLength
352 //
353 /////////////////////////////////////////////////////////////////////
354
355
356 GlueLength::GlueLength(Length const & len)
357         : len_(len)
358 {}
359
360
361 GlueLength::GlueLength(Length const & len, Length const & plus,
362                 Length const & minus)
363         : len_(len), plus_(plus), minus_(minus)
364 {}
365
366
367 GlueLength::GlueLength(string const & data)
368 {
369         isValidGlueLength(data, this);
370 }
371
372
373 string const GlueLength::asString() const
374 {
375         if (len_.empty())
376                 return string();
377
378         ostringstream buffer;
379
380         buffer << len_.value();
381
382         if (plus_.zero() && minus_.zero()) {
383                 buffer << unit_name[len_.unit()];
384                 return buffer.str();
385         }
386
387         // just len and plus
388         if (minus_.zero()) {
389                 if (len_.unit() != plus_.unit())
390                         buffer << unit_name[len_.unit()];
391                 buffer << '+' << plus_.value();
392                 buffer << unit_name[plus_.unit()];
393                 return buffer.str();
394         }
395
396         // just len and minus
397         if (plus_.zero()) {
398                 if (len_.unit() != minus_.unit())
399                         buffer << unit_name[len_.unit()];
400                 buffer << '-' << minus_.value();
401                 buffer << unit_name[minus_.unit()];
402                 return buffer.str();
403         }
404
405         // ok, len, plus AND minus
406
407         // len+-
408         if (minus_ == plus_) {
409                 if (len_.unit() != minus_.unit())
410                         buffer << unit_name[len_.unit()];
411                 buffer << "+-" << minus_.value();
412                 buffer << unit_name[minus_.unit()];
413                 return buffer.str();
414         }
415
416         // this is so rare a case, why bother minimising units ?
417
418         buffer << unit_name[len_.unit()];
419         buffer << '+' << plus_.value() << unit_name[plus_.unit()];
420         buffer << '-' << minus_.value() << unit_name[minus_.unit()];
421
422         return buffer.str();
423 }
424
425
426 string const GlueLength::asLatexString() const
427 {
428         ostringstream buffer;
429         // use Length::asLatexString() to handle also the percent lengths
430         buffer << len_.Length::asLatexString();
431         if (!plus_.zero())
432                 buffer << " plus " << plus_.Length::asLatexString();
433         if (!minus_.zero())
434                 buffer << " minus " << minus_.Length::asLatexString();
435         return buffer.str();
436 }
437
438
439 Length const & GlueLength::len() const
440 {
441         return len_;
442 }
443
444
445 Length const & GlueLength::plus() const
446 {
447         return plus_;
448 }
449
450
451 Length const & GlueLength::minus() const
452 {
453         return minus_;
454 }
455
456
457 bool operator==(GlueLength const & l1, GlueLength const & l2)
458 {
459         return l1.len() == l2.len()
460                  && l1.plus() == l2.plus()
461                  && l1.minus() == l2.minus();
462 }
463
464
465 bool operator!=(GlueLength const & l1, GlueLength const & l2)
466 {
467         return !(l1 == l2);
468 }
469
470
471 } // namespace lyx