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