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