/src/libwps/src/lib/libwps_internal.cpp
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ |
2 | | /* libwps |
3 | | * Version: MPL 2.0 / LGPLv2.1+ |
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 | | * Major Contributor(s): |
10 | | * Copyright (C) 2002, 2005 William Lachance (william.lachance@sympatico.ca) |
11 | | * Copyright (C) 2002, 2004 Marc Maurer (uwog@uwog.net) |
12 | | * |
13 | | * For minor contributions see the git repository. |
14 | | * |
15 | | * Alternatively, the contents of this file may be used under the terms |
16 | | * of the GNU Lesser General Public License Version 2.1 or later |
17 | | * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are |
18 | | * applicable instead of those above. |
19 | | * |
20 | | * For further information visit http://libwps.sourceforge.net |
21 | | */ |
22 | | |
23 | | #include <stdlib.h> |
24 | | #include <time.h> |
25 | | |
26 | | #include <cmath> |
27 | | #include <cstdarg> |
28 | | #include <cstdio> |
29 | | #include <iomanip> |
30 | | #include <limits> |
31 | | #include <sstream> |
32 | | #include <string> |
33 | | |
34 | | #include <librevenge/librevenge.h> |
35 | | |
36 | | #include "libwps_internal.h" |
37 | | |
38 | | namespace libwps |
39 | | { |
40 | | uint8_t readU8(librevenge::RVNGInputStream *input) |
41 | 3.83G | { |
42 | 3.83G | unsigned long numBytesRead; |
43 | 3.83G | unsigned char const *p = input->read(sizeof(uint8_t), numBytesRead); |
44 | | |
45 | 3.83G | if (!p || numBytesRead != sizeof(uint8_t)) |
46 | 228k | { |
47 | 228k | static bool first = true; |
48 | 228k | if (first) |
49 | 8 | { |
50 | 8 | first = false; |
51 | 8 | WPS_DEBUG_MSG(("libwps::readU8: can not read data\n")); |
52 | 8 | } |
53 | 228k | return 0; |
54 | 228k | } |
55 | | |
56 | 3.83G | return *reinterpret_cast<uint8_t const *>(p); |
57 | 3.83G | } |
58 | | |
59 | | int8_t read8(librevenge::RVNGInputStream *input) |
60 | 13.4M | { |
61 | 13.4M | return int8_t(readU8(input)); |
62 | 13.4M | } |
63 | | |
64 | | uint16_t readU16(librevenge::RVNGInputStream *input) |
65 | 319M | { |
66 | 319M | uint8_t p0 = readU8(input); |
67 | 319M | uint8_t p1 = readU8(input); |
68 | 319M | return uint16_t(p0|(p1<<8)); |
69 | 319M | } |
70 | | |
71 | | int16_t read16(librevenge::RVNGInputStream *input) |
72 | 99.0M | { |
73 | 99.0M | return int16_t(readU16(input)); |
74 | 99.0M | } |
75 | | |
76 | | uint32_t readU32(librevenge::RVNGInputStream *input) |
77 | 102M | { |
78 | 102M | uint8_t p0 = readU8(input); |
79 | 102M | uint8_t p1 = readU8(input); |
80 | 102M | uint8_t p2 = readU8(input); |
81 | 102M | uint8_t p3 = readU8(input); |
82 | 102M | return uint32_t((p0<<0)|(p1<<8)|(p2<<16)|(p3<<24)); |
83 | 102M | } |
84 | | |
85 | | int32_t read32(librevenge::RVNGInputStream *input) |
86 | 7.90M | { |
87 | 7.90M | return int32_t(readU32(input)); |
88 | 7.90M | } |
89 | | |
90 | | bool readDouble4(RVNGInputStreamPtr &input, double &res, bool &isNaN) |
91 | 17.9k | { |
92 | 17.9k | isNaN=false; |
93 | 17.9k | res = 0; |
94 | 17.9k | long pos = input->tell(); |
95 | 17.9k | if (input->seek(4, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+4) |
96 | 0 | { |
97 | 0 | WPS_DEBUG_MSG(("libwps::readDouble4: the zone seems too short\n")); |
98 | 0 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
99 | 0 | return false; |
100 | 0 | } |
101 | 17.9k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
102 | | |
103 | 17.9k | double mantisse = 0; |
104 | | /** (first&3)==1: is used to decide if we store 100*N or N., |
105 | | (first&3)==2: indicates a basic int number (appears mainly when converting a dos file in a windows file) |
106 | | (first&3)==3: Can this exist ? What does this mean: 100*a basic int ? |
107 | | The other bytes seem to have classic meaning... |
108 | | */ |
109 | 17.9k | auto first = int(readU8(input)); |
110 | 17.9k | if ((first&3)==2) |
111 | 5.00k | { |
112 | | // so read it as a normal number |
113 | 5.00k | input->seek(-1, librevenge::RVNG_SEEK_CUR); |
114 | 5.00k | auto val=long(readU16(input)>>2); |
115 | 5.00k | val+=long(readU16(input))<<14; |
116 | 5.00k | if (val&0x20000000) |
117 | 666 | res = double(val-0x40000000); |
118 | 4.33k | else |
119 | 4.33k | res = double(val); |
120 | 5.00k | return true; |
121 | 5.00k | } |
122 | 12.9k | mantisse = double(first & 0xFC)/256 + double(readU8(input)); |
123 | 12.9k | auto mantExp = int(readU8(input)); |
124 | 12.9k | mantisse = (mantisse/256 + double(0x10+(mantExp & 0x0F)))/16; |
125 | 12.9k | int exp = ((mantExp&0xF0)>>4)+int(readU8(input)<<4); |
126 | 12.9k | int sign = 1; |
127 | 12.9k | if (exp & 0x800) |
128 | 2.25k | { |
129 | 2.25k | exp &= 0x7ff; |
130 | 2.25k | sign = -1; |
131 | 2.25k | } |
132 | | |
133 | 12.9k | if (exp == 0) |
134 | 4.92k | { |
135 | 4.92k | if (double(mantisse) > 1.-1e-4) return true; // ok zero |
136 | | // fixme find Nan representation |
137 | 0 | return false; |
138 | 4.92k | } |
139 | 8.02k | if (exp == 0x7FF) |
140 | 1.05k | { |
141 | 1.05k | if (double(mantisse) > 1.-1e-4) |
142 | 1.05k | { |
143 | 1.05k | res=std::numeric_limits<double>::quiet_NaN(); |
144 | 1.05k | isNaN=true; |
145 | | /* 0x7FFFF.. are nan(infinite, ...):ok |
146 | | |
147 | | 0xFFFFF.. are nan(in the sense, not a number but |
148 | | text...). In this case wps2csv and wps2text will |
149 | | display a nan. Not good, but difficult to retrieve the |
150 | | cell's content without excuting the formula associated |
151 | | to this cell :-~ |
152 | | */ |
153 | 1.05k | return true; |
154 | 1.05k | } |
155 | 0 | return false; |
156 | 1.05k | } |
157 | | |
158 | 6.96k | exp -= 0x3ff; |
159 | 6.96k | res = std::ldexp(mantisse, exp); |
160 | 6.96k | if (sign == -1) |
161 | 1.19k | { |
162 | 1.19k | res *= -1.; |
163 | 1.19k | } |
164 | 6.96k | if (first & 1) res/=100; |
165 | 6.96k | if (first & 2) |
166 | 1.28k | { |
167 | | // CHECKME... |
168 | 1.28k | WPS_DEBUG_MSG(("libwps::readDouble4: ARRGGGGGGGGGG find a float with first & 3 ARRGGGGGGGGGG in pos%lx,\n some float can be broken\n", static_cast<unsigned long>(pos))); |
169 | 1.28k | } |
170 | 6.96k | return true; |
171 | 8.02k | } |
172 | | |
173 | | bool readDouble8(RVNGInputStreamPtr &input, double &res, bool &isNaN) |
174 | 838k | { |
175 | 838k | isNaN=false; |
176 | 838k | res = 0; |
177 | 838k | long pos = input->tell(); |
178 | 838k | if (input->seek(8, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+8) |
179 | 41 | { |
180 | 41 | WPS_DEBUG_MSG(("libwps::readDouble8: the zone seems too short\n")); |
181 | 41 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
182 | 41 | return false; |
183 | 41 | } |
184 | 838k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
185 | 838k | double mantisse = 0; |
186 | 5.86M | for (int i = 0; i < 6; i++) |
187 | 5.03M | mantisse = mantisse/256 + double(readU8(input)); |
188 | 838k | auto mantExp = int(readU8(input)); |
189 | 838k | mantisse = (mantisse/256 + double(0x10+(mantExp & 0x0F)))/16; |
190 | 838k | int exp = ((mantExp&0xF0)>>4)+int(readU8(input)<<4); |
191 | 838k | int sign = 1; |
192 | 838k | if (exp & 0x800) |
193 | 85.3k | { |
194 | 85.3k | exp &= 0x7ff; |
195 | 85.3k | sign = -1; |
196 | 85.3k | } |
197 | | |
198 | 838k | double const epsilon=1.e-5; |
199 | 838k | if (exp == 0) |
200 | 412k | { |
201 | 412k | if (mantisse > 1-epsilon && mantisse < 1+epsilon) return true; // ok zero |
202 | | // fixme find Nan representation |
203 | 90.5k | return false; |
204 | 412k | } |
205 | 425k | if (exp == 0x7FF) |
206 | 22.5k | { |
207 | 22.5k | if (mantisse >= 1-epsilon) |
208 | 22.5k | { |
209 | 22.5k | res=std::numeric_limits<double>::quiet_NaN(); |
210 | 22.5k | return true; // ok 0x7FF and 0xFFF are nan |
211 | 22.5k | } |
212 | 0 | return false; |
213 | 22.5k | } |
214 | | |
215 | 402k | exp -= 0x3ff; |
216 | 402k | res = std::ldexp(mantisse, exp); |
217 | 402k | if (sign == -1) |
218 | 64.7k | { |
219 | 64.7k | res *= -1.; |
220 | 64.7k | } |
221 | 402k | return true; |
222 | 425k | } |
223 | | |
224 | | bool readDouble10(RVNGInputStreamPtr &input, double &res, bool &isNaN) |
225 | 108k | { |
226 | 108k | isNaN=false; |
227 | 108k | res = 0; |
228 | 108k | long pos = input->tell(); |
229 | 108k | if (input->seek(10, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+10) |
230 | 37 | { |
231 | 37 | WPS_DEBUG_MSG(("libwps::readDouble8: the zone seems too short\n")); |
232 | 37 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
233 | 37 | return false; |
234 | 37 | } |
235 | 108k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
236 | 108k | double mantisse = 0; |
237 | 976k | for (int i = 0; i < 8; i++) |
238 | 868k | mantisse = mantisse/256 + double(readU8(input))/128; |
239 | 108k | auto exp = int(readU16(input)); |
240 | 108k | int sign = 1; |
241 | 108k | if (exp & 0x8000) |
242 | 9.01k | { |
243 | 9.01k | exp &= 0x7fff; |
244 | 9.01k | sign = -1; |
245 | 9.01k | } |
246 | | |
247 | 108k | double const epsilon=1.e-5; |
248 | 108k | if (exp == 0) |
249 | 63.0k | { |
250 | 63.0k | if (mantisse < epsilon) return true; // checkme is this zero or a nan |
251 | | // fixme find Nan representation |
252 | 4.03k | return false; |
253 | 63.0k | } |
254 | 45.4k | if (exp == 0x7FFf) |
255 | 4.10k | { |
256 | 4.10k | if (mantisse >= 1-epsilon) |
257 | 3.78k | { |
258 | 3.78k | res=std::numeric_limits<double>::quiet_NaN(); |
259 | 3.78k | return true; // ok 0x7FF and 0xFFF are nan |
260 | 3.78k | } |
261 | 318 | return false; |
262 | 4.10k | } |
263 | | |
264 | 41.3k | exp -= 0x3fff; |
265 | 41.3k | res = std::ldexp(mantisse, exp); |
266 | 41.3k | if (sign == -1) |
267 | 4.90k | { |
268 | 4.90k | res *= -1.; |
269 | 4.90k | } |
270 | 41.3k | return true; |
271 | 45.4k | } |
272 | | |
273 | | bool readDouble2Inv(RVNGInputStreamPtr &input, double &res, bool &isNaN) |
274 | 16.2k | { |
275 | 16.2k | isNaN=false; |
276 | 16.2k | res = 0; |
277 | 16.2k | long pos = input->tell(); |
278 | 16.2k | if (input->seek(2, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+2) |
279 | 4 | { |
280 | 4 | WPS_DEBUG_MSG(("libwps::readDouble2Inv: the zone seems too short\n")); |
281 | 4 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
282 | 4 | return false; |
283 | 4 | } |
284 | 16.2k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
285 | 16.2k | auto val=int(libwps::readU16(input)); |
286 | 16.2k | int exp=val&0xf; |
287 | 16.2k | if ((exp&1)==1) |
288 | 3.20k | { |
289 | 3.20k | int mantisse=(val>>4); |
290 | 3.20k | if ((mantisse&0x800)) |
291 | 738 | mantisse -= 0x1000; |
292 | 3.20k | exp/=2; |
293 | 3.20k | const double factors[8]= { 5000, 500, 0.05, 0.005, 0.0005, 0.00005, 1/16., 1/64. }; |
294 | 3.20k | res=double(mantisse)*factors[exp]; |
295 | 3.20k | return true; |
296 | 3.20k | } |
297 | 13.0k | if ((val&0x8000)) |
298 | 325 | val-=0x10000; |
299 | 13.0k | res=double(val>>1); |
300 | 13.0k | return true; |
301 | 16.2k | } |
302 | | |
303 | | bool readDouble4Inv(RVNGInputStreamPtr &input, double &res, bool &isNaN) |
304 | 69.7k | { |
305 | 69.7k | isNaN=false; |
306 | 69.7k | res = 0; |
307 | 69.7k | long pos = input->tell(); |
308 | 69.7k | if (input->seek(4, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+4) |
309 | 16 | { |
310 | 16 | WPS_DEBUG_MSG(("libwps::readDouble4Inv: the zone seems too short\n")); |
311 | 16 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
312 | 16 | return false; |
313 | 16 | } |
314 | 69.7k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
315 | 69.7k | auto val=long(libwps::readU32(input)); |
316 | 69.7k | auto exp=int(val&0xf); |
317 | 69.7k | auto mantisse=int(val>>6); |
318 | 69.7k | if (val&0x20) |
319 | 2.83k | mantisse *= -1; |
320 | 69.7k | if (exp) |
321 | 38.1k | { |
322 | 38.1k | if (val&0x10) |
323 | 27.2k | res=mantisse/std::pow(10., exp); |
324 | 10.9k | else |
325 | 10.9k | res=mantisse*std::pow(10., exp); |
326 | 38.1k | return true; |
327 | 38.1k | } |
328 | 31.5k | res=double(mantisse); |
329 | 31.5k | return true; |
330 | 69.7k | } |
331 | | |
332 | | bool readData(RVNGInputStreamPtr &input, unsigned long size, librevenge::RVNGBinaryData &data) |
333 | 12.9k | { |
334 | 12.9k | data.clear(); |
335 | 12.9k | if (size == 0) return true; |
336 | | |
337 | 12.9k | const unsigned char *readData; |
338 | 12.9k | unsigned long sizeRead; |
339 | 12.9k | if ((readData=input->read(size, sizeRead)) == nullptr || sizeRead!=size) |
340 | 0 | return false; |
341 | 12.9k | data.append(readData, sizeRead); |
342 | | |
343 | 12.9k | return true; |
344 | 12.9k | } |
345 | | |
346 | | bool readDataToEnd(RVNGInputStreamPtr &input, librevenge::RVNGBinaryData &data) |
347 | 10.1k | { |
348 | 10.1k | data.clear(); |
349 | 10.1k | long pos=input->tell(); |
350 | 10.1k | input->seek(0,librevenge::RVNG_SEEK_END); |
351 | 10.1k | long sz=input->tell()-pos; |
352 | 10.1k | if (sz < 0) return false; |
353 | 10.1k | input->seek(pos,librevenge::RVNG_SEEK_SET); |
354 | 10.1k | return readData(input, static_cast<unsigned long>(sz), data) && input->isEnd(); |
355 | 10.1k | } |
356 | | |
357 | | std::string numberingTypeToString(NumberingType type) |
358 | 209k | { |
359 | 209k | switch (type) |
360 | 209k | { |
361 | 173k | case ARABIC: |
362 | 173k | return "1"; |
363 | 13.8k | case LOWERCASE: |
364 | 13.8k | return "a"; |
365 | 2.78k | case UPPERCASE: |
366 | 2.78k | return "A"; |
367 | 8.54k | case LOWERCASE_ROMAN: |
368 | 8.54k | return "i"; |
369 | 10.7k | case UPPERCASE_ROMAN: |
370 | 10.7k | return "I"; |
371 | 0 | case NONE: |
372 | 0 | case BULLET: |
373 | 0 | default: |
374 | 0 | break; |
375 | 209k | } |
376 | 0 | WPS_DEBUG_MSG(("libwps::numberingTypeToString: must not be called with type %d\n", int(type))); |
377 | 0 | return "1"; |
378 | 209k | } |
379 | | } |
380 | | |
381 | | //////////////////////////////////////////////////////////// |
382 | | // color |
383 | | //////////////////////////////////////////////////////////// |
384 | | |
385 | | // color function |
386 | | WPSColor WPSColor::barycenter(float alpha, WPSColor const &colA, |
387 | | float beta, WPSColor const &colB) |
388 | 437k | { |
389 | 437k | uint32_t res = 0; |
390 | 2.18M | for (int i=0, depl=0; i<4; i++, depl+=8) |
391 | 1.74M | { |
392 | 1.74M | float val=alpha*float((colA.m_value>>depl)&0xFF)+beta*float((colB.m_value>>depl)&0xFF); |
393 | 1.74M | if (val < 0) val=0; |
394 | 1.74M | if (val > 256) val=256; |
395 | 1.74M | auto comp= static_cast<unsigned char>(val); |
396 | 1.74M | res+=uint32_t(comp<<depl); |
397 | 1.74M | } |
398 | 437k | return WPSColor(res); |
399 | 437k | } |
400 | | |
401 | | std::ostream &operator<< (std::ostream &o, WPSColor const &c) |
402 | 11.2M | { |
403 | 11.2M | auto const width = o.width(); |
404 | 11.2M | auto const fill = o.fill(); |
405 | 11.2M | o << "#" << std::hex << std::setfill('0') << std::setw(6) |
406 | 11.2M | << (c.m_value&0xFFFFFF) |
407 | | // std::ios::width() takes/returns std::streamsize (long), but |
408 | | // std::setw() takes int. Go figure... |
409 | 11.2M | << std::dec << std::setfill(fill) << std::setw(static_cast<int>(width)); |
410 | 11.2M | return o; |
411 | 11.2M | } |
412 | | |
413 | | std::string WPSColor::str() const |
414 | 11.2M | { |
415 | 11.2M | std::stringstream stream; |
416 | 11.2M | stream << *this; |
417 | 11.2M | return stream.str(); |
418 | 11.2M | } |
419 | | |
420 | | //////////////////////////////////////////////////////////// |
421 | | // field |
422 | | //////////////////////////////////////////////////////////// |
423 | | // format function |
424 | | namespace libwps |
425 | | { |
426 | | static bool convertDTFormat(std::string const &dtFormat, librevenge::RVNGPropertyListVector &propVect) |
427 | 237k | { |
428 | 237k | propVect.clear(); |
429 | 237k | std::string text(""); |
430 | 237k | librevenge::RVNGPropertyList list; |
431 | 237k | size_t len=dtFormat.size(); |
432 | 1.76M | for (size_t c=0; c < len; ++c) |
433 | 1.52M | { |
434 | 1.52M | if (dtFormat[c]!='%' || c+1==len) |
435 | 645k | { |
436 | 645k | text+=dtFormat[c]; |
437 | 645k | continue; |
438 | 645k | } |
439 | 882k | char ch=dtFormat[++c]; |
440 | 882k | if (ch=='%') |
441 | 0 | { |
442 | 0 | text += '%'; |
443 | 0 | continue; |
444 | 0 | } |
445 | 882k | if (!text.empty()) |
446 | 645k | { |
447 | 645k | list.clear(); |
448 | 645k | list.insert("librevenge:value-type", "text"); |
449 | 645k | list.insert("librevenge:text", text.c_str()); |
450 | 645k | propVect.append(list); |
451 | 645k | text.clear(); |
452 | 645k | } |
453 | 882k | list.clear(); |
454 | 882k | switch (ch) |
455 | 882k | { |
456 | 2.10k | case 'Y': |
457 | 2.10k | list.insert("number:style", "long"); |
458 | 2.10k | WPS_FALLTHROUGH; |
459 | 63.5k | case 'y': |
460 | 63.5k | list.insert("librevenge:value-type", "year"); |
461 | 63.5k | propVect.append(list); |
462 | 63.5k | break; |
463 | 843 | case 'B': |
464 | 843 | list.insert("number:style", "long"); |
465 | 843 | WPS_FALLTHROUGH; |
466 | 843 | case 'b': |
467 | 843 | case 'h': |
468 | 843 | list.insert("librevenge:value-type", "month"); |
469 | 843 | list.insert("number:textual", true); |
470 | 843 | propVect.append(list); |
471 | 843 | break; |
472 | 62.6k | case 'm': |
473 | 62.6k | list.insert("librevenge:value-type", "month"); |
474 | 62.6k | propVect.append(list); |
475 | 62.6k | break; |
476 | 0 | case 'e': |
477 | 0 | list.insert("number:style", "long"); |
478 | 0 | WPS_FALLTHROUGH; |
479 | 63.2k | case 'd': |
480 | 63.2k | list.insert("librevenge:value-type", "day"); |
481 | 63.2k | propVect.append(list); |
482 | 63.2k | break; |
483 | 498 | case 'A': |
484 | 498 | list.insert("number:style", "long"); |
485 | 498 | WPS_FALLTHROUGH; |
486 | 498 | case 'a': |
487 | 498 | list.insert("librevenge:value-type", "day-of-week"); |
488 | 498 | propVect.append(list); |
489 | 498 | break; |
490 | | |
491 | 1.22k | case 'H': |
492 | 1.22k | list.insert("number:style", "long"); |
493 | 1.22k | WPS_FALLTHROUGH; |
494 | 173k | case 'I': |
495 | 173k | list.insert("librevenge:value-type", "hours"); |
496 | 173k | propVect.append(list); |
497 | 173k | break; |
498 | 173k | case 'M': |
499 | 173k | list.insert("librevenge:value-type", "minutes"); |
500 | 173k | list.insert("number:style", "long"); |
501 | 173k | propVect.append(list); |
502 | 173k | break; |
503 | 172k | case 'S': |
504 | 172k | list.insert("librevenge:value-type", "seconds"); |
505 | 172k | list.insert("number:style", "long"); |
506 | 172k | propVect.append(list); |
507 | 172k | break; |
508 | 171k | case 'p': |
509 | 171k | list.clear(); |
510 | 171k | list.insert("librevenge:value-type", "am-pm"); |
511 | 171k | propVect.append(list); |
512 | 171k | break; |
513 | | #if !defined(__clang__) |
514 | | default: |
515 | | WPS_DEBUG_MSG(("convertDTFormat: find unimplement command %c(ignored)\n", ch)); |
516 | | #endif |
517 | 882k | } |
518 | 882k | } |
519 | 237k | if (!text.empty()) |
520 | 0 | { |
521 | 0 | list.clear(); |
522 | 0 | list.insert("librevenge:value-type", "text"); |
523 | 0 | list.insert("librevenge:text", text.c_str()); |
524 | 0 | propVect.append(list); |
525 | 0 | } |
526 | 237k | return propVect.count()!=0; |
527 | 237k | } |
528 | | } |
529 | | |
530 | | bool WPSField::addTo(librevenge::RVNGPropertyList &propList) const |
531 | 474k | { |
532 | 474k | switch (m_type) |
533 | 474k | { |
534 | 65.4k | case Date: |
535 | 65.4k | { |
536 | 65.4k | propList.insert("librevenge:field-type", "text:date"); |
537 | | |
538 | 65.4k | librevenge::RVNGPropertyListVector pVect; |
539 | 65.4k | if (!libwps::convertDTFormat(m_DTFormat.empty() ? std::string("%m/%d/%y") : m_DTFormat, pVect)) |
540 | 0 | break; |
541 | | |
542 | 65.4k | propList.insert("librevenge:value-type", "date"); |
543 | 65.4k | propList.insert("number:automatic-order", "true"); |
544 | 65.4k | propList.insert("librevenge:format", pVect); |
545 | 65.4k | break; |
546 | 65.4k | } |
547 | 0 | case PageCount: |
548 | 0 | propList.insert("librevenge:field-type", "text:page-count"); |
549 | 0 | propList.insert("style:num-format", numberingTypeToString(m_numberingType).c_str()); |
550 | 0 | break; |
551 | 168k | case PageNumber: |
552 | 169k | case PageNumberNext: |
553 | 169k | propList.insert("librevenge:field-type", "text:page-number"); |
554 | 169k | propList.insert("style:num-format", numberingTypeToString(m_numberingType).c_str()); |
555 | 169k | if (m_type==PageNumberNext) |
556 | 1.23k | propList.insert("text:select-page", "next"); |
557 | 169k | break; |
558 | 68.0k | case Title: |
559 | 68.0k | propList.insert("librevenge:field-type", "text:title"); |
560 | 68.0k | break; |
561 | 171k | case Time: |
562 | 171k | { |
563 | 171k | propList.insert("librevenge:field-type", "text:time"); |
564 | | |
565 | 171k | librevenge::RVNGPropertyListVector pVect; |
566 | 171k | if (!libwps::convertDTFormat(m_DTFormat.empty() ? std::string("%I:%M:%S %p") : m_DTFormat, pVect)) |
567 | 0 | break; |
568 | | |
569 | 171k | propList.insert("librevenge:value-type", "time"); |
570 | 171k | propList.insert("number:automatic-order", "true"); |
571 | 171k | propList.insert("librevenge:format", pVect); |
572 | 171k | break; |
573 | 171k | } |
574 | | |
575 | 0 | case Database: |
576 | 0 | case Link: |
577 | 0 | case None: |
578 | 0 | default: |
579 | 0 | return false; |
580 | 474k | } |
581 | 474k | return true; |
582 | 474k | } |
583 | | |
584 | | librevenge::RVNGString WPSField::getString() const |
585 | 0 | { |
586 | 0 | librevenge::RVNGString res; |
587 | 0 | switch (m_type) |
588 | 0 | { |
589 | 0 | case Database: |
590 | 0 | if (m_data.length()) |
591 | 0 | res=librevenge::RVNGString(m_data.c_str()); |
592 | 0 | else |
593 | 0 | res=librevenge::RVNGString("#DATAFIELD#"); |
594 | 0 | break; |
595 | 0 | case Link: |
596 | 0 | if (m_data.length()) |
597 | 0 | res=librevenge::RVNGString(m_data.c_str()); |
598 | 0 | else |
599 | 0 | res=librevenge::RVNGString("#LINK#"); |
600 | 0 | break; |
601 | 0 | case Title: |
602 | 0 | if (m_data.length()) |
603 | 0 | res=librevenge::RVNGString(m_data.c_str()); |
604 | 0 | else |
605 | 0 | res=librevenge::RVNGString("#TITLE#"); |
606 | 0 | break; |
607 | 0 | case Date: |
608 | 0 | case PageCount: |
609 | 0 | case PageNumber: |
610 | 0 | case PageNumberNext: |
611 | 0 | case Time: |
612 | 0 | case None: |
613 | 0 | default: |
614 | 0 | break; |
615 | 0 | } |
616 | | |
617 | 0 | return res; |
618 | 0 | } |
619 | | //////////////////////////////////////////////////////////// |
620 | | // border |
621 | | //////////////////////////////////////////////////////////// |
622 | | |
623 | | int WPSBorder::compare(WPSBorder const &orig) const |
624 | 24.3k | { |
625 | 24.3k | int diff = int(m_style)-int(orig.m_style); |
626 | 24.3k | if (diff) return diff; |
627 | 22.6k | diff = m_width-orig.m_width; |
628 | 22.6k | if (diff) return diff; |
629 | 22.4k | if (m_color < orig.m_color) return -1; |
630 | 21.7k | if (m_color > orig.m_color) return 1; |
631 | 21.1k | if (m_widthsList.size() != orig.m_widthsList.size()) |
632 | 0 | return m_widthsList.size() < orig.m_widthsList.size() ? -1 : 1; |
633 | 21.1k | for (size_t i=0; i<m_widthsList.size(); ++i) |
634 | 0 | { |
635 | 0 | if (m_widthsList[i]<orig.m_widthsList[i]) return -1; |
636 | 0 | if (m_widthsList[i]>orig.m_widthsList[i]) return 1; |
637 | 0 | } |
638 | 21.1k | return 0; |
639 | 21.1k | } |
640 | | |
641 | | bool WPSBorder::addTo(librevenge::RVNGPropertyList &propList, std::string const which) const |
642 | 1.06M | { |
643 | 1.06M | std::stringstream stream, field; |
644 | 1.06M | stream << m_width << "pt "; |
645 | 1.06M | if (m_type==WPSBorder::Double || m_type==WPSBorder::Triple) |
646 | 142k | { |
647 | 142k | static bool first = true; |
648 | 142k | if (first && m_style!=Simple) |
649 | 0 | { |
650 | 0 | WPS_DEBUG_MSG(("WPSBorder::addTo: find double or tripe border with complex style\n")); |
651 | 0 | first = false; |
652 | 0 | } |
653 | 142k | stream << "double"; |
654 | 142k | } |
655 | 921k | else |
656 | 921k | { |
657 | 921k | switch (m_style) |
658 | 921k | { |
659 | 48.8k | case Dot: |
660 | 52.9k | case LargeDot: |
661 | 52.9k | stream << "dotted"; |
662 | 52.9k | break; |
663 | 98.4k | case Dash: |
664 | 98.4k | stream << "dashed"; |
665 | 98.4k | break; |
666 | 770k | case Simple: |
667 | 770k | stream << "solid"; |
668 | 770k | break; |
669 | 0 | case None: |
670 | 0 | default: |
671 | 0 | stream << "none"; |
672 | 0 | break; |
673 | 921k | } |
674 | 921k | } |
675 | 1.06M | stream << " " << m_color.str(); |
676 | 1.06M | field << "fo:border"; |
677 | 1.06M | if (which.length()) |
678 | 1.06M | field << "-" << which; |
679 | 1.06M | propList.insert(field.str().c_str(), stream.str().c_str()); |
680 | 1.06M | size_t numRelWidth=m_widthsList.size(); |
681 | 1.06M | if (!numRelWidth) |
682 | 1.06M | return true; |
683 | 0 | if (m_type!=WPSBorder::Double || numRelWidth!=3) |
684 | 0 | { |
685 | 0 | static bool first = true; |
686 | 0 | if (first) |
687 | 0 | { |
688 | 0 | WPS_DEBUG_MSG(("WPSBorder::addTo: relative width is only implemented with double style\n")); |
689 | 0 | first = false; |
690 | 0 | } |
691 | 0 | return true; |
692 | 0 | } |
693 | 0 | double totalWidth=0; |
694 | 0 | for (auto const &w : m_widthsList) |
695 | 0 | totalWidth+=w; |
696 | 0 | if (totalWidth <= 0) |
697 | 0 | { |
698 | 0 | WPS_DEBUG_MSG(("WPSBorder::addTo: can not compute total width\n")); |
699 | 0 | return true; |
700 | 0 | } |
701 | 0 | double factor=m_width/totalWidth; |
702 | 0 | stream.str(""); |
703 | 0 | for (size_t w=0; w < numRelWidth; w++) |
704 | 0 | { |
705 | 0 | stream << factor *m_widthsList[w]<< "pt"; |
706 | 0 | if (w+1!=numRelWidth) |
707 | 0 | stream << " "; |
708 | 0 | } |
709 | 0 | field.str(""); |
710 | 0 | field << "style:border-line-width"; |
711 | 0 | if (!which.empty()) |
712 | 0 | field << "-" << which; |
713 | 0 | propList.insert(field.str().c_str(), stream.str().c_str()); |
714 | 0 | return true; |
715 | 0 | } |
716 | | |
717 | | std::ostream &operator<< (std::ostream &o, WPSBorder::Style const &style) |
718 | 0 | { |
719 | 0 | switch (style) |
720 | 0 | { |
721 | 0 | case WPSBorder::None: |
722 | 0 | o << "none"; |
723 | 0 | break; |
724 | 0 | case WPSBorder::Simple: |
725 | 0 | break; |
726 | 0 | case WPSBorder::Dot: |
727 | 0 | o << "dot"; |
728 | 0 | break; |
729 | 0 | case WPSBorder::LargeDot: |
730 | 0 | o << "large dot"; |
731 | 0 | break; |
732 | 0 | case WPSBorder::Dash: |
733 | 0 | o << "dash"; |
734 | 0 | break; |
735 | 0 | default: |
736 | 0 | WPS_DEBUG_MSG(("WPSBorder::operator<<: find unknown style\n")); |
737 | 0 | o << "#style=" << int(style); |
738 | 0 | break; |
739 | 0 | } |
740 | 0 | return o; |
741 | 0 | } |
742 | | |
743 | | std::ostream &operator<< (std::ostream &o, WPSBorder const &border) |
744 | 0 | { |
745 | 0 | o << border.m_style << ":"; |
746 | 0 | switch (border.m_type) |
747 | 0 | { |
748 | 0 | case WPSBorder::Single: |
749 | 0 | break; |
750 | 0 | case WPSBorder::Double: |
751 | 0 | o << "double:"; |
752 | 0 | break; |
753 | 0 | case WPSBorder::Triple: |
754 | 0 | o << "triple:"; |
755 | 0 | break; |
756 | 0 | default: |
757 | 0 | WPS_DEBUG_MSG(("WPSBorder::operator<<: find unknown type\n")); |
758 | 0 | o << "#type=" << int(border.m_type) << ":"; |
759 | 0 | break; |
760 | 0 | } |
761 | 0 | if (border.m_width > 1 || border.m_width < 1) o << "w=" << border.m_width << ":"; |
762 | 0 | if (!border.m_color.isBlack()) |
763 | 0 | o << "col=" << std::hex << border.m_color << std::dec << ":"; |
764 | 0 | o << ","; |
765 | 0 | size_t numRelWidth=border.m_widthsList.size(); |
766 | 0 | if (numRelWidth) |
767 | 0 | { |
768 | 0 | o << "bordW[rel]=["; |
769 | 0 | for (auto const &w : border.m_widthsList) |
770 | 0 | o << w << ","; |
771 | 0 | o << "]:"; |
772 | 0 | } |
773 | 0 | o << border.m_extra; |
774 | 0 | return o; |
775 | 0 | } |
776 | | |
777 | | //////////////////////////////////////////////////////////// |
778 | | // object |
779 | | //////////////////////////////////////////////////////////// |
780 | | WPSEmbeddedObject::~WPSEmbeddedObject() |
781 | 385k | { |
782 | 385k | } |
783 | | |
784 | | bool WPSEmbeddedObject::addTo(librevenge::RVNGPropertyList &propList) const |
785 | 2.32k | { |
786 | 2.32k | bool firstSet=false; |
787 | 2.32k | librevenge::RVNGPropertyListVector auxiliarVector; |
788 | 4.63k | for (size_t i=0; i<m_dataList.size(); ++i) |
789 | 2.30k | { |
790 | 2.30k | if (m_dataList[i].empty()) continue; |
791 | 2.30k | std::string type=m_typeList.size() ? m_typeList[i] : "image/pict"; |
792 | 2.30k | if (!firstSet) |
793 | 2.09k | { |
794 | 2.09k | propList.insert("librevenge:mime-type", type.c_str()); |
795 | 2.09k | propList.insert("office:binary-data", m_dataList[i]); |
796 | 2.09k | firstSet=true; |
797 | 2.09k | continue; |
798 | 2.09k | } |
799 | 211 | librevenge::RVNGPropertyList auxiList; |
800 | 211 | auxiList.insert("librevenge:mime-type", type.c_str()); |
801 | 211 | auxiList.insert("office:binary-data", m_dataList[i]); |
802 | 211 | auxiliarVector.append(auxiList); |
803 | 211 | } |
804 | 2.32k | if (!auxiliarVector.empty()) |
805 | 202 | propList.insert("librevenge:replacement-objects", auxiliarVector); |
806 | 2.32k | if (!firstSet) |
807 | 225 | { |
808 | 225 | WPS_DEBUG_MSG(("WPSEmbeddedObject::addTo: called without picture\n")); |
809 | 225 | return false; |
810 | 225 | } |
811 | 2.09k | return true; |
812 | 2.32k | } |
813 | | |
814 | | std::ostream &operator<<(std::ostream &o, WPSEmbeddedObject const &pict) |
815 | 0 | { |
816 | 0 | if (pict.isEmpty()) return o; |
817 | 0 | if (pict.m_size!=Vec2f()) |
818 | 0 | o << "size=" << pict.m_size << ","; |
819 | 0 | o << "["; |
820 | 0 | for (auto const &type : pict.m_typeList) |
821 | 0 | { |
822 | 0 | if (type.empty()) |
823 | 0 | o << "_,"; |
824 | 0 | else |
825 | 0 | o << type << ","; |
826 | 0 | } |
827 | 0 | o << "],"; |
828 | 0 | return o; |
829 | 0 | } |
830 | | |
831 | | //////////////////////////////////////////////////////////// |
832 | | // unicode |
833 | | //////////////////////////////////////////////////////////// |
834 | | namespace libwps |
835 | | { |
836 | | void appendUnicode(uint32_t val, librevenge::RVNGString &buffer) |
837 | 142M | { |
838 | 142M | if (val < 0x20) |
839 | 822k | { |
840 | 822k | WPS_DEBUG_MSG(("libwps::appendUnicode: find an old char %x, skip it\n", val)); |
841 | 822k | return; |
842 | 822k | } |
843 | 142M | uint8_t first; |
844 | 142M | int len; |
845 | 142M | if (val < 0x80) |
846 | 43.8M | { |
847 | 43.8M | first = 0; |
848 | 43.8M | len = 1; |
849 | 43.8M | } |
850 | 98.2M | else if (val < 0x800) |
851 | 31.3M | { |
852 | 31.3M | first = 0xc0; |
853 | 31.3M | len = 2; |
854 | 31.3M | } |
855 | 66.9M | else if (val < 0x10000) |
856 | 66.4M | { |
857 | 66.4M | first = 0xe0; |
858 | 66.4M | len = 3; |
859 | 66.4M | } |
860 | 493k | else if (val < 0x200000) |
861 | 493k | { |
862 | 493k | first = 0xf0; |
863 | 493k | len = 4; |
864 | 493k | } |
865 | 0 | else if (val < 0x4000000) |
866 | 0 | { |
867 | 0 | first = 0xf8; |
868 | 0 | len = 5; |
869 | 0 | } |
870 | 0 | else |
871 | 0 | { |
872 | 0 | first = 0xfc; |
873 | 0 | len = 6; |
874 | 0 | } |
875 | | |
876 | 142M | char outbuf[7]; |
877 | 142M | int i; |
878 | 307M | for (i = len - 1; i > 0; --i) |
879 | 165M | { |
880 | 165M | outbuf[i] = char((val & 0x3f) | 0x80); |
881 | 165M | val >>= 6; |
882 | 165M | } |
883 | 142M | outbuf[0] = char(val | first); |
884 | 142M | outbuf[len] = 0; |
885 | 142M | buffer.append(outbuf); |
886 | 142M | } |
887 | | } |
888 | | |
889 | | // a little geometry |
890 | | WPSTransformation WPSTransformation::rotation(float angle, Vec2f const ¢er) |
891 | 23.7k | { |
892 | 23.7k | auto angl=float(double(angle)*M_PI/180); |
893 | 23.7k | auto cosA=float(std::cos(angl)); |
894 | 23.7k | auto sinA=float(std::sin(angl)); |
895 | 23.7k | return WPSTransformation(WPSVec3f(cosA, -sinA, center[0]-cosA*center[0]+sinA*center[1]), |
896 | 23.7k | WPSVec3f(sinA, cosA, center[1]-sinA*center[0]-cosA*center[1])); |
897 | 23.7k | } |
898 | | |
899 | | bool WPSTransformation::decompose(float &rot, Vec2f &shearing, WPSTransformation &transform, Vec2f const &origCenter) const |
900 | 23.5k | { |
901 | 23.5k | if (m_isIdentity) return false; |
902 | 23.5k | WPSVec3f const &xRow=(*this)[0]; |
903 | 23.5k | WPSVec3f const &yRow=(*this)[1]; |
904 | 23.5k | Vec2f const ¢er=*this * origCenter; |
905 | | // first check shearing |
906 | 23.5k | float shearY=0; |
907 | 23.5k | float val1=xRow[0]*xRow[1]; |
908 | 23.5k | float val2=yRow[0]*yRow[1]; |
909 | 23.5k | float diff=val2-val1; |
910 | 23.5k | if (diff<-0.01f || diff>0.01f) |
911 | 21.8k | { |
912 | 21.8k | float const &A=val1; |
913 | 21.8k | float const B=xRow[1]*yRow[0]+xRow[0]*yRow[1]; |
914 | 21.8k | float const &C=diff; |
915 | 21.8k | if (A>=0 && A<=0) |
916 | 2.94k | { |
917 | 2.94k | if (B>=0 && A<=0) |
918 | 1.45k | { |
919 | 1.45k | WPS_DEBUG_MSG(("WPSTransformation::decompose: can not determine the shearing\n")); |
920 | 1.45k | return false; |
921 | 1.45k | } |
922 | 1.48k | shearY=C/B; |
923 | 1.48k | } |
924 | 18.8k | else |
925 | 18.8k | { |
926 | 18.8k | float const &delta=B*B-4*A*C; |
927 | 18.8k | if (delta<0) |
928 | 259 | { |
929 | 259 | WPS_DEBUG_MSG(("WPSTransformation::decompose: can not determine the shearing\n")); |
930 | 259 | return false; |
931 | 259 | } |
932 | 18.6k | shearY=(B-float(std::sqrt(delta)))/2.f/A; |
933 | 18.6k | } |
934 | 20.1k | transform=WPSTransformation::shear(Vec2f(0,-shearY), center) **this; |
935 | 20.1k | } |
936 | 1.69k | else |
937 | 1.69k | transform=*this; |
938 | 21.8k | shearing=Vec2f(0,shearY); |
939 | | // fixme: we must first check for symetry here... |
940 | | // now the rotation |
941 | 21.8k | rot=-std::atan2(-transform[1][0],transform[1][1]); |
942 | 21.8k | rot *= float(180/M_PI); |
943 | 21.8k | transform=WPSTransformation::rotation(-rot, center) * transform; |
944 | 21.8k | return true; |
945 | 23.5k | } |
946 | | |
947 | | //////////////////////////////////////////////////////////// |
948 | | // utility |
949 | | //////////////////////////////////////////////////////////// |
950 | | |
951 | | namespace libwps |
952 | | { |
953 | | std::string getCellName(Vec2i const &cellPos, Vec2b const &relative) |
954 | 95.7k | { |
955 | 95.7k | if (cellPos[0]<0 || cellPos[1]<0) |
956 | 823 | { |
957 | 823 | WPS_DEBUG_MSG(("libwps::getCellName: invalid cell position\n")); |
958 | 823 | return ""; |
959 | 823 | } |
960 | 94.9k | std::stringstream o; |
961 | 94.9k | if (!relative[0]) o << "$"; |
962 | 94.9k | int c=cellPos[0]; |
963 | 94.9k | std::string colString(1, char(c%26+'A')); |
964 | 94.9k | c /= 26; |
965 | 168k | while (c>0) |
966 | 73.6k | { |
967 | 73.6k | --c; |
968 | 73.6k | colString.insert(0, std::string(1,char(c%26+'A'))); |
969 | 73.6k | c /= 26; |
970 | 73.6k | } |
971 | 94.9k | o << colString; |
972 | 94.9k | if (!relative[1]) o << "$"; |
973 | 94.9k | o << cellPos[1]+1; |
974 | 94.9k | return o.str(); |
975 | 95.7k | } |
976 | | |
977 | | bool encodeLotusPassword(char const *password, uint16_t &key, std::vector<uint8_t> &keys, uint8_t const(&defValues)[16]) |
978 | 3.44k | { |
979 | 3.44k | if (!password) |
980 | 0 | { |
981 | 0 | WPS_DEBUG_MSG(("libwps::encodeLotusPassword: called without password\n")); |
982 | 0 | return false; |
983 | 0 | } |
984 | 3.44k | size_t const len=16; |
985 | 3.44k | key=0xFFFF; |
986 | 3.44k | uint16_t val=0; |
987 | 18.8k | for (size_t i=0; i<len; ++i) |
988 | 18.8k | { |
989 | 18.8k | if (password[i]==0) |
990 | 3.44k | break; |
991 | 15.3k | auto c=uint8_t(password[i]); |
992 | 15.3k | key=uint16_t(key^c); |
993 | 15.3k | val=uint16_t((val&0xFF)|(key<<8)); |
994 | 15.3k | val=uint16_t(((val<<4)&0xFFF0)|(val>>12)); |
995 | 15.3k | key^=val; |
996 | 15.3k | val=uint16_t((val<<8)|(val>>8)); |
997 | 15.3k | val=uint16_t((val<<1)|(val>>15)); |
998 | 15.3k | val=uint16_t((val<<8)|(val>>8)); |
999 | 15.3k | key=uint16_t((key<<8)|(key>>8)); |
1000 | 15.3k | key^=val; |
1001 | | |
1002 | 15.3k | val=uint16_t((((val>>4)&0xfff)|(val<<12))&0xe0ff); |
1003 | 15.3k | key^=val; |
1004 | 15.3k | val=uint16_t((val>>1)|(val<<15)); |
1005 | 15.3k | key=uint16_t(key^(val>>8)); |
1006 | 15.3k | } |
1007 | | |
1008 | 3.44k | size_t cPos; |
1009 | 3.44k | keys.resize(len); |
1010 | | // copy password in keys and fill the remaining space with |
1011 | | // defValues |
1012 | 18.8k | for (cPos=0 ; cPos<len; ++cPos) |
1013 | 18.8k | { |
1014 | 18.8k | if (password[cPos]==0) |
1015 | 3.44k | break; |
1016 | 15.3k | keys[cPos]=uint8_t(password[cPos]); |
1017 | 15.3k | } |
1018 | 3.44k | uint8_t const *defPtr=defValues; |
1019 | 43.1k | for (; cPos<len; ++cPos) |
1020 | 39.7k | keys[cPos] = *(defPtr++); |
1021 | | // now do an xor to code the result |
1022 | 58.5k | for (size_t i=0; i<len; ++i) |
1023 | 55.1k | keys[i]=uint8_t(keys[i]^(key>>((i%2)==0 ? 8 : 0))); |
1024 | 3.44k | return true; |
1025 | 3.44k | } |
1026 | | } |
1027 | | //////////////////////////////////////////////////////////// |
1028 | | // debug |
1029 | | //////////////////////////////////////////////////////////// |
1030 | | |
1031 | | namespace libwps |
1032 | | { |
1033 | | #ifdef DEBUG |
1034 | | void printDebugMsg(const char *format, ...) |
1035 | | { |
1036 | | va_list args; |
1037 | | va_start(args, format); |
1038 | | std::vfprintf(stderr, format, args); |
1039 | | va_end(args); |
1040 | | } |
1041 | | #endif |
1042 | | } |
1043 | | /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */ |