/src/libqxp/src/lib/libqxp_utils.cpp
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* |
3 | | * This file is part of the libqxp project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | */ |
9 | | |
10 | | #include "libqxp_utils.h" |
11 | | |
12 | | #include <unicode/ucnv.h> |
13 | | #include <unicode/utypes.h> |
14 | | |
15 | | #ifdef DEBUG |
16 | | #include <cstdarg> |
17 | | #include <cstdio> |
18 | | #endif |
19 | | |
20 | | #include <boost/math/constants/constants.hpp> |
21 | | |
22 | | using std::string; |
23 | | |
24 | | namespace libqxp |
25 | | { |
26 | | |
27 | | namespace |
28 | | { |
29 | | |
30 | | void checkStream(librevenge::RVNGInputStream *const input) |
31 | 49.6M | { |
32 | 49.6M | if (!input || input->isEnd()) |
33 | 25.0k | throw EndOfStreamException(); |
34 | 49.6M | } |
35 | | |
36 | | struct SeekFailedException {}; |
37 | | |
38 | | static void _appendUCS4(librevenge::RVNGString &text, unsigned ucs4Character) |
39 | 762k | { |
40 | 762k | unsigned char first; |
41 | 762k | int len; |
42 | 762k | if (ucs4Character < 0x80) |
43 | 581k | { |
44 | 581k | first = 0; |
45 | 581k | len = 1; |
46 | 581k | } |
47 | 180k | else if (ucs4Character < 0x800) |
48 | 172k | { |
49 | 172k | first = 0xc0; |
50 | 172k | len = 2; |
51 | 172k | } |
52 | 8.89k | else if (ucs4Character < 0x10000) |
53 | 8.89k | { |
54 | 8.89k | first = 0xe0; |
55 | 8.89k | len = 3; |
56 | 8.89k | } |
57 | 0 | else if (ucs4Character < 0x200000) |
58 | 0 | { |
59 | 0 | first = 0xf0; |
60 | 0 | len = 4; |
61 | 0 | } |
62 | 0 | else if (ucs4Character < 0x4000000) |
63 | 0 | { |
64 | 0 | first = 0xf8; |
65 | 0 | len = 5; |
66 | 0 | } |
67 | 0 | else |
68 | 0 | { |
69 | 0 | first = 0xfc; |
70 | 0 | len = 6; |
71 | 0 | } |
72 | | |
73 | 762k | unsigned char outbuf[6] = { 0, 0, 0, 0, 0, 0 }; |
74 | 762k | int i; |
75 | 952k | for (i = len - 1; i > 0; --i) |
76 | 189k | { |
77 | 189k | outbuf[i] = (ucs4Character & 0x3f) | 0x80; |
78 | 189k | ucs4Character >>= 6; |
79 | 189k | } |
80 | 762k | outbuf[0] = (ucs4Character & 0xff) | first; |
81 | | |
82 | 1.71M | for (i = 0; i < len; i++) |
83 | 952k | text.append(char(outbuf[i])); |
84 | 762k | } |
85 | | |
86 | | |
87 | | } |
88 | | |
89 | | #ifdef DEBUG |
90 | | void debugPrint(const char *format, ...) |
91 | | { |
92 | | va_list args; |
93 | | va_start(args, format); |
94 | | std::vfprintf(stderr, format, args); |
95 | | va_end(args); |
96 | | } |
97 | | #endif |
98 | | |
99 | | uint8_t readU8(librevenge::RVNGInputStream *input, bool /* bigEndian */) |
100 | 20.3M | { |
101 | 20.3M | checkStream(input); |
102 | | |
103 | 20.3M | unsigned long numBytesRead; |
104 | 20.3M | uint8_t const *p = input->read(sizeof(uint8_t), numBytesRead); |
105 | | |
106 | 20.3M | if (p && numBytesRead == sizeof(uint8_t)) |
107 | 20.3M | return *(uint8_t const *)(p); |
108 | 972 | throw EndOfStreamException(); |
109 | 20.3M | } |
110 | | |
111 | | uint16_t readU16(librevenge::RVNGInputStream *input, bool bigEndian) |
112 | 9.24M | { |
113 | 9.24M | checkStream(input); |
114 | | |
115 | 9.24M | unsigned long numBytesRead; |
116 | 9.24M | uint8_t const *p = input->read(sizeof(uint16_t), numBytesRead); |
117 | | |
118 | 9.24M | if (p && numBytesRead == sizeof(uint16_t)) |
119 | 9.24M | { |
120 | 9.24M | if (bigEndian) |
121 | 7.74M | return static_cast<uint16_t>((uint16_t)p[1]|((uint16_t)p[0]<<8)); |
122 | 1.49M | return static_cast<uint16_t>((uint16_t)p[0]|((uint16_t)p[1]<<8)); |
123 | 9.24M | } |
124 | 1.42k | throw EndOfStreamException(); |
125 | 9.24M | } |
126 | | |
127 | | uint32_t readU32(librevenge::RVNGInputStream *input, bool bigEndian) |
128 | 13.1M | { |
129 | 13.1M | checkStream(input); |
130 | | |
131 | 13.1M | unsigned long numBytesRead; |
132 | 13.1M | uint8_t const *p = input->read(sizeof(uint32_t), numBytesRead); |
133 | | |
134 | 13.1M | if (p && numBytesRead == sizeof(uint32_t)) |
135 | 13.1M | { |
136 | 13.1M | if (bigEndian) |
137 | 10.5M | return (uint32_t)p[3]|((uint32_t)p[2]<<8)|((uint32_t)p[1]<<16)|((uint32_t)p[0]<<24); |
138 | 2.61M | return (uint32_t)p[0]|((uint32_t)p[1]<<8)|((uint32_t)p[2]<<16)|((uint32_t)p[3]<<24); |
139 | 13.1M | } |
140 | 3.73k | throw EndOfStreamException(); |
141 | 13.1M | } |
142 | | |
143 | | uint64_t readU64(librevenge::RVNGInputStream *input, bool bigEndian) |
144 | 0 | { |
145 | 0 | checkStream(input); |
146 | |
|
147 | 0 | unsigned long numBytesRead; |
148 | 0 | uint8_t const *p = input->read(sizeof(uint64_t), numBytesRead); |
149 | |
|
150 | 0 | if (p && numBytesRead == sizeof(uint64_t)) |
151 | 0 | { |
152 | 0 | if (bigEndian) |
153 | 0 | return (uint64_t)p[7]|((uint64_t)p[6]<<8)|((uint64_t)p[5]<<16)|((uint64_t)p[4]<<24)|((uint64_t)p[3]<<32)|((uint64_t)p[2]<<40)|((uint64_t)p[1]<<48)|((uint64_t)p[0]<<56); |
154 | 0 | return (uint64_t)p[0]|((uint64_t)p[1]<<8)|((uint64_t)p[2]<<16)|((uint64_t)p[3]<<24)|((uint64_t)p[4]<<32)|((uint64_t)p[5]<<40)|((uint64_t)p[6]<<48)|((uint64_t)p[7]<<56); |
155 | 0 | } |
156 | 0 | throw EndOfStreamException(); |
157 | 0 | } |
158 | | |
159 | | int16_t readS16(librevenge::RVNGInputStream *input, bool bigEndian) |
160 | 1.34M | { |
161 | 1.34M | return int16_t(readU16(input, bigEndian)); |
162 | 1.34M | } |
163 | | |
164 | | int32_t readS32(librevenge::RVNGInputStream *input, bool bigEndian) |
165 | 5.06M | { |
166 | 5.06M | return int32_t(readU32(input, bigEndian)); |
167 | 5.06M | } |
168 | | |
169 | | double readFloat16(librevenge::RVNGInputStream *input, bool bigEndian) |
170 | 39.9k | { |
171 | 39.9k | return readU16(input, bigEndian) / double(0x10000); |
172 | 39.9k | } |
173 | | |
174 | | double readFraction(librevenge::RVNGInputStream *input, bool bigEndian) |
175 | 5.01M | { |
176 | 5.01M | int32_t num = readS32(input, bigEndian); |
177 | 5.01M | return (num >> 16) + ((num & 0xffff) / double(0x10000)); |
178 | 5.01M | } |
179 | | |
180 | | const unsigned char *readNBytes(librevenge::RVNGInputStream *const input, const unsigned long numBytes) |
181 | 2.51k | { |
182 | 2.51k | checkStream(input); |
183 | | |
184 | 2.51k | unsigned long readBytes = 0; |
185 | 2.51k | const unsigned char *const s = input->read(numBytes, readBytes); |
186 | | |
187 | 2.51k | if (numBytes != readBytes) |
188 | 0 | throw EndOfStreamException(); |
189 | | |
190 | 2.51k | return s; |
191 | 2.51k | } |
192 | | |
193 | | string readCString(librevenge::RVNGInputStream *input) |
194 | 1.86M | { |
195 | 1.86M | checkStream(input); |
196 | | |
197 | 1.86M | string str; |
198 | 1.86M | unsigned char c = readU8(input); |
199 | 4.06M | while (0 != c) |
200 | 2.19M | { |
201 | 2.19M | str.push_back(c); |
202 | 2.19M | c = readU8(input); |
203 | 2.19M | } |
204 | | |
205 | 1.86M | return str; |
206 | 1.86M | } |
207 | | |
208 | | string readPascalString(librevenge::RVNGInputStream *input) |
209 | 789k | { |
210 | 789k | checkStream(input); |
211 | | |
212 | 789k | const unsigned length = readU8(input); |
213 | | |
214 | 789k | return readString(input, length); |
215 | 789k | } |
216 | | |
217 | | std::string readString(librevenge::RVNGInputStream *input, const unsigned length) |
218 | 814k | { |
219 | 814k | checkStream(input); |
220 | | |
221 | 814k | string str; |
222 | 814k | str.reserve(length); |
223 | 11.0M | for (unsigned i = 0; length != i; ++i) |
224 | 10.2M | str.push_back(readU8(input)); |
225 | | |
226 | 814k | return str; |
227 | 814k | } |
228 | | |
229 | | std::string readPlatformString(librevenge::RVNGInputStream *input, bool bigEndian) |
230 | 2.65M | { |
231 | 2.65M | return bigEndian ? readPascalString(input) : readCString(input); |
232 | 2.65M | } |
233 | | |
234 | | void skip(librevenge::RVNGInputStream *input, unsigned long numBytes) |
235 | 3.44M | { |
236 | 3.44M | checkStream(input); |
237 | | |
238 | 3.44M | seekRelative(input, static_cast<long>(numBytes)); |
239 | 3.44M | } |
240 | | |
241 | | void seek(librevenge::RVNGInputStream *const input, const unsigned long pos) |
242 | 551k | { |
243 | 551k | if (!input) |
244 | 0 | throw EndOfStreamException(); |
245 | | |
246 | 551k | if (0 != input->seek(static_cast<long>(pos), librevenge::RVNG_SEEK_SET)) |
247 | 466 | throw SeekFailedException(); |
248 | 551k | } |
249 | | |
250 | | void seekRelative(librevenge::RVNGInputStream *const input, const long pos) |
251 | 3.42M | { |
252 | 3.42M | if (!input) |
253 | 0 | throw EndOfStreamException(); |
254 | | |
255 | 3.42M | if (0 != input->seek(pos, librevenge::RVNG_SEEK_CUR)) |
256 | 3.71k | throw SeekFailedException(); |
257 | 3.42M | } |
258 | | |
259 | | unsigned long getRemainingLength(librevenge::RVNGInputStream *const input) |
260 | 317k | { |
261 | 317k | if (!input || input->tell() < 0) |
262 | 1.54k | throw SeekFailedException(); |
263 | | |
264 | 316k | const unsigned long begin = input->tell(); |
265 | 316k | unsigned long end = begin; |
266 | | |
267 | 316k | if (0 == input->seek(0, librevenge::RVNG_SEEK_END)) |
268 | 316k | end = input->tell(); |
269 | 0 | else |
270 | 0 | { |
271 | | // librevenge::RVNG_SEEK_END does not work. Use the harder way. |
272 | 0 | while (!input->isEnd()) |
273 | 0 | { |
274 | 0 | readU8(input); |
275 | 0 | ++end; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | 316k | seek(input, begin); |
280 | | |
281 | 316k | return end - begin; |
282 | 317k | } |
283 | | |
284 | | uint8_t readU8(const std::shared_ptr<librevenge::RVNGInputStream> input, bool) |
285 | 5.28M | { |
286 | 5.28M | return readU8(input.get()); |
287 | 5.28M | } |
288 | | |
289 | | uint16_t readU16(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian) |
290 | 7.86M | { |
291 | 7.86M | return readU16(input.get(), bigEndian); |
292 | 7.86M | } |
293 | | |
294 | | uint32_t readU32(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian) |
295 | 8.09M | { |
296 | 8.09M | return readU32(input.get(), bigEndian); |
297 | 8.09M | } |
298 | | |
299 | | uint64_t readU64(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian) |
300 | 0 | { |
301 | 0 | return readU64(input.get(), bigEndian); |
302 | 0 | } |
303 | | |
304 | | int16_t readS16(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
305 | 1.34M | { |
306 | 1.34M | return readS16(input.get(), bigEndian); |
307 | 1.34M | } |
308 | | |
309 | | int32_t readS32(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
310 | 52.7k | { |
311 | 52.7k | return readS32(input.get(), bigEndian); |
312 | 52.7k | } |
313 | | |
314 | | double readFloat16(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
315 | 39.9k | { |
316 | 39.9k | return readFloat16(input.get(), bigEndian); |
317 | 39.9k | } |
318 | | |
319 | | double readFraction(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
320 | 5.01M | { |
321 | 5.01M | return readFraction(input.get(), bigEndian); |
322 | 5.01M | } |
323 | | |
324 | | const unsigned char *readNBytes(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long numBytes) |
325 | 2.51k | { |
326 | 2.51k | return readNBytes(input.get(), numBytes); |
327 | 2.51k | } |
328 | | |
329 | | std::string readCString(const std::shared_ptr<librevenge::RVNGInputStream> input) |
330 | 0 | { |
331 | 0 | return readCString(input.get()); |
332 | 0 | } |
333 | | |
334 | | std::string readPascalString(const std::shared_ptr<librevenge::RVNGInputStream> input) |
335 | 0 | { |
336 | 0 | return readPascalString(input.get()); |
337 | 0 | } |
338 | | |
339 | | std::string readString(std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned length) |
340 | 24.9k | { |
341 | 24.9k | return readString(input.get(), length); |
342 | 24.9k | } |
343 | | |
344 | | std::string readPlatformString(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
345 | 2.65M | { |
346 | 2.65M | return readPlatformString(input.get(), bigEndian); |
347 | 2.65M | } |
348 | | |
349 | | void skip(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long numBytes) |
350 | 3.44M | { |
351 | 3.44M | return skip(input.get(), numBytes); |
352 | 3.44M | } |
353 | | |
354 | | void seek(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long pos) |
355 | 206k | { |
356 | 206k | seek(input.get(), pos); |
357 | 206k | } |
358 | | |
359 | | void seekRelative(const std::shared_ptr<librevenge::RVNGInputStream> input, const long pos) |
360 | 7.15k | { |
361 | 7.15k | seekRelative(input.get(), pos); |
362 | 7.15k | } |
363 | | |
364 | | unsigned long getRemainingLength(const std::shared_ptr<librevenge::RVNGInputStream> &input) |
365 | 303k | { |
366 | 303k | return getRemainingLength(input.get()); |
367 | 303k | } |
368 | | |
369 | | EndOfStreamException::EndOfStreamException() |
370 | 27.3k | { |
371 | 27.3k | QXP_DEBUG_MSG(("Throwing EndOfStreamException\n")); |
372 | 27.3k | } |
373 | | |
374 | | double deg2rad(double value) |
375 | 445k | { |
376 | 445k | using namespace boost::math::double_constants; |
377 | | |
378 | 445k | return normalizeDegAngle(value) * degree; |
379 | 445k | } |
380 | | |
381 | | double normalizeRadAngle(double radAngle) |
382 | 0 | { |
383 | 0 | using namespace boost::math::double_constants; |
384 | 0 | radAngle = std::fmod(radAngle, two_pi); |
385 | 0 | if (radAngle < 0) |
386 | 0 | radAngle += two_pi; |
387 | 0 | return radAngle; |
388 | 0 | } |
389 | | |
390 | | double normalizeDegAngle(double degAngle) |
391 | 467k | { |
392 | 467k | degAngle = std::fmod(degAngle, 360.0); |
393 | 467k | if (degAngle < 0.0) |
394 | 393k | degAngle += 360.0; |
395 | 467k | return degAngle; |
396 | 467k | } |
397 | | |
398 | | void appendCharacters(librevenge::RVNGString &text, const char *characters, const size_t size, |
399 | | const char *encoding) |
400 | 26.3k | { |
401 | 26.3k | if (size == 0) |
402 | 15.1k | { |
403 | 15.1k | QXP_DEBUG_MSG(("Attempt to append 0 characters!")); |
404 | 15.1k | return; |
405 | 15.1k | } |
406 | | |
407 | 11.1k | UErrorCode status = U_ZERO_ERROR; |
408 | 11.1k | std::unique_ptr<UConverter, void(*)(UConverter *)> conv(ucnv_open(encoding, &status), ucnv_close); |
409 | 11.1k | if (U_SUCCESS(status)) |
410 | 11.1k | { |
411 | | // ICU documentation claims that character-by-character processing is faster "for small amounts of data" and "'normal' charsets" |
412 | | // (in any case, it is more convenient :) ) |
413 | 11.1k | const char *src = &characters[0]; |
414 | 11.1k | const char *srcLimit = (const char *)src + size; |
415 | 773k | while (src < srcLimit) |
416 | 762k | { |
417 | 762k | uint32_t ucs4Character = (uint32_t)ucnv_getNextUChar(conv.get(), &src, srcLimit, &status); |
418 | 762k | if (U_SUCCESS(status)) |
419 | 762k | { |
420 | 762k | _appendUCS4(text, ucs4Character); |
421 | 762k | } |
422 | 762k | } |
423 | 11.1k | } |
424 | 11.1k | } |
425 | | |
426 | | } |
427 | | |
428 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |