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