/src/libwps/src/lib/XYWrite.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) 2006, 2007 Andrew Ziem |
11 | | * Copyright (C) 2004 Marc Maurer (uwog@uwog.net) |
12 | | * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch) |
13 | | * |
14 | | * For minor contributions see the git repository. |
15 | | * |
16 | | * Alternatively, the contents of this file may be used under the terms |
17 | | * of the GNU Lesser General Public License Version 2.1 or later |
18 | | * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are |
19 | | * applicable instead of those above. |
20 | | */ |
21 | | |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | |
25 | | #include <sstream> |
26 | | |
27 | | #include <librevenge-stream/librevenge-stream.h> |
28 | | |
29 | | #include "libwps_internal.h" |
30 | | #include "libwps_tools_win.h" |
31 | | |
32 | | #include "WPSCell.h" |
33 | | #include "WPSContentListener.h" |
34 | | #include "WPSEntry.h" |
35 | | #include "WPSFont.h" |
36 | | #include "WPSHeader.h" |
37 | | #include "WPSPageSpan.h" |
38 | | #include "WPSParagraph.h" |
39 | | #include "WPSPosition.h" |
40 | | #include "WPSTable.h" |
41 | | #include "WPSTextSubDocument.h" |
42 | | |
43 | | #include "XYWrite.h" |
44 | | |
45 | | namespace XYWriteParserInternal |
46 | | { |
47 | | /** Internal: class to store a basic cell with borders */ |
48 | | struct Cell final : public WPSCell |
49 | | { |
50 | | //! constructor |
51 | | explicit Cell(XYWriteParser &parser) |
52 | 125k | : WPSCell() |
53 | 125k | , m_parser(parser) |
54 | 125k | , m_entry() |
55 | 125k | , m_style() |
56 | 125k | { |
57 | 125k | } |
58 | | //! virtual destructor |
59 | | ~Cell() final; |
60 | | |
61 | | //! operator<< |
62 | | friend std::ostream &operator<<(std::ostream &o, Cell const &cell); |
63 | | //! call when a cell must be send |
64 | | bool send(WPSListenerPtr &listener) final |
65 | 0 | { |
66 | 0 | if (!listener) return true; |
67 | 0 | auto *listen=dynamic_cast<WPSContentListener *>(listener.get()); |
68 | 0 | if (!listen) |
69 | 0 | { |
70 | 0 | WPS_DEBUG_MSG(("XYWriteParserInternal::Cell::send: unexpected listener\n")); |
71 | 0 | return true; |
72 | 0 | } |
73 | 0 | listen->openTableCell(*this); |
74 | 0 | sendContent(listener); |
75 | 0 | listen->closeTableCell(); |
76 | 0 | return true; |
77 | 0 | } |
78 | | |
79 | | //! call when the content of a cell must be send |
80 | | bool sendContent(WPSListenerPtr &) final |
81 | 107k | { |
82 | 107k | auto input=m_parser.getInput(); |
83 | 107k | if (!input) |
84 | 0 | { |
85 | 0 | WPS_DEBUG_MSG(("XYWriteParserInternal::Cell::sendContent: can not find the impu\n")); |
86 | 0 | return true; |
87 | 0 | } |
88 | 107k | long pos=input->tell(); |
89 | 107k | m_parser.parseTextZone(m_entry, m_style); |
90 | 107k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
91 | 107k | return true; |
92 | 107k | } |
93 | | |
94 | | //! the actual parser |
95 | | XYWriteParser &m_parser; |
96 | | //! the text entry |
97 | | WPSEntry m_entry; |
98 | | //! the cell style |
99 | | std::string m_style; |
100 | | }; |
101 | | Cell::~Cell() |
102 | 410k | { |
103 | 410k | } |
104 | | |
105 | | //! a structure used to store a format |
106 | | struct Format |
107 | | { |
108 | | //! constructor |
109 | | explicit Format(bool inDosFile=false) |
110 | 6.86M | : m_inDosFile(inDosFile) |
111 | 6.86M | , m_string() |
112 | 6.86M | , m_args() |
113 | 6.86M | , m_entry() |
114 | 6.86M | , m_isComplex(false) |
115 | 6.86M | , m_listCounter(-1) |
116 | 6.86M | , m_level() |
117 | 6.86M | , m_children() |
118 | 6.86M | { |
119 | 6.86M | } |
120 | | //! return true if the format is empty |
121 | | bool empty() const |
122 | 0 | { |
123 | 0 | return m_string.empty() && m_args.empty(); |
124 | 0 | } |
125 | | //! returns the upper case of a string |
126 | | static std::string upperCase(std::string const &str) |
127 | 9.62M | { |
128 | 9.62M | std::string res(str); |
129 | 9.62M | for (auto &c : res) |
130 | 430M | c=char(std::toupper(c)); |
131 | 9.62M | return res; |
132 | 9.62M | } |
133 | | //! returns the title in uppercase |
134 | | std::string title() const |
135 | 3.21M | { |
136 | 3.21M | return upperCase(m_string); |
137 | 3.21M | } |
138 | | //! returns the two/... first character of the main string in uppercase |
139 | | std::string shortTitle(size_t sz=2) const |
140 | 4.28M | { |
141 | 4.28M | if (m_string.size()<=sz) |
142 | 1.54M | return upperCase(m_string); |
143 | 2.73M | return upperCase(m_string.substr(0,sz)); |
144 | 4.28M | } |
145 | | //! update a font if possible |
146 | | bool updateFont(WPSFont &font) const |
147 | 36.6k | { |
148 | 36.6k | bool ok=true; |
149 | 36.6k | auto str=title(); |
150 | 36.6k | auto sTitle=shortTitle(); |
151 | 36.6k | if (sTitle=="MD") |
152 | 21.0k | { |
153 | 21.0k | if (str=="MDNM") |
154 | 645 | font.m_attributes&=(unsigned int)(WPS_SMALL_CAPS_BIT|WPS_ALL_CAPS_BIT); |
155 | 20.3k | else if (str=="MDBO" || str=="MD+BO") |
156 | 893 | font.m_attributes|=WPS_BOLD_BIT; |
157 | 19.4k | else if (str=="MD-BO") |
158 | 351 | font.m_attributes&=~(unsigned int)(WPS_BOLD_BIT); |
159 | 19.1k | else if (str=="MDBR" || str=="MD+BR") |
160 | 1.59k | font.m_attributes|=(WPS_BOLD_BIT|WPS_ITALICS_BIT); |
161 | 17.5k | else if (str=="MD-BR") |
162 | 439 | font.m_attributes&=~(unsigned int)(WPS_BOLD_BIT|WPS_ITALICS_BIT); |
163 | 17.1k | else if (str=="MDBU" || str=="MD+BU") |
164 | 932 | font.m_attributes|=(WPS_BOLD_BIT|WPS_UNDERLINE_BIT); |
165 | 16.1k | else if (str=="MD-BU") |
166 | 461 | font.m_attributes&=~(unsigned int)(WPS_BOLD_BIT|WPS_UNDERLINE_BIT); |
167 | 15.7k | else if (str=="MDDN" || str=="MD+DN") |
168 | 1.02k | font.m_attributes|=WPS_STRIKEOUT_BIT; |
169 | 14.6k | else if (str=="MD-DN") |
170 | 639 | font.m_attributes&=~(unsigned int)(WPS_STRIKEOUT_BIT); |
171 | 14.0k | else if (str=="MDRV" || str=="MD+IT") |
172 | 681 | font.m_attributes|=WPS_ITALICS_BIT; |
173 | 13.3k | else if (str=="MD-IT") |
174 | 348 | font.m_attributes&=~(unsigned int)(WPS_ITALICS_BIT); |
175 | 13.0k | else if (str=="MD+RV") |
176 | 370 | font.m_attributes|=WPS_REVERSEVIDEO_BIT; |
177 | 12.6k | else if (str=="MD-RV") |
178 | 183 | font.m_attributes&=~(unsigned int)(WPS_REVERSEVIDEO_BIT); |
179 | 12.4k | else if (str=="MDSD" || str=="MD+SD") |
180 | 1.50k | font.m_attributes|=WPS_SUBSCRIPT_BIT; |
181 | 10.9k | else if (str=="MD-SD") |
182 | 407 | font.m_attributes&=~(unsigned int)(WPS_SUBSCRIPT_BIT); |
183 | 10.5k | else if (str=="MDSU" || str=="MD+SU") |
184 | 1.13k | font.m_attributes|=WPS_SUPERSCRIPT_BIT; |
185 | 9.41k | else if (str=="MD-SU") |
186 | 1.13k | font.m_attributes&=~(unsigned int)(WPS_SUPERSCRIPT_BIT); |
187 | 8.28k | else if (str=="MDUL" || str=="MD+UL") |
188 | 703 | font.m_attributes|=WPS_UNDERLINE_BIT; |
189 | 7.58k | else if (str=="MD-UL") |
190 | 327 | font.m_attributes&=~(unsigned int)(WPS_UNDERLINE_BIT); |
191 | 7.25k | else |
192 | 7.25k | ok=false; |
193 | 21.0k | } |
194 | 15.6k | else if (sTitle=="RG") |
195 | 10.0k | { |
196 | 10.0k | if (str=="RG0") |
197 | 1.27k | font.m_attributes&=~(unsigned int)(WPS_SMALL_CAPS_BIT|WPS_ALL_CAPS_BIT); |
198 | 8.74k | else if (str=="RG1") |
199 | 1.76k | { |
200 | 1.76k | font.m_attributes&=~(unsigned int)(WPS_SMALL_CAPS_BIT); |
201 | 1.76k | font.m_attributes|=WPS_ALL_CAPS_BIT; |
202 | 1.76k | } |
203 | 6.97k | else if (str=="RG2") |
204 | 576 | { |
205 | 576 | font.m_attributes&=~(unsigned int)(WPS_ALL_CAPS_BIT); |
206 | 576 | font.m_attributes|=WPS_SMALL_CAPS_BIT; |
207 | 576 | } |
208 | 6.40k | else |
209 | 6.40k | ok=false; |
210 | 10.0k | } |
211 | 5.63k | else if (sTitle=="FG") |
212 | 1.57k | ok=readColor(font.m_color); |
213 | 4.06k | else if (sTitle=="SZ") |
214 | 4.06k | { |
215 | 4.06k | double value; |
216 | 4.06k | bool inPoint; |
217 | 4.06k | std::string extra; |
218 | 4.06k | if (readUnit(str, 2, m_inDosFile, value, inPoint, extra) && inPoint) |
219 | 1.03k | font.m_size=value; |
220 | 4.06k | } |
221 | | |
222 | 36.6k | if (!ok && !m_string.empty()) |
223 | 14.7k | { |
224 | 14.7k | WPS_DEBUG_MSG(("XYWriteParserInternal::Format::updateFont: unknown format=%s\n", m_string.c_str())); |
225 | 14.7k | return false; |
226 | 14.7k | } |
227 | 21.8k | return true; |
228 | 36.6k | } |
229 | | //! update a paragraph if possible |
230 | | bool updateParagraph(WPSParagraph ¶) const |
231 | 65.6k | { |
232 | 65.6k | bool ok=true; |
233 | 65.6k | auto str=title(); |
234 | 65.6k | auto sTitle=shortTitle(); |
235 | 65.6k | double value=0; |
236 | 65.6k | bool inPoint; |
237 | 65.6k | std::string tmp; |
238 | 65.6k | if (str=="FC") |
239 | 238 | para.m_justify=libwps::JustificationCenter; |
240 | 65.4k | else if (str=="FL") |
241 | 401 | para.m_justify=libwps::JustificationLeft; |
242 | 65.0k | else if (str=="FR") |
243 | 384 | para.m_justify=libwps::JustificationRight; |
244 | 64.6k | else if (str=="JU") |
245 | 1.98k | { |
246 | 1.98k | if (para.m_justify==libwps::JustificationLeft) |
247 | 1.41k | para.m_justify=libwps::JustificationFull; |
248 | 1.98k | } |
249 | 62.6k | else if (str=="NJ") |
250 | 1.17k | { |
251 | 1.17k | if (para.m_justify==libwps::JustificationFull) |
252 | 341 | para.m_justify=libwps::JustificationLeft; |
253 | 1.17k | } |
254 | 61.4k | else if (sTitle=="IP" || sTitle=="RM" || sTitle=="LS") |
255 | 10.5k | { |
256 | 10.5k | size_t p=2; |
257 | 10.5k | if (readUnit(str, p, m_inDosFile, value, inPoint, tmp, sTitle!="LS")) |
258 | 8.18k | { |
259 | 8.18k | if (sTitle=="LS") |
260 | 3.67k | para.setInterline(value, (!inPoint || (m_inDosFile && value<=3)) ? librevenge::RVNG_PERCENT : librevenge::RVNG_POINT, WPSParagraph::AtLeast); |
261 | 4.51k | else if (inPoint) |
262 | 3.75k | { |
263 | 3.75k | if (sTitle=="IP") |
264 | 943 | para.m_margins[0]=value/72; |
265 | 2.81k | else if (!m_inDosFile || value<150) // in dos file, size from left |
266 | 2.43k | para.m_margins[2]=value/72; |
267 | 3.75k | } |
268 | 8.18k | if (!tmp.empty()) |
269 | 6.03k | { |
270 | 6.03k | WPS_DEBUG_MSG(("XYWriteParserInternal::Paragraph::updateParagraph: find extra data in %s\n", m_string.c_str())); |
271 | 6.03k | } |
272 | 8.18k | } |
273 | 10.5k | } |
274 | 50.9k | else if (sTitle=="AL") |
275 | 2.31k | { |
276 | 2.31k | if (str=="AL0" || str=="AL1") |
277 | 1.14k | para.setInterline(1, librevenge::RVNG_PERCENT); |
278 | 2.31k | } |
279 | 48.6k | else if (sTitle=="NB" || sTitle=="BB") |
280 | 5.77k | { |
281 | 5.77k | if (str=="NB0" || str=="NB1" || str=="BB") |
282 | 1.63k | para.m_breakStatus=0; |
283 | 4.14k | else if (sTitle=="NB") // no break |
284 | 2.48k | { |
285 | 2.48k | size_t p=2; |
286 | 2.48k | if (readNumber(str, p, value)) |
287 | 1.07k | para.m_breakStatus=libwps::NoBreakWithNextBit; |
288 | 1.41k | else |
289 | 1.41k | ok=false; |
290 | 2.48k | } |
291 | 1.65k | else // allow break + data? |
292 | 1.65k | ok=false; |
293 | 5.77k | } |
294 | 42.8k | else if (sTitle=="LL" || sTitle=="EL") |
295 | 6.00k | { |
296 | | // some extra spacings |
297 | 15.0k | for (size_t i=0; i<=(sTitle=="LL" ? std::min<size_t>(1,m_args.size()) : 0); ++i) |
298 | 9.04k | { |
299 | 9.04k | size_t p=i==0 ? 2 : 0; |
300 | 9.04k | if (readUnit(i==0 ? str : m_args[i-1], p, m_inDosFile, value, inPoint, tmp)) |
301 | 3.21k | { |
302 | 3.21k | if (sTitle=="EL") // after a line |
303 | 420 | continue; |
304 | | // LL after para |
305 | 2.79k | if (i==1) |
306 | 1.20k | para.m_spacings[2]=inPoint ? value/72 : value*12/72; |
307 | | // FIXME update also para spacings 0 when i==0 |
308 | 2.79k | } |
309 | 9.04k | } |
310 | 6.00k | } |
311 | 36.8k | else if (sTitle=="TS") |
312 | 35.2k | { |
313 | 1.45M | for (size_t i=0; i<=m_args.size(); ++i) |
314 | 1.41M | { |
315 | 1.41M | size_t p=i==0 ? 2 : 0; |
316 | 1.41M | WPSTabStop tab; |
317 | 1.41M | if (readUnit(i==0 ? m_string : m_args[i-1], p, m_inDosFile, value, inPoint, tmp)) |
318 | 863k | { |
319 | 863k | if (!inPoint) |
320 | 331 | continue; |
321 | 862k | tmp=upperCase(tmp); |
322 | 862k | if (tmp=="R") |
323 | 12.9k | tab.m_alignment=WPSTabStop::RIGHT; |
324 | 850k | else if (tmp=="C") |
325 | 1.23k | tab.m_alignment=WPSTabStop::CENTER; |
326 | 848k | else if (tmp=="D") |
327 | 2.47k | tab.m_alignment=WPSTabStop::DECIMAL; |
328 | 846k | else |
329 | 846k | tab.m_alignment=WPSTabStop::LEFT; |
330 | 862k | tab.m_position=value/72.; |
331 | 862k | para.m_tabs.push_back(tab); |
332 | 862k | } |
333 | 1.41M | } |
334 | 35.2k | } |
335 | 1.57k | else if (sTitle=="BG") |
336 | 1.57k | ok=readColor(para.m_backgroundColor); |
337 | 0 | else |
338 | 0 | ok=false; |
339 | 65.6k | if (!ok && !m_string.empty()) |
340 | 4.64k | { |
341 | 4.64k | WPS_DEBUG_MSG(("XYWriteParserInternal::Paragraph::updateParagraph: unknown format=%s\n", m_string.c_str())); |
342 | 4.64k | return false; |
343 | 4.64k | } |
344 | 61.0k | return true; |
345 | 65.6k | } |
346 | | //! operator<< |
347 | | friend std::ostream &operator<<(std::ostream &o, Format const &format) |
348 | 0 | { |
349 | 0 | o << format.m_string; |
350 | 0 | if (!format.m_children.empty()) |
351 | 0 | { |
352 | 0 | o << "\n"; |
353 | 0 | for (auto const &child : format.m_children) |
354 | 0 | o << "\t" << child << "\n"; |
355 | 0 | return o; |
356 | 0 | } |
357 | 0 | if (format.m_isComplex && format.m_entry.valid()) |
358 | 0 | o << "[dt=" << format.m_entry.length() << "]"; |
359 | 0 | if (format.m_args.empty()) |
360 | 0 | return o; |
361 | 0 | o << "["; |
362 | 0 | for (auto const &arg : format.m_args) |
363 | 0 | o << arg << ","; |
364 | 0 | o << "]"; |
365 | 0 | return o; |
366 | 0 | } |
367 | | //! flag to know if we are in a dos file |
368 | | bool m_inDosFile; |
369 | | //! the main part |
370 | | std::string m_string; |
371 | | //! the other arguments |
372 | | std::vector<std::string> m_args; |
373 | | //! a text zone entry |
374 | | WPSEntry m_entry; |
375 | | //! a flag to know if this is a complex entry |
376 | | bool m_isComplex; |
377 | | //! the list counter (if known) |
378 | | mutable int m_listCounter; |
379 | | //! the list level (if known) |
380 | | mutable WPSList::Level m_level; |
381 | | //! the list of child (for style, ...) |
382 | | std::vector<Format> m_children; |
383 | | |
384 | | //////////////////////////////// |
385 | | // low level |
386 | | //////////////////////////////// |
387 | | |
388 | | //! try to read a color |
389 | | bool readColor(WPSColor &color) const |
390 | 3.14k | { |
391 | 3.14k | if (m_args.size()!=2) |
392 | 1.44k | { |
393 | 1.44k | WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readColor: bad number of argument\n")); |
394 | 1.44k | return false; |
395 | 1.44k | } |
396 | 1.70k | unsigned char col[3]; |
397 | 4.06k | for (size_t i=0; i<3; ++i) |
398 | 3.62k | { |
399 | 3.62k | size_t p=i==0 ? 2 : 0; |
400 | 3.62k | unsigned int val; |
401 | 3.62k | std::string tmp; |
402 | 3.62k | if (!readUInt(i==0 ? m_string : m_args[i-1], p, val, tmp) || val>255) |
403 | 1.26k | { |
404 | 1.26k | WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readColor: can not read a component\n")); |
405 | 1.26k | return false; |
406 | 1.26k | } |
407 | 2.35k | col[i]=(unsigned char)(val); |
408 | 2.35k | } |
409 | 438 | color=WPSColor(col[0],col[1],col[2]); |
410 | 438 | return true; |
411 | 1.70k | } |
412 | | //! try to read a box of double in points |
413 | | static bool readBox2f(std::string const &str, size_t i, bool inDosFile, WPSBox2f &box, std::string &extra) |
414 | 105k | { |
415 | 105k | Vec2f vec; |
416 | 105k | std::string remain; |
417 | 232k | for (int c=0; c<2; ++c) |
418 | 169k | { |
419 | 169k | if ((c==0 && (!readVec2f(str, i, inDosFile, vec, remain) || remain.substr(0,1)!=" ")) || |
420 | 129k | (c==1 && (!readVec2f(remain, 1, inDosFile, vec, extra)))) |
421 | 42.0k | return false; |
422 | 127k | if (c==0) |
423 | 64.6k | box.setMin(vec); |
424 | 63.1k | else |
425 | 63.1k | box.setMax(vec); |
426 | 127k | } |
427 | 63.1k | return true; |
428 | 105k | } |
429 | | //! try to read a vector of double in points |
430 | | static bool readVec2f(std::string const &str, size_t i, bool inDosFile, Vec2f &vec, std::string &extra) |
431 | 182k | { |
432 | 182k | double value=0; |
433 | 182k | bool inPoint; |
434 | 182k | std::string remain; |
435 | 463k | for (int c=0; c<2; ++c) |
436 | 324k | { |
437 | 324k | if ((c==0 && (!readUnit(str, i, inDosFile, value, inPoint, remain) || remain.substr(0,1)!="x")) || |
438 | 282k | (c==1 && (!readUnit(remain, 1, inDosFile, value, inPoint, extra))) || !inPoint) |
439 | 43.6k | return false; |
440 | 280k | vec[c]=float(value); |
441 | 280k | } |
442 | 139k | return true; |
443 | 182k | } |
444 | | //! try to read a vector of uint |
445 | | static bool readVec2i(std::string const &str, size_t i, Vec2i &vec, std::string &extra) |
446 | 3.93k | { |
447 | 3.93k | unsigned int value=0; |
448 | 3.93k | std::string remain; |
449 | 5.59k | for (int c=0; c<2; ++c) |
450 | 4.81k | { |
451 | 4.81k | size_t p=c==0 ? i : 1; |
452 | 4.81k | if ((c==0 && (!readUInt(str, p, value, remain) || remain.substr(0,1)!="x")) || |
453 | 1.76k | (c==1 && (!readUInt(remain, p, value, extra)))) |
454 | 3.16k | return false; |
455 | 1.65k | vec[c]=int(value); |
456 | 1.65k | } |
457 | 773 | return true; |
458 | 3.93k | } |
459 | | //! try to read a int |
460 | | static bool readUInt(std::string const &str, size_t &i, unsigned int &value, std::string &extra) |
461 | 225k | { |
462 | 225k | auto len=str.size(); |
463 | 225k | value=0; |
464 | 225k | size_t p=i; |
465 | 512k | while (p<len && str[p]>='0' && str[p]<='9') |
466 | 286k | value=value*10+(unsigned int)(str[p++]-'0'); |
467 | 225k | if (p==i) |
468 | 48.7k | return false; |
469 | 177k | i=p; |
470 | 177k | if (i<len) |
471 | 155k | extra=str.substr(i); |
472 | 177k | return true; |
473 | 225k | } |
474 | | //! try to read a double. |
475 | | static bool readNumber(std::string const &str, size_t &i, double &value) |
476 | 2.66M | try |
477 | 2.66M | { |
478 | 2.66M | if (str.size()<=i) |
479 | 1.12M | { |
480 | 1.12M | if (!str.empty()) |
481 | 21.4k | { |
482 | 21.4k | WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readNumber: the string %s is too short\n", str.c_str())); |
483 | 21.4k | } |
484 | 1.12M | return false; |
485 | 1.12M | } |
486 | 1.54M | size_t p; |
487 | 1.54M | if (i==0) |
488 | 1.11M | value = std::stod(str,&p); |
489 | 429k | else |
490 | 429k | value = std::stod(str.substr(i),&p); |
491 | 1.54M | i+=p; |
492 | 1.54M | return true; |
493 | 2.66M | } |
494 | 2.66M | catch (...) |
495 | 2.66M | { |
496 | 284k | if (!str.empty()) |
497 | 284k | { |
498 | 284k | WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readNumber: can not extract number in %s\n", str.c_str())); |
499 | 284k | } |
500 | 284k | return false; |
501 | 284k | } |
502 | | /** try to read an unit, returns a number in Point or in Line. |
503 | | \note if unit=AUTO set value to 72pt=1in |
504 | | */ |
505 | | static bool readUnit(std::string const &str, size_t i, bool inDosFile, double &value, bool &inPoint, std::string &extra, bool dosInChar=true) |
506 | 2.66M | { |
507 | 2.66M | if (str.size()>=i+4 && str.substr(i,4)=="AUTO") |
508 | 2.99k | { |
509 | 2.99k | value=72; |
510 | 2.99k | inPoint=true; |
511 | 2.99k | if (str.size()>i+4) |
512 | 2.57k | extra=str.substr(i+4); |
513 | 2.99k | return true; |
514 | 2.99k | } |
515 | 2.66M | if (!readNumber(str,i,value)) |
516 | 1.40M | return false; |
517 | 1.25M | auto remain=upperCase(str.substr(i,2)); |
518 | 1.25M | if (i+2<str.size()) |
519 | 369k | extra=str.substr(i+2); |
520 | 1.25M | inPoint=true; |
521 | 1.25M | if (inDosFile && remain.empty()) |
522 | 836k | { |
523 | 836k | if (dosInChar) value*=8; |
524 | 836k | return true; |
525 | 836k | } |
526 | 422k | if (remain=="PT") |
527 | 970 | return true; |
528 | 421k | if (remain=="IN") |
529 | 565 | { |
530 | 565 | value *= 72; |
531 | 565 | return true; |
532 | 565 | } |
533 | 420k | if (remain=="CM") |
534 | 1.00k | { |
535 | 1.00k | value *= 72/2.54; |
536 | 1.00k | return true; |
537 | 1.00k | } |
538 | 419k | if (remain=="MM") |
539 | 16.4k | { |
540 | 16.4k | value *= 72/25.4; |
541 | 16.4k | return true; |
542 | 16.4k | } |
543 | 403k | if (remain=="LI") |
544 | 4.87k | { |
545 | 4.87k | inPoint=false; |
546 | 4.87k | return true; |
547 | 4.87k | } |
548 | 398k | if (inDosFile) |
549 | 395k | { |
550 | 395k | if (dosInChar) value*=8; |
551 | 395k | extra=str.substr(i); |
552 | 395k | return true; |
553 | 395k | } |
554 | 2.69k | if (!str.empty()) |
555 | 2.69k | { |
556 | 2.69k | WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readUnit: can not extract unit in %s\n", str.c_str())); |
557 | 2.69k | } |
558 | 2.69k | return false; |
559 | | |
560 | 398k | } |
561 | | }; |
562 | | |
563 | | //! the state of XYWrite |
564 | | struct State |
565 | | { |
566 | | //! constructor |
567 | | explicit State(libwps_tools_win::Font::Type fontType) |
568 | 4.47k | : m_isDosFile(false) |
569 | 4.47k | , m_eof(-1) |
570 | 4.47k | , m_fontType(fontType) |
571 | 4.47k | , m_metaData() |
572 | 4.47k | , m_nameToStyleMap() |
573 | 4.47k | , m_counterToTypeMap() |
574 | 4.47k | , m_counterToValueMap() |
575 | 4.47k | { |
576 | 4.47k | } |
577 | | //! returns the current font type |
578 | | libwps_tools_win::Font::Type getFontType() const |
579 | 345k | { |
580 | 345k | if (m_fontType!=libwps_tools_win::Font::UNKNOWN) |
581 | 0 | return m_fontType; |
582 | | // checkme |
583 | 345k | return m_isDosFile ? libwps_tools_win::Font::CP_437 : |
584 | 345k | libwps_tools_win::Font::WIN3_WEUROPE; |
585 | 345k | } |
586 | | //! flag to know if the file is or not a dos file |
587 | | bool m_isDosFile; |
588 | | //! the last file position |
589 | | long m_eof; |
590 | | //! the user font type |
591 | | libwps_tools_win::Font::Type m_fontType; |
592 | | //! the meta data |
593 | | librevenge::RVNGPropertyList m_metaData; |
594 | | //! map name to style |
595 | | std::map<std::string, Format> m_nameToStyleMap; |
596 | | //! map counter to type |
597 | | std::map<int, libwps::NumberingType> m_counterToTypeMap; |
598 | | //! map counter to value |
599 | | std::map<int, int> m_counterToValueMap; |
600 | | }; |
601 | | |
602 | | //! Internal: the subdocument of a XYWriteParser |
603 | | class SubDocument final : public WPSTextSubDocument |
604 | | { |
605 | | public: |
606 | | //! constructor for a text entry |
607 | | SubDocument(RVNGInputStreamPtr const &input, XYWriteParser &pars, WPSEntry const &entry, std::string const &style="") |
608 | 132k | : WPSTextSubDocument(input, &pars), m_entry(entry), m_style(style) {} |
609 | | //! destructor |
610 | 132k | ~SubDocument() final {} |
611 | | |
612 | | //! operator== |
613 | | bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final |
614 | 3.38M | { |
615 | 3.38M | if (!doc || !WPSTextSubDocument::operator==(doc)) |
616 | 0 | return false; |
617 | 3.38M | auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get()); |
618 | 3.38M | if (!sDoc) return false; |
619 | 3.38M | return m_entry == sDoc->m_entry && m_style == sDoc->m_style; |
620 | 3.38M | } |
621 | | |
622 | | //! the parser function |
623 | | void parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType subDocumentType) final; |
624 | | //! the entry |
625 | | WPSEntry m_entry; |
626 | | //! the style |
627 | | std::string m_style; |
628 | | }; |
629 | | |
630 | | void SubDocument::parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType /*subDocumentType*/) |
631 | 216k | { |
632 | 216k | if (!listener.get()) |
633 | 0 | { |
634 | 0 | WPS_DEBUG_MSG(("XYWriteParserInternal::SubDocument::parse: no listener\n")); |
635 | 0 | return; |
636 | 0 | } |
637 | | |
638 | 216k | if (!m_parser) |
639 | 0 | { |
640 | 0 | listener->insertCharacter(' '); |
641 | 0 | WPS_DEBUG_MSG(("XYWriteParserInternal::SubDocument::parse: bad parser\n")); |
642 | 0 | return; |
643 | 0 | } |
644 | 216k | if (!m_entry.valid() || !m_input) |
645 | 9.55k | { |
646 | 9.55k | listener->insertCharacter(' '); |
647 | 9.55k | return; |
648 | 9.55k | } |
649 | | |
650 | 206k | auto *mnParser = dynamic_cast<XYWriteParser *>(m_parser); |
651 | 206k | if (!mnParser) |
652 | 0 | { |
653 | 0 | WPS_DEBUG_MSG(("XYWriteParserInternal::SubDocument::parse: bad parser...\n")); |
654 | 0 | listener->insertCharacter(' '); |
655 | 0 | return; |
656 | 0 | } |
657 | 206k | long pos=m_input->tell(); |
658 | 206k | mnParser->parseTextZone(m_entry, m_style); |
659 | 206k | m_input->seek(pos, librevenge::RVNG_SEEK_SET); |
660 | 206k | } |
661 | | } |
662 | | |
663 | | // constructor, destructor |
664 | | XYWriteParser::XYWriteParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header, |
665 | | libwps_tools_win::Font::Type encoding) |
666 | 4.47k | : WPSParser(input, header) |
667 | 4.47k | , m_listener() |
668 | 4.47k | , m_state() |
669 | 4.47k | { |
670 | 4.47k | m_state.reset(new XYWriteParserInternal::State(encoding)); |
671 | 4.47k | } |
672 | | |
673 | | XYWriteParser::~XYWriteParser() |
674 | 4.47k | { |
675 | 4.47k | } |
676 | | |
677 | | bool XYWriteParser::checkFilePosition(long pos) const |
678 | 4.47k | { |
679 | 4.47k | if (m_state->m_eof < 0) |
680 | 4.47k | { |
681 | 4.47k | RVNGInputStreamPtr input = const_cast<XYWriteParser *>(this)->getInput(); |
682 | 4.47k | long actPos = input->tell(); |
683 | 4.47k | input->seek(0, librevenge::RVNG_SEEK_END); |
684 | 4.47k | m_state->m_eof=input->tell(); |
685 | 4.47k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
686 | 4.47k | } |
687 | 4.47k | return pos>=0 && pos <= m_state->m_eof; |
688 | 4.47k | } |
689 | | |
690 | | // listener |
691 | | std::shared_ptr<WPSContentListener> XYWriteParser::createListener(librevenge::RVNGTextInterface *interface) |
692 | 4.44k | { |
693 | 4.44k | auto input=getInput(); |
694 | 4.44k | if (!input) |
695 | 0 | throw "Can not find input"; |
696 | 4.44k | std::vector<WPSPageSpan> pageList; |
697 | 4.44k | WPSPageSpan ps; |
698 | 4.44k | ps.setMarginLeft(0.1); |
699 | 4.44k | ps.setMarginRight(0.1); |
700 | 4.44k | ps.setMarginTop(0.1); |
701 | 4.44k | ps.setMarginBottom(0.1); |
702 | 4.44k | input->seek(0, librevenge::RVNG_SEEK_SET); |
703 | 11.0M | while (!input->isEnd() && input->tell()<m_state->m_eof) |
704 | 11.0M | { |
705 | 11.0M | uint8_t c=libwps::readU8(input); |
706 | 11.0M | if (c==0x1a) |
707 | 2.84k | break; |
708 | 11.0M | if (c!=0xae) |
709 | 10.4M | continue; |
710 | 588k | XYWriteParserInternal::Format format; |
711 | 588k | if (!parseFormat(format)) |
712 | 1.46k | break; |
713 | 586k | std::string const str=format.title(); |
714 | 586k | std::string const sTitle=format.shortTitle(); |
715 | 586k | if (str=="PG") |
716 | 129k | pageList.push_back(ps); |
717 | 456k | else if (sTitle=="RH" || sTitle=="RF") |
718 | 56.5k | { |
719 | 56.5k | if (str.size()==2) |
720 | 6.40k | { |
721 | 6.40k | WPSEntry fEntry=format.m_entry; |
722 | 6.40k | long fEnd=fEntry.end(); |
723 | 6.40k | fEntry.setBegin(fEntry.begin()+2); |
724 | 6.40k | fEntry.setEnd(fEnd); |
725 | 6.40k | WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument |
726 | 6.40k | (getInput(), *this,fEntry)); |
727 | 6.40k | ps.setHeaderFooter(sTitle=="RH" ? WPSPageSpan::HEADER : WPSPageSpan::FOOTER, |
728 | 6.40k | WPSPageSpan::ALL, subdoc); |
729 | 6.40k | } |
730 | 50.1k | else if (str.size()>=3 && (str[2]=='A' || str[2]=='E' || str[2]=='O')) |
731 | 13.1k | { |
732 | 13.1k | WPSEntry fEntry=format.m_entry; |
733 | 13.1k | long fEnd=fEntry.end(); |
734 | 13.1k | fEntry.setBegin(fEntry.begin()+3); |
735 | 13.1k | fEntry.setEnd(fEnd); |
736 | 13.1k | WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument |
737 | 13.1k | (getInput(), *this,fEntry)); |
738 | 13.1k | ps.setHeaderFooter(sTitle=="RH" ? WPSPageSpan::HEADER : WPSPageSpan::FOOTER, |
739 | 13.1k | str[2]=='A' ? WPSPageSpan::ALL : str[2]=='E' ? WPSPageSpan::EVEN : WPSPageSpan::ODD, subdoc); |
740 | 13.1k | } |
741 | 56.5k | } |
742 | 400k | else if (sTitle=="PW" || sTitle=="FD") |
743 | 9.59k | { |
744 | | // PB is probably related to the page type A4 paysage==82? |
745 | 9.59k | double val; |
746 | 9.59k | bool inPoint; |
747 | 9.59k | std::string tmp; |
748 | 9.59k | if (format.readUnit(str, 2, m_state->m_isDosFile, val, inPoint, tmp) && inPoint && val>0 && val<100*72) |
749 | 6.96k | { |
750 | 6.96k | if (sTitle=="FD") |
751 | 6.28k | ps.setFormLength(val/72.); |
752 | 679 | else |
753 | 679 | ps.setFormWidth(val/72.); |
754 | 6.96k | } |
755 | 2.63k | else |
756 | 2.63k | { |
757 | 2.63k | WPS_DEBUG_MSG(("XYWriteParser::createListener: can not parse %s\n", format.m_string.c_str())); |
758 | 2.63k | } |
759 | 9.59k | } |
760 | 390k | else if (sTitle=="OF" || sTitle=="TP" || sTitle=="BT") |
761 | 4.53k | { |
762 | 40.0k | for (size_t j=0; j<=format.m_args.size(); ++j) |
763 | 35.5k | { |
764 | 35.5k | size_t p=j==0 ? 2 : 0; |
765 | 35.5k | double value; |
766 | 35.5k | bool inPoint; |
767 | 35.5k | std::string tmp; |
768 | 35.5k | if (format.readUnit(j==0 ? str : format.m_args[j-1], p, m_state->m_isDosFile, value, inPoint, tmp)) |
769 | 8.99k | { |
770 | 8.99k | if (sTitle=="OF" && inPoint) |
771 | 2.89k | { |
772 | 2.89k | if (j==0) |
773 | 1.25k | ps.setMarginLeft(value/72); |
774 | 1.64k | else |
775 | 1.64k | ps.setMarginRight(value/72); |
776 | 2.89k | } |
777 | 6.09k | else if (sTitle=="TP" && inPoint) |
778 | 2.16k | { |
779 | 2.16k | if (j==1) // i==0 bef header |
780 | 538 | ps.setMarginTop(value/72); |
781 | 2.16k | } |
782 | 3.92k | else if (sTitle=="BT" && inPoint) |
783 | 2.76k | { |
784 | 2.76k | if (j==2) // i==0 aft footer, i==1 min, 3 max |
785 | 549 | ps.setMarginBottom(value/72); |
786 | 2.76k | } |
787 | 8.99k | } |
788 | 26.5k | else |
789 | 26.5k | { |
790 | 26.5k | WPS_DEBUG_MSG(("XYWriteParser::createListener: can not parse %s\n", format.m_string.c_str())); |
791 | 26.5k | } |
792 | 35.5k | } |
793 | | |
794 | 4.53k | } |
795 | 586k | } |
796 | 4.44k | pageList.push_back(ps); |
797 | 4.44k | auto listener=std::make_shared<WPSContentListener>(pageList, interface); |
798 | 4.44k | listener->setMetaData(m_state->m_metaData); |
799 | 4.44k | return listener; |
800 | 4.44k | } |
801 | | |
802 | | //////////////////////////////////////////////////////////// |
803 | | // main funtions to parse a document |
804 | | //////////////////////////////////////////////////////////// |
805 | | |
806 | | // main function to parse the document |
807 | | void XYWriteParser::parse(librevenge::RVNGTextInterface *documentInterface) |
808 | 4.47k | { |
809 | 4.47k | RVNGInputStreamPtr input=getInput(); |
810 | 4.47k | if (!input) |
811 | 0 | { |
812 | 0 | WPS_DEBUG_MSG(("XYWriteParser::parse: does not find main input\n")); |
813 | 0 | throw (libwps::ParseException()); |
814 | 0 | } |
815 | 4.47k | if (!checkHeader(nullptr, true)) |
816 | 28 | throw (libwps::ParseException()); |
817 | | |
818 | 4.44k | ascii().setStream(input); |
819 | 4.44k | ascii().open("MN0"); |
820 | 4.44k | try |
821 | 4.44k | { |
822 | 4.44k | if (!m_state->m_isDosFile && !findAllZones()) |
823 | 1 | throw (libwps::ParseException()); |
824 | 4.44k | m_listener=createListener(documentInterface); |
825 | 4.44k | if (!m_listener) |
826 | 0 | { |
827 | 0 | WPS_DEBUG_MSG(("XYWriteParser::parse: can not create the listener\n")); |
828 | 0 | throw (libwps::ParseException()); |
829 | 0 | } |
830 | 4.44k | m_listener->startDocument(); |
831 | 4.44k | WPSEntry entry; |
832 | 4.44k | entry.setBegin(0); |
833 | 4.44k | entry.setEnd(m_state->m_eof); |
834 | 4.44k | parseTextZone(entry); |
835 | 4.44k | m_listener->endDocument(); |
836 | 4.44k | } |
837 | 4.44k | catch (...) |
838 | 4.44k | { |
839 | 1.46k | WPS_DEBUG_MSG(("XYWriteParser::parse: exception catched when parsing the main document\n")); |
840 | 1.46k | throw (libwps::ParseException()); |
841 | 1.46k | } |
842 | | |
843 | 2.97k | m_listener.reset(); |
844 | | |
845 | 2.97k | ascii().reset(); |
846 | 2.97k | } |
847 | | |
848 | | bool XYWriteParser::findAllZones() |
849 | 228 | { |
850 | 228 | RVNGInputStreamPtr input = getInput(); |
851 | 228 | if (!input) |
852 | 0 | { |
853 | 0 | WPS_DEBUG_MSG(("XYWriteParser::findAllZones: can not find the input\n")); |
854 | 0 | return false; |
855 | 0 | } |
856 | 228 | input->seek(0, librevenge::RVNG_SEEK_SET); |
857 | 228 | bool ok=false; |
858 | 830k | while (!input->isEnd()) |
859 | 830k | { |
860 | 830k | if (libwps::readU8(input)==0x1a) |
861 | 227 | { |
862 | 227 | ok=true; |
863 | 227 | break; |
864 | 227 | } |
865 | 830k | } |
866 | 228 | if (!ok) |
867 | 1 | { |
868 | 1 | WPS_DEBUG_MSG(("XYWriteParser::findAllZones: can not find the end main zone marker\n")); |
869 | 1 | return false; |
870 | 1 | } |
871 | 227 | long endZone1=input->tell(); |
872 | 4.82M | while (!input->isEnd()) |
873 | 4.82M | { |
874 | 4.82M | if (libwps::readU8(input)==0x1a) |
875 | 199 | { |
876 | 199 | WPSEntry entry; |
877 | 199 | entry.setBegin(endZone1); |
878 | 199 | entry.setEnd(input->tell()); |
879 | 199 | parseMetaData(entry); |
880 | 199 | input->seek(entry.end(), librevenge::RVNG_SEEK_SET); |
881 | 199 | break; |
882 | 199 | } |
883 | 4.82M | } |
884 | | // now normally 22 02 fe fc fe 01 00 |
885 | 227 | if (m_state->m_eof!=input->tell()+7) |
886 | 182 | { |
887 | 182 | WPS_DEBUG_MSG(("XYWriteParser::findAllZones: end of file seems bad\n")); |
888 | 182 | } |
889 | | // ok, update the end of the main zone and returns |
890 | 227 | m_state->m_eof=endZone1-1; |
891 | 227 | return true; |
892 | 228 | } |
893 | | |
894 | | bool XYWriteParser::update(XYWriteParserInternal::Format const &format, libwps_tools_win::Font::Type &fontType) const |
895 | 1.11M | { |
896 | 1.11M | if (!m_listener) |
897 | 0 | throw "no listener"; |
898 | 1.11M | std::string const str=format.title(); |
899 | 1.11M | std::string const sTitle=format.shortTitle(); |
900 | 1.11M | if (sTitle=="MD" || sTitle=="RG" || sTitle=="SZ" || |
901 | 1.07M | sTitle=="FG") |
902 | 36.6k | { |
903 | 36.6k | auto font=m_listener->getFont(); |
904 | 36.6k | if (!format.updateFont(font)) |
905 | 14.7k | return false; |
906 | 21.8k | m_listener->setFont(font); |
907 | 21.8k | } |
908 | 1.07M | else if (sTitle=="UF" && str.size()>2) |
909 | 42.7k | { |
910 | 42.7k | auto font=m_listener->getFont(); |
911 | 42.7k | font.m_name=libwps_tools_win::Font::unicodeString(format.m_string.substr(2), m_state->getFontType()); |
912 | 42.7k | auto newType=libwps_tools_win::Font::getFontType(font.m_name); |
913 | 42.7k | if (newType!=libwps_tools_win::Font::UNKNOWN) |
914 | 21.2k | fontType=newType; |
915 | 42.7k | m_listener->setFont(font); |
916 | 42.7k | } |
917 | 1.03M | else if (str=="FC" || str=="FL" || str=="FR" || |
918 | 1.03M | str=="JU" || str=="NJ" || |
919 | 1.02M | sTitle=="IP" || sTitle=="RM" || |
920 | 1.02M | sTitle=="AL" || sTitle=="LS" || sTitle=="BB" || sTitle=="NB" || |
921 | 1.00M | sTitle=="EL" || sTitle=="LL" || sTitle=="TS" || |
922 | 968k | sTitle=="BG") |
923 | 65.6k | { |
924 | 65.6k | auto paragraph=m_listener->getParagraph(); |
925 | 65.6k | if (!format.updateParagraph(paragraph)) |
926 | 4.64k | return false; |
927 | 61.0k | m_listener->setParagraph(paragraph); |
928 | 61.0k | } |
929 | 967k | else if (sTitle=="LM") |
930 | 39.6k | { |
931 | 39.6k | size_t p=2; |
932 | 39.6k | double value; |
933 | 39.6k | std::string tmp; |
934 | 39.6k | bool inPoint=false; |
935 | 39.6k | if (!format.readUnit(str, p, m_state->m_isDosFile, value, inPoint, tmp)) |
936 | 2.00k | return false; |
937 | 37.6k | auto paragraph=m_listener->getParagraph(); |
938 | 37.6k | if (inPoint) |
939 | 37.1k | paragraph.m_margins[1]=value/72; |
940 | 37.6k | if (format.m_listCounter==-1 && !tmp.empty()&&format.m_entry.valid()) |
941 | 34.4k | { |
942 | 34.4k | format.m_listCounter=-2; |
943 | | // look for a counter |
944 | 34.4k | auto THIS=const_cast<XYWriteParser *>(this); |
945 | 34.4k | auto input=THIS->getInput(); |
946 | 34.4k | if (!input) |
947 | 0 | throw "XYWriteParser::update: no input"; |
948 | 34.4k | long actPos=input->tell(); |
949 | 34.4k | bool ok=false; |
950 | 34.4k | input->seek(format.m_entry.begin(), librevenge::RVNG_SEEK_SET); |
951 | 6.46M | while (!input->isEnd() && input->tell()<format.m_entry.end()) |
952 | 6.45M | { |
953 | 6.45M | if (libwps::readU8(input)==';') |
954 | 29.2k | { |
955 | 29.2k | ok=true; |
956 | 29.2k | break; |
957 | 29.2k | } |
958 | 6.45M | } |
959 | 34.4k | if (ok) |
960 | 29.2k | { |
961 | 29.2k | ok=false; |
962 | 29.2k | auto defType=m_state->getFontType(); |
963 | 13.1M | while (!input->isEnd() && input->tell()<format.m_entry.end()) |
964 | 13.1M | { |
965 | 13.1M | auto ch=libwps::readU8(input); |
966 | 13.1M | if (ch!=0xae) |
967 | 13.0M | { |
968 | 13.0M | if (ch=='\t') ch=' '; // replace tabulations by space |
969 | 13.0M | libwps::appendUnicode(uint32_t(libwps_tools_win::Font::unicode(ch, defType)), |
970 | 13.0M | format.m_listCounter >= 0 ? format.m_level.m_suffix : |
971 | 13.0M | format.m_level.m_prefix); |
972 | 13.0M | continue; |
973 | 13.0M | } |
974 | 61.7k | XYWriteParserInternal::Format newFormat; |
975 | 61.7k | if (!THIS->parseFormat(newFormat)) |
976 | 690 | break; |
977 | 61.0k | p=1; |
978 | 61.0k | unsigned int val; |
979 | 61.0k | std::string const newFStr=newFormat.title(); |
980 | 61.0k | std::string extra; |
981 | 61.0k | if (!newFStr.empty() && newFStr[0]=='C' && |
982 | 29.3k | newFormat.readUInt(newFStr, p, val, extra)) |
983 | 28.4k | { |
984 | 28.4k | if (format.m_listCounter>=0) |
985 | 4.51k | { |
986 | 4.51k | format.m_level.m_prefix.clear(); |
987 | 4.51k | format.m_level.m_suffix.clear(); |
988 | 4.51k | } |
989 | 28.4k | format.m_listCounter=int(val); |
990 | 28.4k | auto it=m_state->m_counterToTypeMap.find(format.m_listCounter); |
991 | 28.4k | if (it!=m_state->m_counterToTypeMap.end()) |
992 | 20.3k | { |
993 | 20.3k | format.m_level.m_type=it->second; |
994 | 20.3k | } |
995 | 8.15k | else |
996 | 8.15k | { |
997 | 8.15k | WPS_DEBUG_MSG(("XYWriteParser::update: can not find counter %d\n", format.m_listCounter)); |
998 | 8.15k | } |
999 | 28.4k | ok=true; |
1000 | 28.4k | } |
1001 | 32.6k | else |
1002 | 32.6k | { |
1003 | | #ifdef DEBUG |
1004 | | std::cerr << "XYWriteParser::update[LM]: unused\n"; |
1005 | | std::cerr << "\t" << newFormat << "\n"; |
1006 | | #endif |
1007 | 32.6k | } |
1008 | 61.0k | } |
1009 | 29.2k | } |
1010 | 34.4k | if (!ok) |
1011 | 10.4k | { |
1012 | | #ifdef DEBUG |
1013 | | std::cerr << "XYWriteParser::update[LM]: unused\n"; |
1014 | | std::cerr << "\t" << format << "\n"; |
1015 | | #endif |
1016 | 10.4k | } |
1017 | 34.4k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
1018 | 34.4k | } |
1019 | 37.6k | if (!format.m_level.isDefault()) |
1020 | 18.9k | { |
1021 | 18.9k | format.m_level.m_labelIndent=paragraph.m_margins[1]; |
1022 | 18.9k | paragraph.m_listLevel=format.m_level; |
1023 | 18.9k | paragraph.m_listLevelIndex=1; |
1024 | 18.9k | paragraph.m_margins[1]=0; |
1025 | 18.9k | } |
1026 | 37.6k | m_listener->setParagraph(paragraph); |
1027 | 37.6k | } |
1028 | 927k | else |
1029 | 927k | return false; |
1030 | 163k | return true; |
1031 | 1.11M | } |
1032 | | |
1033 | | bool XYWriteParser::parseTextZone(WPSEntry const &entry, std::string const &styleName) |
1034 | 318k | { |
1035 | 318k | RVNGInputStreamPtr input = getInput(); |
1036 | 318k | if (!input || !m_listener) |
1037 | 0 | throw (libwps::ParseException()); |
1038 | 318k | if (!entry.valid()) |
1039 | 44.6k | return true; |
1040 | 273k | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
1041 | 273k | auto fontType=m_state->getFontType(); |
1042 | 273k | WPSFont defFont; |
1043 | 273k | defFont.m_name="Courier New"; |
1044 | 273k | defFont.m_size=10; |
1045 | 273k | m_listener->setFont(defFont); |
1046 | 273k | if (!styleName.empty()) |
1047 | 22.9k | { |
1048 | 22.9k | auto it=m_state->m_nameToStyleMap.find(styleName); |
1049 | 22.9k | if (it!=m_state->m_nameToStyleMap.end()) |
1050 | 2.33k | { |
1051 | 2.33k | for (auto const &child : it->second.m_children) |
1052 | 6.35k | { |
1053 | 6.35k | std::string const sTitle=child.shortTitle(); |
1054 | 6.35k | if (!update(child, fontType) && sTitle!="FT" && sTitle!="BF") // checkme: FT and BF related to footnote |
1055 | 4.48k | { |
1056 | | #ifdef DEBUG |
1057 | | std::cerr << "XYWriteParser::parseTextZone[child]: unused\n"; |
1058 | | std::cerr << "\t" << child << "\n"; |
1059 | | #endif |
1060 | 4.48k | } |
1061 | 6.35k | } |
1062 | 2.33k | } |
1063 | 22.9k | } |
1064 | 30.2M | while (!input->isEnd() && input->tell()<entry.end()) |
1065 | 29.9M | { |
1066 | 29.9M | uint8_t c=libwps::readU8(input); |
1067 | 29.9M | if (c==0x1a) |
1068 | 6.51k | { |
1069 | 6.51k | if (input->tell()<entry.end()) |
1070 | 3.96k | { |
1071 | 3.96k | WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: find unexpected end zone\n")); |
1072 | 3.96k | } |
1073 | 6.51k | break; |
1074 | 6.51k | } |
1075 | 29.9M | if (c==0xae) |
1076 | 1.08M | { |
1077 | 1.08M | XYWriteParserInternal::Format format; |
1078 | 1.08M | if (!parseFormat(format)) |
1079 | 5.21k | throw (libwps::ParseException()); |
1080 | 1.08M | std::string const str=format.title(); |
1081 | 1.08M | std::string const sTitle=format.shortTitle(); |
1082 | 1.08M | bool done=true; |
1083 | 1.08M | if (update(format, fontType)) |
1084 | 161k | ; |
1085 | 919k | else if (sTitle=="DC" && str.size()>2) // DCxxx=[1iIaA]... |
1086 | 23.2k | { |
1087 | 23.2k | size_t p=2; |
1088 | 23.2k | unsigned int val; |
1089 | 23.2k | std::string extra; |
1090 | 23.2k | if (!format.readUInt(format.m_string, p, val, extra) || !format.m_entry.valid() || |
1091 | 22.0k | extra.size()<=1 || extra[0]!='=' || |
1092 | 17.1k | m_state->m_counterToTypeMap.find(int(val)) != m_state->m_counterToTypeMap.end()) |
1093 | 17.8k | done=false; |
1094 | 5.39k | else |
1095 | 5.39k | { |
1096 | 5.39k | std::map<char, libwps::NumberingType> const cToTypeMap= |
1097 | 5.39k | { |
1098 | 5.39k | {'1', libwps::ARABIC}, {'a', libwps::LOWERCASE}, {'A', libwps::UPPERCASE}, {'i', libwps::LOWERCASE_ROMAN}, {'I', libwps::UPPERCASE_ROMAN} |
1099 | 5.39k | }; |
1100 | 5.39k | auto it=cToTypeMap.find(extra[1]); |
1101 | 5.39k | if (it==cToTypeMap.end()) |
1102 | 4.31k | { |
1103 | 4.31k | WPS_DEBUG_MSG(("XYWriteParserInternal::Format::parseTextZone: can not decode counter format in %s\n", extra.c_str())); |
1104 | 4.31k | } |
1105 | 1.07k | else |
1106 | 1.07k | m_state->m_counterToTypeMap[int(val)]=it->second; |
1107 | 5.39k | } |
1108 | 23.2k | } |
1109 | 896k | else if (sTitle=="SS") |
1110 | 43.3k | { |
1111 | 43.3k | if (createFormatChildren(format,format.m_string.size()+1) && format.m_string.size()>2) |
1112 | 42.2k | m_state->m_nameToStyleMap[format.m_string.substr(2)]=format; |
1113 | 1.04k | else |
1114 | 1.04k | done=false; |
1115 | 43.3k | } |
1116 | 853k | else if (sTitle=="FM" && format.m_string.size()>2 && |
1117 | 18.6k | (format.m_string[2]>='1' && format.m_string[2]<='3')) |
1118 | 15.4k | { |
1119 | 15.4k | if (createFormatChildren(format,3)) |
1120 | 15.1k | { |
1121 | 15.1k | std::string name("__"); |
1122 | 15.1k | name+=format.m_string.substr(0,3); |
1123 | 15.1k | m_state->m_nameToStyleMap[name]=format; |
1124 | 15.1k | } |
1125 | 306 | else |
1126 | 306 | done=false; |
1127 | 15.4k | } |
1128 | 837k | else if (sTitle=="US" && format.m_string.size()>2) |
1129 | 13.2k | { |
1130 | 13.2k | auto it=m_state->m_nameToStyleMap.find(format.m_string.substr(2)); |
1131 | 13.2k | if (it==m_state->m_nameToStyleMap.end()) |
1132 | 9.76k | { |
1133 | 9.76k | WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: can not find style %s\n", format.m_string.c_str())); |
1134 | 9.76k | } |
1135 | 3.46k | else |
1136 | 3.46k | { |
1137 | 3.46k | if (format.m_string.size()>4) |
1138 | 1.30k | { |
1139 | | // unsure, when we need to reset the style |
1140 | 1.30k | m_listener->setFont(defFont); |
1141 | 1.30k | m_listener->setParagraph(WPSParagraph()); |
1142 | 1.30k | } |
1143 | 3.46k | for (auto const &child : it->second.m_children) |
1144 | 24.9k | { |
1145 | 24.9k | if (!update(child, fontType)) |
1146 | 24.1k | { |
1147 | | #ifdef DEBUG |
1148 | | std::cerr << "XYWriteParser::parseTextZone[child]: unused\n"; |
1149 | | std::cerr << "\t" << child << "\n"; |
1150 | | #endif |
1151 | 24.1k | } |
1152 | 24.9k | } |
1153 | 3.46k | } |
1154 | 13.2k | } |
1155 | 824k | else if (sTitle=="FA") |
1156 | 23.9k | { |
1157 | 23.9k | if (createFormatChildren(format)) |
1158 | 22.2k | parseFrameZone(format); |
1159 | 1.65k | else |
1160 | 1.65k | done=false; |
1161 | 23.9k | } |
1162 | 800k | else if (sTitle=="IG") |
1163 | 84.2k | { |
1164 | 84.2k | if (createFormatChildren(format,format.m_string.size()+1)) |
1165 | 83.6k | parsePictureZone(format); |
1166 | 602 | else |
1167 | 602 | done=false; |
1168 | 84.2k | } |
1169 | 716k | else if (sTitle=="NT" && format.m_string.size()>2 && format.m_entry.valid()) |
1170 | 22.6k | { |
1171 | 22.6k | WPSEntry fEntry=format.m_entry; |
1172 | 22.6k | long fEnd=fEntry.end(); |
1173 | 22.6k | fEntry.setBegin(fEntry.begin()+2); |
1174 | 22.6k | fEntry.setEnd(fEnd); |
1175 | 22.6k | WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument |
1176 | 22.6k | (getInput(), *this,fEntry)); |
1177 | 22.6k | m_listener->insertComment(subdoc); |
1178 | 22.6k | } |
1179 | 693k | else if (sTitle=="FN") |
1180 | 20.4k | { |
1181 | 20.4k | bool hasId=format.m_string.size()>2 && |
1182 | 16.0k | (format.m_string[2]>='1' && format.m_string[2]<='3'); |
1183 | 20.4k | WPSEntry fEntry=format.m_entry; |
1184 | 20.4k | long fEnd=fEntry.end(); |
1185 | 20.4k | fEntry.setBegin(fEntry.begin()+(hasId ? 3 : 2)); |
1186 | 20.4k | fEntry.setEnd(fEnd); |
1187 | 20.4k | std::string sName("__FM"); |
1188 | 20.4k | sName+=format.m_string[2]; |
1189 | 20.4k | WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument |
1190 | 20.4k | (getInput(), *this, fEntry, hasId ? sName : "")); |
1191 | 20.4k | m_listener->insertNote(WPSContentListener::FOOTNOTE, subdoc); |
1192 | 20.4k | } |
1193 | 673k | else if (str=="PG") |
1194 | 221k | m_listener->insertBreak(WPS_PAGE_BREAK); |
1195 | 451k | else if (str=="PN") |
1196 | 1.84k | m_listener->insertField(WPSField(WPSField::PageNumber)); |
1197 | 450k | else if (str=="DA" || str=="TM") // TM date custom format ? |
1198 | 865 | m_listener->insertField(WPSField(WPSField::Date)); |
1199 | 449k | else if (str=="TI") |
1200 | 739 | m_listener->insertField(WPSField(WPSField::Time)); |
1201 | 448k | else if (sTitle=="CT") |
1202 | 16.8k | { |
1203 | 16.8k | long actPos=input->tell(); |
1204 | 16.8k | if (!createTable(format, entry.end())) |
1205 | 11.1k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
1206 | 16.8k | } |
1207 | 431k | else if ((sTitle=="RH" || sTitle=="RF") && // header/footer already parsed |
1208 | 64.8k | (str.size()==2 || |
1209 | 56.3k | (str.size()>=3 && (str[2]=='A'||str[2]=='E'||str[2]=='O')))) |
1210 | 26.3k | ; |
1211 | 405k | else if (sTitle=="PW" || sTitle=="FD" || sTitle=="PB" || sTitle=="OF" || sTitle=="TP" || sTitle=="BT" || // page dimension |
1212 | 383k | format.shortTitle(3)=="UBN" || sTitle=="GU" || sTitle=="EE" || sTitle=="ET" || // related to table (checkme) |
1213 | 371k | sTitle=="SY" || // other method to define a font |
1214 | 371k | sTitle=="NF") // footnote number |
1215 | 33.4k | ; |
1216 | 371k | else if (sTitle=="LB" || sTitle=="RE") |
1217 | 1.67k | { |
1218 | | // LBname: label |
1219 | | // RE[PCF]name: ref to page, chapter, reference |
1220 | 1.67k | static bool first=true; |
1221 | 1.67k | if (first) |
1222 | 1.67k | { |
1223 | 1.67k | WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: retrieving label/cross ref is not implemented\n")); |
1224 | 1.67k | } |
1225 | 1.67k | } |
1226 | 370k | else if (format.shortTitle(1)=="C") |
1227 | 50.1k | { |
1228 | 50.1k | unsigned int value; |
1229 | 50.1k | size_t p=1; |
1230 | 50.1k | std::string tmp; |
1231 | 50.1k | if (!format.readUInt(str, p, value, tmp)) |
1232 | 42.3k | done=false; |
1233 | 7.87k | else |
1234 | 7.87k | { |
1235 | 7.87k | int actValue=0; |
1236 | 7.87k | auto it=m_state->m_counterToValueMap.find(int(value)); |
1237 | 7.87k | if (it!=m_state->m_counterToValueMap.end()) |
1238 | 6.81k | actValue=it->second; |
1239 | 7.87k | ++actValue; |
1240 | 7.87k | std::stringstream s; |
1241 | 7.87k | s << actValue; |
1242 | 7.87k | for (auto res : s.str()) |
1243 | 13.4k | m_listener->insertCharacter(uint8_t(res)); |
1244 | 7.87k | m_state->m_counterToValueMap[int(value)]=actValue; |
1245 | 7.87k | } |
1246 | 50.1k | } |
1247 | 319k | else |
1248 | 319k | done=false; |
1249 | 1.08M | if (!done) |
1250 | 383k | { |
1251 | | #ifdef DEBUG |
1252 | | std::cerr << "XYWriteParser::parseTextZone: unused\n"; |
1253 | | std::cerr << "\t" << format << "\n"; |
1254 | | #endif |
1255 | 383k | } |
1256 | 1.08M | continue; |
1257 | 1.08M | } |
1258 | 28.8M | if (c==0xff && input->tell()+2<entry.end()) |
1259 | 1.25M | { |
1260 | | // special case a char in binary... |
1261 | 1.25M | long pos=input->tell(); |
1262 | 1.25M | c=0; |
1263 | 1.25M | bool ok=true; |
1264 | 1.36M | for (int i=0; i<2; ++i) |
1265 | 1.32M | { |
1266 | 1.32M | auto ch=libwps::readU8(input); |
1267 | 1.32M | if (ch>='A' && ch<='F') |
1268 | 52.2k | c=(unsigned char)(c*16+(ch-'A'+10)); |
1269 | 1.26M | else if (ch>='0' && ch<='9') |
1270 | 62.1k | c=(unsigned char)(c*16+(ch-'0')); |
1271 | 1.20M | else |
1272 | 1.20M | { |
1273 | 1.20M | ok=false; |
1274 | 1.20M | break; |
1275 | 1.20M | } |
1276 | 1.32M | } |
1277 | 1.25M | if (!ok) |
1278 | 1.20M | { |
1279 | 1.20M | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1280 | 1.20M | WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: find bad char FF in pos=%lx\n", (unsigned long)(input->tell()))); |
1281 | 1.20M | continue; |
1282 | 1.20M | } |
1283 | 1.25M | } |
1284 | 27.6M | switch (c) |
1285 | 27.6M | { |
1286 | 35.4k | case 0x9: |
1287 | 35.4k | m_listener->insertTab(); |
1288 | 35.4k | break; |
1289 | 56.9k | case 0xa: |
1290 | 56.9k | break; |
1291 | 240k | case 0xd: |
1292 | 240k | { |
1293 | | // we must reset the list level to 0 |
1294 | 240k | if (m_listener->getCurrentList()) |
1295 | 20.9k | { |
1296 | 20.9k | auto paragraph=m_listener->getParagraph(); |
1297 | 20.9k | if (paragraph.m_listLevelIndex>0) |
1298 | 7.46k | { |
1299 | 7.46k | paragraph.m_margins[1]=paragraph.m_listLevel.m_labelIndent; |
1300 | 7.46k | paragraph.m_listLevelIndex=0; |
1301 | 7.46k | m_listener->setParagraph(paragraph); |
1302 | 7.46k | } |
1303 | 20.9k | } |
1304 | 240k | m_listener->insertEOL(); |
1305 | 240k | break; |
1306 | 0 | } |
1307 | 27.3M | default: |
1308 | 27.3M | if (c<0x1f || ((c==0xaf || c==0xfa) && input->tell() != entry.begin()+1 && input->tell() != entry.end())) |
1309 | 3.03M | { |
1310 | 3.03M | WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: find bad char %x in pos=%lx\n", unsigned(c), (unsigned long)(input->tell()))); |
1311 | 3.03M | } |
1312 | 24.3M | else |
1313 | 24.3M | m_listener->insertUnicode(uint32_t(libwps_tools_win::Font::unicode(c, fontType))); |
1314 | 27.6M | } |
1315 | 27.6M | } |
1316 | 268k | return true; |
1317 | 273k | } |
1318 | | |
1319 | | bool XYWriteParser::parseFrameZone(XYWriteParserInternal::Format const &frameFormat) |
1320 | 22.2k | { |
1321 | 22.2k | RVNGInputStreamPtr input = getInput(); |
1322 | 22.2k | if (!input || !m_listener ||!frameFormat.m_entry.valid()) |
1323 | 0 | throw (libwps::ParseException()); |
1324 | 22.2k | Vec2f dim; |
1325 | 22.2k | WPSEntry textEntry; |
1326 | 22.2k | for (auto const &child : frameFormat.m_children) |
1327 | 66.1k | { |
1328 | 66.1k | std::string const sTitle=child.shortTitle(); |
1329 | 66.1k | bool done=false; |
1330 | 66.1k | if (sTitle=="SI") |
1331 | 12.9k | { |
1332 | 12.9k | std::string tmp; |
1333 | 12.9k | done=child.readVec2f(child.m_string, 2, m_state->m_isDosFile, dim, tmp); |
1334 | 12.9k | } |
1335 | 53.2k | else if (sTitle=="LB") // label are not implement |
1336 | 317 | done=true; |
1337 | 52.9k | else if (sTitle=="PO") // find POTMxPC;data |
1338 | 10.5k | { |
1339 | 10.5k | textEntry=child.m_entry; |
1340 | 10.5k | done=true; |
1341 | 10.5k | } |
1342 | 66.1k | if (!done) |
1343 | 45.5k | { |
1344 | | #ifdef DEBUG |
1345 | | std::cerr << "XYWriteParser::parseFrameZone: unused\n"; |
1346 | | std::cerr << "\t" << child << "\n"; |
1347 | | #endif |
1348 | 45.5k | } |
1349 | 66.1k | } |
1350 | 22.2k | if (dim[0]<=0 || dim[1]<=0 || !textEntry.valid()) |
1351 | 13.7k | { |
1352 | 13.7k | WPS_DEBUG_MSG(("XYWriteParser::parseFrameZone: can not find frame data\n")); |
1353 | 13.7k | return false; |
1354 | 13.7k | } |
1355 | 8.54k | long begPos=input->tell(); |
1356 | | |
1357 | 8.54k | long endPos=textEntry.end(); |
1358 | 8.54k | input->seek(textEntry.begin(), librevenge::RVNG_SEEK_SET); |
1359 | 602k | while (!input->isEnd() && input->tell()<endPos) |
1360 | 601k | { |
1361 | 601k | if (libwps::readU8(input)==';') |
1362 | 7.33k | break; |
1363 | 601k | } |
1364 | 8.54k | textEntry.setBegin(input->tell()); |
1365 | 8.54k | textEntry.setEnd(endPos); |
1366 | 8.54k | WPSPosition fPos(Vec2f(), dim, librevenge::RVNG_POINT); |
1367 | 8.54k | fPos.setRelativePosition(WPSPosition::Char); |
1368 | 8.54k | WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument |
1369 | 8.54k | (getInput(), *this, textEntry)); |
1370 | 8.54k | m_listener->insertTextBox(fPos, subdoc); |
1371 | 8.54k | input->seek(begPos, librevenge::RVNG_SEEK_SET); |
1372 | 8.54k | return true; |
1373 | 22.2k | } |
1374 | | |
1375 | | bool XYWriteParser::parsePictureZone(XYWriteParserInternal::Format const &pictureFormat) |
1376 | 83.6k | { |
1377 | 83.6k | RVNGInputStreamPtr input = getInput(); |
1378 | 83.6k | if (!input || !m_listener || !pictureFormat.m_entry.valid()) |
1379 | 0 | throw (libwps::ParseException()); |
1380 | 83.6k | WPSBox2f box; |
1381 | 83.6k | Vec2i scaleArray(100,100); |
1382 | 83.6k | for (auto const &child : pictureFormat.m_children) |
1383 | 293k | { |
1384 | 293k | std::string const sTitle=child.shortTitle(); |
1385 | 293k | bool done=false; |
1386 | 293k | if (sTitle=="CR") |
1387 | 105k | { |
1388 | 105k | std::string tmp; |
1389 | 105k | done=child.readBox2f(child.m_string, 2, m_state->m_isDosFile, box, tmp); |
1390 | 105k | } |
1391 | 188k | else if (sTitle=="TY" || child.m_string=="IML" || sTitle=="RV") // type, unknown, revision? |
1392 | 1.16k | done=true; |
1393 | 187k | else if (sTitle=="SC") |
1394 | 3.93k | { |
1395 | 3.93k | std::string tmp; |
1396 | 3.93k | done=child.readVec2i(child.m_string, 2, scaleArray, tmp); |
1397 | 3.93k | } |
1398 | 293k | if (!done) |
1399 | 228k | { |
1400 | | #ifdef DEBUG |
1401 | | std::cerr << "XYWriteParser::parsePictureZone: unused\n"; |
1402 | | std::cerr << "\t" << child << "\n"; |
1403 | | #endif |
1404 | 228k | } |
1405 | 293k | } |
1406 | 83.6k | Vec2f dim(static_cast<float>(scaleArray[0])/100.f*box.size()[0], float(scaleArray[1])/100.f*box.size()[1]); |
1407 | 83.6k | if (dim[0]<=0 || dim[1]<=0) |
1408 | 21.7k | { |
1409 | 21.7k | WPS_DEBUG_MSG(("XYWriteParser::parsePictureZone: can not find picture dimension\n")); |
1410 | 21.7k | return false; |
1411 | 21.7k | } |
1412 | | |
1413 | 61.8k | long begPos=input->tell(); |
1414 | 61.8k | long endPos=pictureFormat.m_entry.end(); |
1415 | 61.8k | input->seek(pictureFormat.m_entry.begin()+2, librevenge::RVNG_SEEK_SET); |
1416 | 241M | while (!input->isEnd() && input->tell()<endPos) |
1417 | 241M | { |
1418 | 241M | if (libwps::readU8(input)==',') |
1419 | 49.0k | break; |
1420 | 241M | } |
1421 | 61.8k | WPSEntry textEntry; |
1422 | 61.8k | textEntry.setBegin(pictureFormat.m_entry.begin()+2); |
1423 | 61.8k | textEntry.setEnd(input->tell()-1); |
1424 | 61.8k | WPSPosition fPos(Vec2f(), dim, librevenge::RVNG_POINT); |
1425 | 61.8k | fPos.setRelativePosition(WPSPosition::Char); |
1426 | 61.8k | WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument |
1427 | 61.8k | (getInput(), *this, textEntry)); |
1428 | 61.8k | m_listener->insertTextBox(fPos, subdoc); |
1429 | 61.8k | input->seek(begPos, librevenge::RVNG_SEEK_SET); |
1430 | 61.8k | return true; |
1431 | 83.6k | } |
1432 | | |
1433 | | bool XYWriteParser::createTable(XYWriteParserInternal::Format const &tableFormat, long endPos) |
1434 | 16.8k | { |
1435 | 16.8k | RVNGInputStreamPtr input = getInput(); |
1436 | 16.8k | if (!input || !m_listener || tableFormat.shortTitle()!="CT") |
1437 | 0 | throw (libwps::ParseException()); |
1438 | 16.8k | long begPos=input->tell(); |
1439 | 16.8k | if (begPos>=endPos) |
1440 | 680 | { |
1441 | 680 | WPS_DEBUG_MSG(("XYWriteParser::createTable: the zone seems too short\n")); |
1442 | 680 | return false; |
1443 | 680 | } |
1444 | 16.1k | std::vector<float> colWidth; |
1445 | 16.1k | std::vector<std::string> colStyle; |
1446 | 833k | for (size_t j=0; j<=tableFormat.m_args.size(); ++j) |
1447 | 817k | { |
1448 | 817k | size_t p=j==0 ? 2 : 0; |
1449 | 817k | double value; |
1450 | 817k | bool inPoint; |
1451 | 817k | std::string tmp; |
1452 | 817k | if (tableFormat.readUnit(j==0 ? tableFormat.m_string : tableFormat.m_args[j-1], p, m_state->m_isDosFile, value, inPoint, tmp) && inPoint) |
1453 | 41.8k | { |
1454 | 41.8k | if (j==0) // left pos |
1455 | 3.66k | ; |
1456 | 38.2k | else |
1457 | 38.2k | { |
1458 | 38.2k | colWidth.push_back(float(value)); |
1459 | 38.2k | if (tmp.size()>1) |
1460 | 5.79k | colStyle.push_back(tmp.substr(1)); |
1461 | 32.4k | else |
1462 | 32.4k | colStyle.push_back(""); |
1463 | 38.2k | } |
1464 | 41.8k | } |
1465 | 775k | else |
1466 | 775k | { |
1467 | 775k | WPS_DEBUG_MSG(("XYWriteParser::createTable: can not read some colum size %s\n", j==0 ? tableFormat.m_string.c_str() : tableFormat.m_args[j-1].c_str())); |
1468 | 775k | if (j) |
1469 | 763k | { |
1470 | 763k | colWidth.push_back(0); |
1471 | 763k | colStyle.push_back(""); |
1472 | 763k | } |
1473 | 775k | } |
1474 | 817k | } |
1475 | 16.1k | if (colWidth.empty()) |
1476 | 2.20k | { |
1477 | 2.20k | WPS_DEBUG_MSG(("XYWriteParser::createTable: can not find any columns\n")); |
1478 | 2.20k | return false; |
1479 | 2.20k | } |
1480 | 13.9k | int numColumns=int(colWidth.size()); |
1481 | | |
1482 | 13.9k | std::vector<XYWriteParserInternal::Cell> cells; |
1483 | 13.9k | int cRow=0, cCol=0; |
1484 | 13.9k | cells.push_back(XYWriteParserInternal::Cell(*this)); |
1485 | 13.9k | cells.back().m_entry.setBegin(input->tell()); |
1486 | 13.9k | cells.back().setPosition(Vec2i(cCol,cRow)); |
1487 | | |
1488 | 13.9k | bool ok=false; |
1489 | 3.32M | while (!input->isEnd()) |
1490 | 3.32M | { |
1491 | 3.32M | long pos=input->tell(); |
1492 | 3.32M | if (pos>=endPos) |
1493 | 5.10k | break; |
1494 | 3.32M | uint8_t c=libwps::readU8(input); |
1495 | 3.32M | if (c==0x1a) |
1496 | 845 | { |
1497 | 845 | if (input->tell()<endPos) |
1498 | 768 | { |
1499 | 768 | WPS_DEBUG_MSG(("XYWriteParser::createTable: find unexpected end zone\n")); |
1500 | 768 | } |
1501 | 845 | break; |
1502 | 845 | } |
1503 | 3.32M | if (c!=0xae) |
1504 | 3.05M | continue; |
1505 | 269k | XYWriteParserInternal::Format format; |
1506 | 269k | if (!parseFormat(format)) |
1507 | 1.11k | throw (libwps::ParseException()); |
1508 | 268k | std::string const str=format.title(); |
1509 | 268k | std::string const sTitle=format.shortTitle(); |
1510 | 268k | if (str=="EC") |
1511 | 4.58k | { |
1512 | 4.58k | ok=true; |
1513 | 4.58k | break; |
1514 | 4.58k | } |
1515 | 263k | if (sTitle=="CT") |
1516 | 1.23k | break; |
1517 | 262k | else if (sTitle=="CO") |
1518 | 114k | { |
1519 | 114k | unsigned int value; |
1520 | 114k | size_t p=2; |
1521 | 114k | std::string tmp; |
1522 | 114k | if (!format.readUInt(format.m_string, p, value, tmp)) |
1523 | 1.95k | continue; |
1524 | 112k | if (numColumns==0 || cells.empty() || value==0) // the table is not created |
1525 | 570 | break; |
1526 | 112k | cells.back().m_entry.setEnd(pos); |
1527 | 112k | int newCol=int(value)-1; |
1528 | 112k | if (newCol>numColumns) |
1529 | 535 | break; |
1530 | 111k | if (newCol<=cCol) |
1531 | 105k | ++cRow; |
1532 | 111k | cCol=newCol; |
1533 | 111k | cells.push_back(XYWriteParserInternal::Cell(*this)); |
1534 | 111k | cells.back().m_entry.setBegin(input->tell()); |
1535 | 111k | cells.back().setPosition(Vec2i(cCol,cRow)); |
1536 | 111k | if (cCol<int(colStyle.size())) |
1537 | 105k | cells.back().m_style=colStyle[size_t(cCol)]; |
1538 | 111k | } |
1539 | 263k | } |
1540 | 12.8k | if (ok) |
1541 | 4.58k | { |
1542 | 4.58k | m_listener->openTable(colWidth, librevenge::RVNG_POINT); |
1543 | 4.58k | cRow=-1; |
1544 | 4.58k | WPSListenerPtr listener(m_listener); |
1545 | 4.58k | for (auto &cell : cells) |
1546 | 107k | { |
1547 | 107k | auto const cPos=cell.position(); |
1548 | 210k | while (cRow<cPos[1]) |
1549 | 103k | { |
1550 | 103k | if (cRow!=-1) |
1551 | 98.5k | m_listener->closeTableRow(); |
1552 | 103k | m_listener->openTableRow(-10, librevenge::RVNG_POINT); |
1553 | 103k | ++cRow; |
1554 | 103k | cCol=0; |
1555 | 103k | } |
1556 | 107k | if (cCol<cPos[0]) |
1557 | 68.1k | m_listener->addEmptyTableCell(Vec2i(cCol,cRow),Vec2i(cPos[0]-cCol,1)); |
1558 | 107k | m_listener->openTableCell(cell); |
1559 | 107k | cell.sendContent(listener); |
1560 | 107k | m_listener->closeTableCell(); |
1561 | 107k | cCol=cPos[0]+1; |
1562 | 107k | } |
1563 | 4.58k | if (cRow!=-1) |
1564 | 4.13k | m_listener->closeTableRow(); |
1565 | 4.58k | m_listener->closeTable(); |
1566 | 4.58k | } |
1567 | 8.29k | else |
1568 | 8.29k | input->seek(begPos, librevenge::RVNG_SEEK_SET); |
1569 | 12.8k | return ok; |
1570 | 13.9k | } |
1571 | | |
1572 | | bool XYWriteParser::parseMetaData(WPSEntry const &entry) |
1573 | 199 | { |
1574 | 199 | RVNGInputStreamPtr input = getInput(); |
1575 | 199 | if (!input) |
1576 | 0 | throw (libwps::ParseException()); |
1577 | 199 | if (!entry.valid()) |
1578 | 0 | return true; |
1579 | 199 | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
1580 | 199 | XYWriteParserInternal::Format format; |
1581 | 199 | std::string actualString; |
1582 | 199 | auto fontType=m_state->getFontType(); |
1583 | 4.06M | while (!input->isEnd() && input->tell()+1<entry.end()) |
1584 | 4.06M | { |
1585 | 4.06M | uint8_t c=libwps::readU8(input); |
1586 | 4.06M | if (c==0x1a) |
1587 | 0 | { |
1588 | 0 | WPS_DEBUG_MSG(("XYWriteParser::parseMetaData: find end of zone\n")); |
1589 | 0 | return false; |
1590 | 0 | } |
1591 | 4.06M | if (c!=0xae) |
1592 | 4.05M | { |
1593 | 4.05M | actualString+=char(c); |
1594 | 4.05M | continue; |
1595 | 4.05M | } |
1596 | 10.7k | const auto strEnd = actualString.find_last_not_of(" "); |
1597 | 10.7k | actualString=actualString.substr(0, strEnd+1); |
1598 | 10.7k | if (!actualString.empty()) |
1599 | 8.43k | { |
1600 | | // also LBLG:40 another author |
1601 | | // find also LBCD:20, LBCT:15, LBMD:20, LBMT:15 with some checksum? |
1602 | | // and LBRP:4, LBPJ:20, LBCM:44, LBKY:~250 empty |
1603 | 8.43k | auto const finalStr=libwps_tools_win::Font::unicodeString(actualString, fontType); |
1604 | 8.43k | std::string const str=format.title(); |
1605 | 8.43k | if (str=="LBAU") // sz:40 |
1606 | 318 | m_state->m_metaData.insert("dc:creator", finalStr); |
1607 | 8.11k | else if (str=="LBRV") // revision sz:4 |
1608 | 2.54k | m_state->m_metaData.insert("librevenge:version-number", finalStr); |
1609 | 8.43k | } |
1610 | 10.7k | actualString.clear(); |
1611 | 10.7k | if (!parseFormat(format)) |
1612 | 79 | return false; |
1613 | 10.7k | } |
1614 | 120 | return true; |
1615 | 199 | } |
1616 | | |
1617 | | //////////////////////////////////////////////////////////// |
1618 | | // low level |
1619 | | //////////////////////////////////////////////////////////// |
1620 | | |
1621 | | bool XYWriteParser::parseFormat(XYWriteParserInternal::Format &format) |
1622 | 2.01M | { |
1623 | 2.01M | RVNGInputStreamPtr input = getInput(); |
1624 | 2.01M | if (!input) |
1625 | 0 | throw (libwps::ParseException()); |
1626 | 2.01M | format=XYWriteParserInternal::Format(m_state->m_isDosFile); |
1627 | 2.01M | format.m_entry.setBegin(input->tell()); |
1628 | 289M | while (!input->isEnd()) |
1629 | 289M | { |
1630 | 289M | if (input->tell()>=m_state->m_eof) |
1631 | 118 | { |
1632 | 118 | WPS_DEBUG_MSG(("XYWriteParser::parseFormat: can not find end of format\n")); |
1633 | 118 | return false; |
1634 | 118 | } |
1635 | 289M | uint8_t c=libwps::readU8(input); |
1636 | 289M | if (c==0xaf) |
1637 | 1.73M | { |
1638 | 1.73M | format.m_entry.setEnd(input->tell()-1); |
1639 | 1.73M | return true; |
1640 | 1.73M | } |
1641 | 287M | if (c==0xfa || c==0xae) |
1642 | 270k | { |
1643 | 270k | format.m_isComplex=true; |
1644 | | // normally ae XXX fa ... af |
1645 | | // but sometimes I found ae XXX ae ... af fa ... af |
1646 | 270k | if (format.m_entry.valid()) |
1647 | 0 | { |
1648 | 0 | WPS_DEBUG_MSG(("XYWriteParser::parseFormat: oops an entry is already defined\n")); |
1649 | 0 | } |
1650 | 270k | int depth=c==0xae ? 1 : 0; |
1651 | 573M | while (!input->isEnd()) |
1652 | 573M | { |
1653 | 573M | if (input->tell()>=m_state->m_eof) |
1654 | 54 | { |
1655 | 54 | WPS_DEBUG_MSG(("XYWriteParser::parseFormat: can not find end of entry\n")); |
1656 | 54 | return false; |
1657 | 54 | } |
1658 | 573M | c=libwps::readU8(input); |
1659 | 573M | if (c==0xae) |
1660 | 21.0M | ++depth; |
1661 | 552M | else if (c==0xaf) |
1662 | 21.5M | { |
1663 | 21.5M | if (depth==0) |
1664 | 269k | { |
1665 | 269k | format.m_entry.setEnd(input->tell()-1); |
1666 | 269k | return true; |
1667 | 269k | } |
1668 | 21.2M | --depth; |
1669 | 21.2M | } |
1670 | 573M | } |
1671 | 270k | } |
1672 | 287M | if (c==0x1a) |
1673 | 8.35k | { |
1674 | 8.35k | WPS_DEBUG_MSG(("XYWriteParser::parseFormat: find end of zone\n")); |
1675 | 8.35k | return false; |
1676 | 8.35k | } |
1677 | 287M | if (c==',') |
1678 | 13.9M | format.m_args.push_back(std::string()); |
1679 | 273M | else if (format.m_args.empty()) |
1680 | 237M | format.m_string+=char(c); |
1681 | 35.6M | else |
1682 | 35.6M | format.m_args.back()+=char(c); |
1683 | 287M | } |
1684 | 32 | WPS_DEBUG_MSG(("XYWriteParser::parseFormat: find end of file\n")); |
1685 | 32 | return false; |
1686 | 2.01M | } |
1687 | | |
1688 | | bool XYWriteParser::createFormatChildren(XYWriteParserInternal::Format &format, size_t fPos) |
1689 | 166k | { |
1690 | 166k | RVNGInputStreamPtr input = getInput(); |
1691 | 166k | if (!input || !format.m_entry.valid()) |
1692 | 0 | throw (libwps::ParseException()); |
1693 | 166k | long endPos=format.m_entry.end(); |
1694 | 166k | if (endPos>m_state->m_eof) |
1695 | 0 | { |
1696 | 0 | WPS_DEBUG_MSG(("XYWriteParser::createFormatChildren: end entry seems bad\n")); |
1697 | 0 | return false; |
1698 | 0 | } |
1699 | 166k | long prevPos=input->tell(); |
1700 | 166k | input->seek(format.m_entry.begin(), librevenge::RVNG_SEEK_SET); |
1701 | | // skip header |
1702 | 46.3M | for (size_t p=0; p<fPos; ++p) |
1703 | 46.2M | { |
1704 | 46.2M | if (input->isEnd() || input->tell()>=endPos) |
1705 | 25.0k | break; |
1706 | 46.2M | if (libwps::readU8(input)==',') |
1707 | 63.1k | break; |
1708 | 46.2M | } |
1709 | 166k | if (input->tell()>=endPos) |
1710 | 26.5k | { |
1711 | 26.5k | input->seek(prevPos, librevenge::RVNG_SEEK_SET); |
1712 | 26.5k | return true; |
1713 | 26.5k | } |
1714 | 140k | std::string actString; |
1715 | 140k | long begPos=input->tell(); |
1716 | 140k | bool isComplex=false; |
1717 | 23.2M | while (!input->isEnd() && input->tell()<=endPos) |
1718 | 23.2M | { |
1719 | 23.2M | uint8_t c=input->tell()==endPos ? ',' : libwps::readU8(input); |
1720 | 23.2M | auto *child=format.m_children.empty() ? nullptr : &format.m_children.back(); |
1721 | 23.2M | if (c==0xfa || c==0xae) |
1722 | 79.7k | { |
1723 | 79.7k | isComplex=true; |
1724 | | // normally ae XXX fa ... af |
1725 | | // but sometimes I found ae XXX ae ... af fa ... af |
1726 | 79.7k | int depth=c==0xae ? 1 : 0; |
1727 | 460M | while (!input->isEnd() && input->tell()<endPos) |
1728 | 460M | { |
1729 | 460M | c=libwps::readU8(input); |
1730 | 460M | if (c==0xae) |
1731 | 20.3M | ++depth; |
1732 | 440M | else if (c==',' && depth==0) |
1733 | 9.49k | break; |
1734 | 440M | else if (c==0xaf) |
1735 | 20.4M | { |
1736 | 20.4M | if (depth==0) |
1737 | 17.4k | break; |
1738 | 20.4M | --depth; |
1739 | 20.4M | } |
1740 | 460M | } |
1741 | 79.7k | if (c!=',') |
1742 | 70.2k | continue; |
1743 | 79.7k | } |
1744 | 23.1M | if (c==0x1a) |
1745 | 2.91k | { |
1746 | 2.91k | WPS_DEBUG_MSG(("XYWriteParser::createFormatChildren: find end of zone\n")); |
1747 | 2.91k | input->seek(prevPos, librevenge::RVNG_SEEK_SET); |
1748 | 2.91k | return false; |
1749 | 2.91k | } |
1750 | 23.1M | if (c==',') |
1751 | 5.47M | { |
1752 | 5.47M | if (!actString.empty()) |
1753 | 3.51M | { |
1754 | 3.51M | if (actString[0]=='.' || (actString[0]>='0' && actString[0]<='9')) |
1755 | 673k | { |
1756 | 673k | if (child) |
1757 | 614k | { |
1758 | 614k | child->m_isComplex=isComplex; |
1759 | 614k | child->m_args.push_back(actString); |
1760 | 614k | } |
1761 | 673k | } |
1762 | 2.84M | else |
1763 | 2.84M | { |
1764 | 2.84M | if (child) |
1765 | 2.71M | child->m_entry.setEnd(begPos-1); |
1766 | 2.84M | format.m_children.push_back(XYWriteParserInternal::Format()); |
1767 | 2.84M | format.m_children.back().m_entry.setBegin(begPos); |
1768 | 2.84M | format.m_children.back().m_inDosFile = m_state->m_isDosFile; |
1769 | 2.84M | format.m_children.back().m_string=actString; |
1770 | 2.84M | format.m_children.back().m_isComplex=isComplex; |
1771 | 2.84M | isComplex=false; |
1772 | 2.84M | } |
1773 | 3.51M | } |
1774 | 5.47M | actString.clear(); |
1775 | 5.47M | begPos=input->tell(); |
1776 | 5.47M | if (!format.m_children.empty()) |
1777 | 5.28M | format.m_children.back().m_entry.setEnd(begPos==endPos ? endPos : begPos-1); |
1778 | 5.47M | if (begPos==endPos) |
1779 | 137k | break; |
1780 | 5.33M | continue; |
1781 | 5.47M | } |
1782 | 17.6M | if (c!='=') |
1783 | 17.6M | actString+=char(c); |
1784 | 17.6M | } |
1785 | 137k | input->seek(prevPos, librevenge::RVNG_SEEK_SET); |
1786 | 137k | return true; |
1787 | 140k | } |
1788 | | |
1789 | | // basic function to check if the header is ok |
1790 | | bool XYWriteParser::checkHeader(WPSHeader *header, bool /*strict*/) |
1791 | 4.47k | { |
1792 | 4.47k | RVNGInputStreamPtr input = getInput(); |
1793 | 4.47k | if (!input || !checkFilePosition(10)) // too small for containing any usefull format |
1794 | 0 | { |
1795 | 0 | WPS_DEBUG_MSG(("XYWriteParser::checkHeader: file is too short\n")); |
1796 | 0 | return false; |
1797 | 0 | } |
1798 | | |
1799 | | /* check sequence 0xae ...[,*] 0xaf */ |
1800 | 4.47k | input->seek(0x0, librevenge::RVNG_SEEK_SET); |
1801 | 4.47k | bool ok=false, inFormat=false; |
1802 | 4.47k | int depth=0; |
1803 | 4.47k | int numBadChar=0, numCurrentChar=0; |
1804 | 6.73M | while (!input->isEnd()) |
1805 | 6.73M | { |
1806 | 6.73M | if (numBadChar>10) |
1807 | 18 | break; |
1808 | 6.73M | uint8_t c=libwps::readU8(input); |
1809 | 6.73M | if (c<=0x1f && c!=0x9 && c!=0xa && c!=0xd && c!=0x1b) |
1810 | 13.9k | ++numBadChar; |
1811 | 6.71M | else if (!depth) |
1812 | 893k | { |
1813 | 893k | if (c==0xae) |
1814 | 4.45k | { |
1815 | 4.45k | inFormat=true; |
1816 | 4.45k | depth=1; |
1817 | 4.45k | } |
1818 | 889k | else if (c==0xaf) // end before begin |
1819 | 0 | break; |
1820 | 893k | } |
1821 | 5.82M | else |
1822 | 5.82M | { |
1823 | 5.82M | if (c==0xaf) |
1824 | 13.2k | { |
1825 | 13.2k | if (--depth==0) // find the end of a sequence, ok |
1826 | 4.44k | { |
1827 | 4.44k | ok=true; |
1828 | 4.44k | break; |
1829 | 4.44k | } |
1830 | 8.81k | inFormat=false; |
1831 | 8.81k | } |
1832 | 5.80M | else if (c==0xae) |
1833 | 457k | { |
1834 | 457k | ++depth; |
1835 | 457k | inFormat=true; |
1836 | 457k | } |
1837 | 5.35M | else if (c==',') |
1838 | 2.31M | numCurrentChar=0; |
1839 | 3.03M | else if (c==0xfa) |
1840 | 593 | inFormat=false; |
1841 | 3.03M | else if (inFormat) |
1842 | 2.54M | { |
1843 | 2.54M | if (++numCurrentChar>256) |
1844 | 1 | break; |
1845 | 2.54M | } |
1846 | 5.82M | } |
1847 | 6.73M | } |
1848 | 4.47k | if (!ok) |
1849 | 28 | { |
1850 | 28 | WPS_DEBUG_MSG(("XYWriteParser::checkHeader: can not find any sequence\n")); |
1851 | 28 | return false; |
1852 | 28 | } |
1853 | 4.44k | input->seek(-1, librevenge::RVNG_SEEK_END); |
1854 | 4.44k | int val=libwps::readU8(input); |
1855 | 4.44k | if (val==0x1a) |
1856 | 4.21k | m_state->m_isDosFile=true; |
1857 | 228 | else if (val) |
1858 | 0 | { |
1859 | 0 | WPS_DEBUG_MSG(("XYWriteParser::checkHeader: oops unexpected last character\n")); |
1860 | 0 | return false; |
1861 | 0 | } |
1862 | 4.44k | if (header) |
1863 | 0 | header->setMajorVersion(m_state->m_isDosFile ? 0 : 1); |
1864 | 4.44k | return true; |
1865 | 4.44k | } |
1866 | | |
1867 | | // read the document structure ... |
1868 | | |
1869 | | /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */ |