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