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