/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.4M | { |
32 | 49.4M | if (!input || input->isEnd()) |
33 | 21.9k | throw EndOfStreamException(); |
34 | 49.4M | } |
35 | | |
36 | | struct SeekFailedException {}; |
37 | | |
38 | | static void _appendUCS4(librevenge::RVNGString &text, unsigned ucs4Character) |
39 | 779k | { |
40 | 779k | unsigned char first; |
41 | 779k | int len; |
42 | 779k | if (ucs4Character < 0x80) |
43 | 594k | { |
44 | 594k | first = 0; |
45 | 594k | len = 1; |
46 | 594k | } |
47 | 184k | else if (ucs4Character < 0x800) |
48 | 175k | { |
49 | 175k | first = 0xc0; |
50 | 175k | len = 2; |
51 | 175k | } |
52 | 9.26k | else if (ucs4Character < 0x10000) |
53 | 9.26k | { |
54 | 9.26k | first = 0xe0; |
55 | 9.26k | len = 3; |
56 | 9.26k | } |
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 | 779k | unsigned char outbuf[6] = { 0, 0, 0, 0, 0, 0 }; |
74 | 779k | int i; |
75 | 972k | for (i = len - 1; i > 0; --i) |
76 | 193k | { |
77 | 193k | outbuf[i] = (ucs4Character & 0x3f) | 0x80; |
78 | 193k | ucs4Character >>= 6; |
79 | 193k | } |
80 | 779k | outbuf[0] = (ucs4Character & 0xff) | first; |
81 | | |
82 | 1.75M | for (i = 0; i < len; i++) |
83 | 972k | text.append(char(outbuf[i])); |
84 | 779k | } |
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 | 19.7M | { |
101 | 19.7M | checkStream(input); |
102 | | |
103 | 19.7M | unsigned long numBytesRead; |
104 | 19.7M | uint8_t const *p = input->read(sizeof(uint8_t), numBytesRead); |
105 | | |
106 | 19.7M | if (p && numBytesRead == sizeof(uint8_t)) |
107 | 19.7M | return *(uint8_t const *)(p); |
108 | 974 | throw EndOfStreamException(); |
109 | 19.7M | } |
110 | | |
111 | | uint16_t readU16(librevenge::RVNGInputStream *input, bool bigEndian) |
112 | 9.47M | { |
113 | 9.47M | checkStream(input); |
114 | | |
115 | 9.47M | unsigned long numBytesRead; |
116 | 9.47M | uint8_t const *p = input->read(sizeof(uint16_t), numBytesRead); |
117 | | |
118 | 9.47M | if (p && numBytesRead == sizeof(uint16_t)) |
119 | 9.47M | { |
120 | 9.47M | if (bigEndian) |
121 | 8.05M | return static_cast<uint16_t>((uint16_t)p[1]|((uint16_t)p[0]<<8)); |
122 | 1.42M | return static_cast<uint16_t>((uint16_t)p[0]|((uint16_t)p[1]<<8)); |
123 | 9.47M | } |
124 | 1.40k | throw EndOfStreamException(); |
125 | 9.47M | } |
126 | | |
127 | | uint32_t readU32(librevenge::RVNGInputStream *input, bool bigEndian) |
128 | 13.5M | { |
129 | 13.5M | checkStream(input); |
130 | | |
131 | 13.5M | unsigned long numBytesRead; |
132 | 13.5M | uint8_t const *p = input->read(sizeof(uint32_t), numBytesRead); |
133 | | |
134 | 13.5M | if (p && numBytesRead == sizeof(uint32_t)) |
135 | 13.5M | { |
136 | 13.5M | if (bigEndian) |
137 | 10.6M | return (uint32_t)p[3]|((uint32_t)p[2]<<8)|((uint32_t)p[1]<<16)|((uint32_t)p[0]<<24); |
138 | 2.90M | return (uint32_t)p[0]|((uint32_t)p[1]<<8)|((uint32_t)p[2]<<16)|((uint32_t)p[3]<<24); |
139 | 13.5M | } |
140 | 3.49k | throw EndOfStreamException(); |
141 | 13.5M | } |
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.26M | { |
161 | 1.26M | return int16_t(readU16(input, bigEndian)); |
162 | 1.26M | } |
163 | | |
164 | | int32_t readS32(librevenge::RVNGInputStream *input, bool bigEndian) |
165 | 4.84M | { |
166 | 4.84M | return int32_t(readU32(input, bigEndian)); |
167 | 4.84M | } |
168 | | |
169 | | double readFloat16(librevenge::RVNGInputStream *input, bool bigEndian) |
170 | 36.5k | { |
171 | 36.5k | return readU16(input, bigEndian) / double(0x10000); |
172 | 36.5k | } |
173 | | |
174 | | double readFraction(librevenge::RVNGInputStream *input, bool bigEndian) |
175 | 4.79M | { |
176 | 4.79M | int32_t num = readS32(input, bigEndian); |
177 | 4.79M | return (num >> 16) + ((num & 0xffff) / double(0x10000)); |
178 | 4.79M | } |
179 | | |
180 | | const unsigned char *readNBytes(librevenge::RVNGInputStream *const input, const unsigned long numBytes) |
181 | 2.43k | { |
182 | 2.43k | checkStream(input); |
183 | | |
184 | 2.43k | unsigned long readBytes = 0; |
185 | 2.43k | const unsigned char *const s = input->read(numBytes, readBytes); |
186 | | |
187 | 2.43k | if (numBytes != readBytes) |
188 | 0 | throw EndOfStreamException(); |
189 | | |
190 | 2.43k | return s; |
191 | 2.43k | } |
192 | | |
193 | | string readCString(librevenge::RVNGInputStream *input) |
194 | 1.75M | { |
195 | 1.75M | checkStream(input); |
196 | | |
197 | 1.75M | string str; |
198 | 1.75M | unsigned char c = readU8(input); |
199 | 3.90M | while (0 != c) |
200 | 2.14M | { |
201 | 2.14M | str.push_back(c); |
202 | 2.14M | c = readU8(input); |
203 | 2.14M | } |
204 | | |
205 | 1.75M | return str; |
206 | 1.75M | } |
207 | | |
208 | | string readPascalString(librevenge::RVNGInputStream *input) |
209 | 766k | { |
210 | 766k | checkStream(input); |
211 | | |
212 | 766k | const unsigned length = readU8(input); |
213 | | |
214 | 766k | return readString(input, length); |
215 | 766k | } |
216 | | |
217 | | std::string readString(librevenge::RVNGInputStream *input, const unsigned length) |
218 | 791k | { |
219 | 791k | checkStream(input); |
220 | | |
221 | 791k | string str; |
222 | 791k | str.reserve(length); |
223 | 10.8M | for (unsigned i = 0; length != i; ++i) |
224 | 10.0M | str.push_back(readU8(input)); |
225 | | |
226 | 791k | return str; |
227 | 791k | } |
228 | | |
229 | | std::string readPlatformString(librevenge::RVNGInputStream *input, bool bigEndian) |
230 | 2.52M | { |
231 | 2.52M | return bigEndian ? readPascalString(input) : readCString(input); |
232 | 2.52M | } |
233 | | |
234 | | void skip(librevenge::RVNGInputStream *input, unsigned long numBytes) |
235 | 3.37M | { |
236 | 3.37M | checkStream(input); |
237 | | |
238 | 3.37M | seekRelative(input, static_cast<long>(numBytes)); |
239 | 3.37M | } |
240 | | |
241 | | void seek(librevenge::RVNGInputStream *const input, const unsigned long pos) |
242 | 544k | { |
243 | 544k | if (!input) |
244 | 0 | throw EndOfStreamException(); |
245 | | |
246 | 544k | if (0 != input->seek(static_cast<long>(pos), librevenge::RVNG_SEEK_SET)) |
247 | 449 | throw SeekFailedException(); |
248 | 544k | } |
249 | | |
250 | | void seekRelative(librevenge::RVNGInputStream *const input, const long pos) |
251 | 3.35M | { |
252 | 3.35M | if (!input) |
253 | 0 | throw EndOfStreamException(); |
254 | | |
255 | 3.35M | if (0 != input->seek(pos, librevenge::RVNG_SEEK_CUR)) |
256 | 3.65k | throw SeekFailedException(); |
257 | 3.35M | } |
258 | | |
259 | | unsigned long getRemainingLength(librevenge::RVNGInputStream *const input) |
260 | 314k | { |
261 | 314k | if (!input || input->tell() < 0) |
262 | 1.52k | throw SeekFailedException(); |
263 | | |
264 | 313k | const unsigned long begin = input->tell(); |
265 | 313k | unsigned long end = begin; |
266 | | |
267 | 313k | if (0 == input->seek(0, librevenge::RVNG_SEEK_END)) |
268 | 313k | 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 | 313k | seek(input, begin); |
280 | | |
281 | 313k | return end - begin; |
282 | 314k | } |
283 | | |
284 | | uint8_t readU8(const std::shared_ptr<librevenge::RVNGInputStream> input, bool) |
285 | 5.03M | { |
286 | 5.03M | return readU8(input.get()); |
287 | 5.03M | } |
288 | | |
289 | | uint16_t readU16(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian) |
290 | 8.17M | { |
291 | 8.17M | return readU16(input.get(), bigEndian); |
292 | 8.17M | } |
293 | | |
294 | | uint32_t readU32(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian) |
295 | 8.74M | { |
296 | 8.74M | return readU32(input.get(), bigEndian); |
297 | 8.74M | } |
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.26M | { |
306 | 1.26M | return readS16(input.get(), bigEndian); |
307 | 1.26M | } |
308 | | |
309 | | int32_t readS32(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
310 | 50.9k | { |
311 | 50.9k | return readS32(input.get(), bigEndian); |
312 | 50.9k | } |
313 | | |
314 | | double readFloat16(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
315 | 36.5k | { |
316 | 36.5k | return readFloat16(input.get(), bigEndian); |
317 | 36.5k | } |
318 | | |
319 | | double readFraction(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
320 | 4.79M | { |
321 | 4.79M | return readFraction(input.get(), bigEndian); |
322 | 4.79M | } |
323 | | |
324 | | const unsigned char *readNBytes(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long numBytes) |
325 | 2.43k | { |
326 | 2.43k | return readNBytes(input.get(), numBytes); |
327 | 2.43k | } |
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.5k | { |
341 | 24.5k | return readString(input.get(), length); |
342 | 24.5k | } |
343 | | |
344 | | std::string readPlatformString(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian) |
345 | 2.52M | { |
346 | 2.52M | return readPlatformString(input.get(), bigEndian); |
347 | 2.52M | } |
348 | | |
349 | | void skip(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long numBytes) |
350 | 3.37M | { |
351 | 3.37M | return skip(input.get(), numBytes); |
352 | 3.37M | } |
353 | | |
354 | | void seek(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long pos) |
355 | 202k | { |
356 | 202k | seek(input.get(), pos); |
357 | 202k | } |
358 | | |
359 | | void seekRelative(const std::shared_ptr<librevenge::RVNGInputStream> input, const long pos) |
360 | 6.97k | { |
361 | 6.97k | seekRelative(input.get(), pos); |
362 | 6.97k | } |
363 | | |
364 | | unsigned long getRemainingLength(const std::shared_ptr<librevenge::RVNGInputStream> &input) |
365 | 300k | { |
366 | 300k | return getRemainingLength(input.get()); |
367 | 300k | } |
368 | | |
369 | | EndOfStreamException::EndOfStreamException() |
370 | 24.2k | { |
371 | 24.2k | QXP_DEBUG_MSG(("Throwing EndOfStreamException\n")); |
372 | 24.2k | } |
373 | | |
374 | | double deg2rad(double value) |
375 | 455k | { |
376 | 455k | using namespace boost::math::double_constants; |
377 | | |
378 | 455k | return normalizeDegAngle(value) * degree; |
379 | 455k | } |
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 | 476k | { |
392 | 476k | degAngle = std::fmod(degAngle, 360.0); |
393 | 476k | if (degAngle < 0.0) |
394 | 392k | degAngle += 360.0; |
395 | 476k | return degAngle; |
396 | 476k | } |
397 | | |
398 | | void appendCharacters(librevenge::RVNGString &text, const char *characters, const size_t size, |
399 | | const char *encoding) |
400 | 27.3k | { |
401 | 27.3k | if (size == 0) |
402 | 15.6k | { |
403 | 15.6k | QXP_DEBUG_MSG(("Attempt to append 0 characters!")); |
404 | 15.6k | return; |
405 | 15.6k | } |
406 | | |
407 | 11.6k | UErrorCode status = U_ZERO_ERROR; |
408 | 11.6k | std::unique_ptr<UConverter, void(*)(UConverter *)> conv(ucnv_open(encoding, &status), ucnv_close); |
409 | 11.6k | if (U_SUCCESS(status)) |
410 | 11.6k | { |
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.6k | const char *src = &characters[0]; |
414 | 11.6k | const char *srcLimit = (const char *)src + size; |
415 | 790k | while (src < srcLimit) |
416 | 779k | { |
417 | 779k | uint32_t ucs4Character = (uint32_t)ucnv_getNextUChar(conv.get(), &src, srcLimit, &status); |
418 | 779k | if (U_SUCCESS(status)) |
419 | 779k | { |
420 | 779k | _appendUCS4(text, ucs4Character); |
421 | 779k | } |
422 | 779k | } |
423 | 11.6k | } |
424 | 11.6k | } |
425 | | |
426 | | } |
427 | | |
428 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |