1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 */
7
8 #include "orcus/measurement.hpp"
9 #include "orcus/pstring.hpp"
10 #include "orcus/exception.hpp"
11 #include "orcus/parser_global.hpp"
12
13 #include <mdds/sorted_string_map.hpp>
14 #include <mdds/global.hpp>
15 #include <orcus/global.hpp>
16
17 #include <sstream>
18
19 using namespace std;
20
21 namespace orcus {
22
to_double(const char * p,const char * p_end,const char ** p_parse_ended)23 double to_double(const char* p, const char* p_end, const char** p_parse_ended)
24 {
25 double val = parse_numeric(p, p_end-p);
26 if (p_parse_ended)
27 *p_parse_ended = p;
28
29 return val;
30 }
31
to_double(const pstring & s)32 double to_double(const pstring& s)
33 {
34 const char* p = s.get();
35 const char* p_end = p + s.size();
36 return to_double(p, p_end, nullptr);
37 }
38
to_long(const char * p,const char * p_end,const char ** p_parse_ended)39 long to_long(const char* p, const char* p_end, const char** p_parse_ended)
40 {
41 size_t n = p_end - p;
42 long val = parse_integer(p, n);
43 if (p_parse_ended)
44 *p_parse_ended = p;
45
46 return val;
47 }
48
to_long(const pstring & s)49 long to_long(const pstring& s)
50 {
51 const char* p = s.get();
52 const char* p_end = p + s.size();
53 return to_long(p, p_end, nullptr);
54 }
55
to_bool(const pstring & s)56 bool to_bool(const pstring& s)
57 {
58 size_t n = s.size();
59 if (n == 1)
60 // Any single char other than '0' is true.
61 return *s.get() != '0';
62
63 if (n == 4)
64 {
65 // Check against 'true'.
66 const char* p = s.get();
67 if (*p++ != 't' || *p++ != 'r' || *p++ != 'u' || *p != 'e')
68 return false;
69
70 return true;
71 }
72
73 return false;
74 }
75
76 namespace {
77
78 typedef mdds::sorted_string_map<length_unit_t> length_map;
79
80 length_map::entry length_map_entries[] =
81 {
82 {MDDS_ASCII("cm"), length_unit_t::centimeter},
83 {MDDS_ASCII("in"), length_unit_t::inch},
84 {MDDS_ASCII("mm"), length_unit_t::millimeter},
85 {MDDS_ASCII("pt"), length_unit_t::point},
86 {MDDS_ASCII("px"), length_unit_t::pixel}
87 };
88
89 }
90
to_length(const pstring & str)91 length_t to_length(const pstring& str)
92 {
93 length_t ret;
94 if (str.empty())
95 return ret;
96
97 const char* p = str.get();
98 const char* p_start = p;
99 const char* p_end = p_start + str.size();
100 ret.value = parse_numeric(p, p_end-p);
101
102 static const length_map units(length_map_entries, ORCUS_N_ELEMENTS(length_map_entries), length_unit_t::unknown);
103 pstring tail(p, p_end-p);
104 ret.unit = units.find(tail.get(), tail.size());
105
106 return ret;
107 }
108
109 namespace {
110
convert_inch(double value,length_unit_t unit_to)111 double convert_inch(double value, length_unit_t unit_to)
112 {
113 switch (unit_to)
114 {
115 case length_unit_t::twip:
116 // inches to twips : 1 twip = 1/1440 inches
117 return value * 1440.0;
118 default:
119 ;
120 }
121
122 throw general_error("convert_inch: unsupported unit of measurement.");
123 }
124
convert_point(double value,length_unit_t unit_to)125 double convert_point(double value, length_unit_t unit_to)
126 {
127 switch (unit_to)
128 {
129 case length_unit_t::twip:
130 // 20 twips = 1 point
131 return value * 20.0;
132 default:
133 ;
134 }
135
136 throw general_error("convert_point: unsupported unit of measurement.");
137 }
138
convert_centimeter(double value,length_unit_t unit_to)139 double convert_centimeter(double value, length_unit_t unit_to)
140 {
141 switch (unit_to)
142 {
143 case length_unit_t::twip:
144 // centimeters to twips : 2.54 cm = 1 inch = 1440 twips
145 return value / 2.54 * 1440.0;
146 default:
147 ;
148 }
149
150 throw general_error("convert_centimeter: unsupported unit of measurement.");
151 }
152
convert_millimeter(double value,length_unit_t unit_to)153 double convert_millimeter(double value, length_unit_t unit_to)
154 {
155 switch (unit_to)
156 {
157 case length_unit_t::twip:
158 // millimeters to twips : 25.4 mm = 1 inch = 1440 twips
159 return value / 25.4 * 1440.0;
160 default:
161 ;
162 }
163
164 throw general_error("convert_millimeter: unsupported unit of measurement.");
165 }
166
convert_twip(double value,length_unit_t unit_to)167 double convert_twip(double value, length_unit_t unit_to)
168 {
169 switch (unit_to)
170 {
171 case length_unit_t::inch:
172 // twips to inches : 1 twip = 1/1440 inches
173 return value / 1440.0;
174 case length_unit_t::point:
175 // 1 twip = 1/1440 inches = 72/1440 points = 1/20 points
176 return value / 20.0;
177 default:
178 ;
179 }
180 throw general_error("convert_twip: unsupported unit of measurement.");
181 }
182
183 /**
184 * Since Excel's column width is based on the maximum digit width of font
185 * used as the "Normal" style font, it's impossible to convert it accurately
186 * without the font information.
187 */
convert_xlsx_column_digit(double value,length_unit_t unit_to)188 double convert_xlsx_column_digit(double value, length_unit_t unit_to)
189 {
190 // Convert to centimeters first. Here, we'll just assume that a single
191 // digit always equals 1.9 millimeters. TODO: find a better way to convert
192 // this.
193 value *= 0.19;
194 return convert_centimeter(value, unit_to);
195 }
196
197 }
198
convert(double value,length_unit_t unit_from,length_unit_t unit_to)199 double convert(double value, length_unit_t unit_from, length_unit_t unit_to)
200 {
201 if (value == 0.0)
202 return value;
203
204 switch (unit_from)
205 {
206 case length_unit_t::point:
207 return convert_point(value, unit_to);
208 case length_unit_t::inch:
209 return convert_inch(value, unit_to);
210 case length_unit_t::centimeter:
211 return convert_centimeter(value, unit_to);
212 case length_unit_t::millimeter:
213 return convert_millimeter(value, unit_to);
214 case length_unit_t::twip:
215 return convert_twip(value, unit_to);
216 case length_unit_t::xlsx_column_digit:
217 return convert_xlsx_column_digit(value, unit_to);
218 default:
219 ;
220 }
221
222 std::ostringstream os;
223 os << "convert: unsupported unit of measurement (from "
224 << static_cast<int>(unit_from) << " to "
225 << static_cast<int>(unit_to) << ") (value=" << value << ")";
226 throw general_error(os.str());
227 }
228
229 }
230 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
231