1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 #include "Wt/WApplication.h"
7 #include "Wt/WEnvironment.h"
8 #include "Wt/WException.h"
9 #include "Wt/WLength.h"
10 #include "Wt/WLogger.h"
11 
12 #include "WebUtils.h"
13 
14 #include <cstring>
15 #include <boost/algorithm/string.hpp>
16 
17 namespace Wt {
18 
19 LOGGER("WLength");
20 
21 WLength WLength::Auto;
22 
WLength()23 WLength::WLength()
24   : auto_(true),
25     unit_(LengthUnit::Pixel),
26     value_(-1)
27 { }
28 
WLength(const std::string & str)29 WLength::WLength(const std::string &str)
30 {
31   parseCssString(str.c_str());
32 }
33 
parseCssString(const char * s)34 void WLength::parseCssString(const char *s)
35 {
36   auto_ = false;
37   unit_ = LengthUnit::Pixel;
38   value_ = -1;
39 
40   if (std::string("auto") == s) {
41     auto_ = true;
42     return;
43   }
44 
45   char *end = nullptr;
46 #ifndef WT_TARGET_JAVA
47   value_ = std::strtod(s, &end);
48 #else
49   Utils::stringToDouble(s, &end, value_);
50 #endif
51 
52   if (s == end) {
53     LOG_ERROR("cannot parse CSS length: '" << s << "'");
54     auto_ = true;
55     return;
56   }
57 
58   std::string unit(end);
59   boost::trim(unit);
60 
61   if (unit == "em")
62     unit_ = LengthUnit::FontEm;
63   else if (unit == "ex")
64     unit_ = LengthUnit::FontEx;
65   else if (unit.empty() || unit == "px")
66     unit_ = LengthUnit::Pixel;
67   else if (unit == "in")
68     unit_ = LengthUnit::Inch;
69   else if (unit == "cm")
70     unit_ = LengthUnit::Centimeter;
71   else if (unit == "mm")
72     unit_ = LengthUnit::Millimeter;
73   else if (unit == "pt")
74     unit_ = LengthUnit::Point;
75   else if (unit == "pc")
76     unit_ = LengthUnit::Pica;
77   else if (unit == "%")
78     unit_ = LengthUnit::Percentage;
79   else if (unit == "vw")
80     unit_ = LengthUnit::ViewportWidth;
81   else if (unit == "vh")
82     unit_ = LengthUnit::ViewportHeight;
83   else if (unit == "vmin")
84     unit_ = LengthUnit::ViewportMin;
85   else if (unit == "vmax")
86     unit_ = LengthUnit::ViewportMax;
87   else {
88     LOG_ERROR("unrecognized unit in '" << s << "'");
89     auto_ = true;
90     value_ = -1;
91     unit_ = LengthUnit::Pixel;
92   }
93 }
94 
WLength(double value,LengthUnit unit)95 WLength::WLength(double value, LengthUnit unit)
96   : auto_(false),
97     value_(value)
98 {
99   setUnit(unit);
100 }
101 
setUnit(LengthUnit unit)102 void WLength::setUnit(LengthUnit unit)
103 {
104   unit_ = unit;
105 }
106 
107 bool WLength::operator== (const WLength& other) const
108 {
109   return
110        auto_  == other.auto_
111     && unit_  == other.unit_
112     && value_ == other.value_;
113 }
114 
115 bool WLength::operator!= (const WLength& other) const
116 {
117   return !(*this == other);
118 }
119 
cssText()120 const std::string WLength::cssText() const
121 {
122   static const char *unitText[]
123     = { "em", "ex", "px", "in", "cm", "mm", "pt", "pc", "%", "vw", "vh", "vmin", "vmax" };
124 
125   if (auto_)
126     return "auto";
127   else {
128 #ifndef WT_TARGET_JAVA
129     char buf[30];
130     Utils::round_css_str(value_, 1, buf);
131     if (unit_ == LengthUnit::ViewportMin) {
132       WApplication *app = WApplication::instance();
133       if (app && app->environment().agentIsIElt(10)) {
134         std::strcat(buf, "vm");
135       } else {
136         std::strcat(buf, "vmin");
137       }
138     } else {
139       std::strcat(buf, unitText[static_cast<unsigned int>(unit_)]);
140     }
141     return buf;
142 #else
143     return std::to_string(value_) + unitText[static_cast<unsigned int>(unit_)];
144 #endif
145   }
146 }
147 
toPixels(double fontSize)148 double WLength::toPixels(double fontSize) const
149 {
150   static const double pxPerPt = 4.0/3.0;
151   static const double unitFactor[]
152     = { 1,
153 	72 * pxPerPt,        // 72 'CSS'points in an inch
154 	72 / 2.54 * pxPerPt, // 2.54 cm in an inch
155 	72 / 25.4 * pxPerPt, // 25.4 mm in an inch
156 	pxPerPt,
157 	12 * pxPerPt };      // 12 points per pica
158 
159   if (auto_)
160     return 0;
161   else
162     if (unit_ == LengthUnit::FontEm)
163       return value_ * fontSize;
164     else if (unit_ == LengthUnit::FontEx)
165       return value_ * fontSize / 2.0; // approximate: ex/em is 0.46 to 0.56...
166     else if (unit_ == LengthUnit::Percentage ||
167              unit_ == LengthUnit::ViewportWidth ||
168              unit_ == LengthUnit::ViewportHeight ||
169              unit_ == LengthUnit::ViewportMin ||
170              unit_ == LengthUnit::ViewportMax)
171       return value_ * fontSize / 100.0; // assuming relative to font size...
172     else
173       return value_ * unitFactor[static_cast<unsigned int>(unit_) - 2];
174 }
175 
176 }
177