/src/libmwaw/src/lib/JazzWriterParser.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */ |
2 | | |
3 | | /* libmwaw |
4 | | * Version: MPL 2.0 / LGPLv2+ |
5 | | * |
6 | | * The contents of this file are subject to the Mozilla Public License Version |
7 | | * 2.0 (the "License"); you may not use this file except in compliance with |
8 | | * the License or as specified alternatively below. You may obtain a copy of |
9 | | * the License at http://www.mozilla.org/MPL/ |
10 | | * |
11 | | * Software distributed under the License is distributed on an "AS IS" basis, |
12 | | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
13 | | * for the specific language governing rights and limitations under the |
14 | | * License. |
15 | | * |
16 | | * Major Contributor(s): |
17 | | * Copyright (C) 2002 William Lachance (wrlach@gmail.com) |
18 | | * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net) |
19 | | * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch) |
20 | | * Copyright (C) 2006, 2007 Andrew Ziem |
21 | | * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr) |
22 | | * |
23 | | * |
24 | | * All Rights Reserved. |
25 | | * |
26 | | * For minor contributions see the git repository. |
27 | | * |
28 | | * Alternatively, the contents of this file may be used under the terms of |
29 | | * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), |
30 | | * in which case the provisions of the LGPLv2+ are applicable |
31 | | * instead of those above. |
32 | | */ |
33 | | |
34 | | #include <iomanip> |
35 | | #include <iostream> |
36 | | #include <limits> |
37 | | #include <set> |
38 | | #include <sstream> |
39 | | |
40 | | #include <librevenge/librevenge.h> |
41 | | |
42 | | #include "MWAWTextListener.hxx" |
43 | | #include "MWAWFontConverter.hxx" |
44 | | #include "MWAWHeader.hxx" |
45 | | #include "MWAWParagraph.hxx" |
46 | | #include "MWAWPictData.hxx" |
47 | | #include "MWAWPosition.hxx" |
48 | | #include "MWAWPrinter.hxx" |
49 | | #include "MWAWRSRCParser.hxx" |
50 | | #include "MWAWSubDocument.hxx" |
51 | | |
52 | | #include "JazzWriterParser.hxx" |
53 | | |
54 | | /** Internal: the structures of a JazzWriterParser */ |
55 | | namespace JazzWriterParserInternal |
56 | | { |
57 | | |
58 | | //////////////////////////////////////// |
59 | | //! Internal: the structure used to store a paragraph/section |
60 | | struct Paragraph { |
61 | | //! constructor |
62 | | Paragraph() |
63 | 0 | : m_paragraph() |
64 | 0 | , m_dimension(0,0) |
65 | 0 | , m_nextParagraphId(0) |
66 | 0 | , m_plcId(0) |
67 | 0 | { |
68 | 0 | } |
69 | | //! the paragraph |
70 | | MWAWParagraph m_paragraph; |
71 | | //! the dimension |
72 | | MWAWVec2i m_dimension; |
73 | | //! the next paragraph id |
74 | | unsigned m_nextParagraphId; |
75 | | //! the plc id |
76 | | unsigned m_plcId; |
77 | | }; |
78 | | |
79 | | //////////////////////////////////////// |
80 | | //! Internal: the structure used to store a zone |
81 | | struct Zone { |
82 | | //! constructor |
83 | | Zone() |
84 | 0 | : m_paragraphId(0) |
85 | 0 | , m_entry() |
86 | 0 | { |
87 | 0 | for (auto &id : m_hfIds) id=0; |
88 | 0 | } |
89 | | //! the header/footer id |
90 | | unsigned m_hfIds[2]; |
91 | | //! the paragraph id |
92 | | unsigned m_paragraphId; |
93 | | //! the text position in the data fork |
94 | | MWAWEntry m_entry; |
95 | | }; |
96 | | |
97 | | //////////////////////////////////////// |
98 | | //! Internal: the state of a JazzWriterParser |
99 | | struct State { |
100 | | //! constructor |
101 | | State() |
102 | 0 | : m_idToZones() |
103 | 0 | , m_idToParagraphs() |
104 | 0 | { |
105 | 0 | } |
106 | | |
107 | | /// map WDOC id to zones |
108 | | std::map<unsigned, Zone> m_idToZones; |
109 | | /// map WPPD id to zones |
110 | | std::map<unsigned, Paragraph> m_idToParagraphs; |
111 | | }; |
112 | | |
113 | | //////////////////////////////////////// |
114 | | //! Internal: the subdocument of a JazzWriterParser |
115 | | class SubDocument final : public MWAWSubDocument |
116 | | { |
117 | | public: |
118 | | SubDocument(JazzWriterParser &pars, MWAWInputStreamPtr const &input, MWAWInputStreamPtr const &rsrcInput, unsigned id) |
119 | 0 | : MWAWSubDocument(&pars, input, MWAWEntry()) |
120 | 0 | , m_rsrcInput(rsrcInput) |
121 | 0 | , m_zId(id) |
122 | 0 | { |
123 | 0 | } |
124 | | |
125 | | //! destructor |
126 | 0 | ~SubDocument() final {} |
127 | | |
128 | | //! operator!= |
129 | | bool operator!=(MWAWSubDocument const &doc) const final; |
130 | | |
131 | | //! the parser function |
132 | | void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final; |
133 | | |
134 | | protected: |
135 | | //! the resource fork input |
136 | | MWAWInputStreamPtr m_rsrcInput; |
137 | | //! the zone id |
138 | | unsigned m_zId; |
139 | | }; |
140 | | |
141 | | void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/) |
142 | 0 | { |
143 | 0 | if (!listener.get()) { |
144 | 0 | MWAW_DEBUG_MSG(("JazzWriterParserInternal::SubDocument::parse: no listener\n")); |
145 | 0 | return; |
146 | 0 | } |
147 | 0 | auto *parser=dynamic_cast<JazzWriterParser *>(m_parser); |
148 | 0 | if (!parser) { |
149 | 0 | MWAW_DEBUG_MSG(("JazzWriterParserInternal::SubDocument::parse: no parser\n")); |
150 | 0 | return; |
151 | 0 | } |
152 | 0 | if (!m_input || !m_rsrcInput) { |
153 | 0 | MWAW_DEBUG_MSG(("JazzWriterParserInternal::SubDocument::parse: no input\n")); |
154 | 0 | return; |
155 | 0 | } |
156 | 0 | long pos = m_input->tell(); |
157 | 0 | long rPos = m_rsrcInput->tell(); |
158 | 0 | parser->sendZone(m_zId); |
159 | 0 | m_input->seek(pos, librevenge::RVNG_SEEK_SET); |
160 | 0 | m_rsrcInput->seek(rPos, librevenge::RVNG_SEEK_SET); |
161 | 0 | } |
162 | | |
163 | | bool SubDocument::operator!=(MWAWSubDocument const &doc) const |
164 | 0 | { |
165 | 0 | if (MWAWSubDocument::operator!=(doc)) return true; |
166 | 0 | auto const *sDoc = dynamic_cast<SubDocument const *>(&doc); |
167 | 0 | if (!sDoc) return true; |
168 | 0 | if (m_zId != sDoc->m_zId) return true; |
169 | 0 | if (m_rsrcInput != sDoc->m_rsrcInput) return true; |
170 | 0 | return false; |
171 | 0 | } |
172 | | } |
173 | | |
174 | | |
175 | | //////////////////////////////////////////////////////////// |
176 | | // constructor/destructor, ... |
177 | | //////////////////////////////////////////////////////////// |
178 | | JazzWriterParser::JazzWriterParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header) |
179 | 0 | : MWAWTextParser(input, rsrcParser, header) |
180 | 0 | , m_state(new JazzWriterParserInternal::State) |
181 | 0 | { |
182 | 0 | setAsciiName("main-1"); |
183 | 0 | } |
184 | | |
185 | | JazzWriterParser::~JazzWriterParser() |
186 | 0 | { |
187 | 0 | } |
188 | | |
189 | | MWAWInputStreamPtr JazzWriterParser::rsrcInput() |
190 | 0 | { |
191 | 0 | return getRSRCParser()->getInput(); |
192 | 0 | } |
193 | | |
194 | | libmwaw::DebugFile &JazzWriterParser::rsrcAscii() |
195 | 0 | { |
196 | 0 | return getRSRCParser()->ascii(); |
197 | 0 | } |
198 | | |
199 | | //////////////////////////////////////////////////////////// |
200 | | // the parser |
201 | | //////////////////////////////////////////////////////////// |
202 | | void JazzWriterParser::parse(librevenge::RVNGTextInterface *docInterface) |
203 | 0 | { |
204 | 0 | if (!getInput().get() || !getRSRCParser() || !checkHeader(nullptr)) throw(libmwaw::ParseException()); |
205 | 0 | bool ok = false; |
206 | 0 | try { |
207 | | // create the asciiFile |
208 | 0 | ascii().setStream(getInput()); |
209 | 0 | ascii().open(asciiName()); |
210 | 0 | checkHeader(nullptr); |
211 | 0 | ok = createZones(); |
212 | 0 | if (ok) { |
213 | 0 | createDocument(docInterface); |
214 | 0 | sendZone(257); |
215 | 0 | if (!getInput()->isEnd()) { |
216 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::parse: find some unsent characters\n")); |
217 | 0 | } |
218 | 0 | } |
219 | 0 | } |
220 | 0 | catch (...) { |
221 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::parse: exception catched when parsing\n")); |
222 | 0 | ok = false; |
223 | 0 | } |
224 | |
|
225 | 0 | resetTextListener(); |
226 | 0 | if (!ok) throw(libmwaw::ParseException()); |
227 | 0 | } |
228 | | |
229 | | //////////////////////////////////////////////////////////// |
230 | | // create the document |
231 | | //////////////////////////////////////////////////////////// |
232 | | void JazzWriterParser::createDocument(librevenge::RVNGTextInterface *documentInterface) |
233 | 0 | { |
234 | 0 | if (!documentInterface) return; |
235 | 0 | if (getTextListener()) { |
236 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::createDocument: listener already exist\n")); |
237 | 0 | return; |
238 | 0 | } |
239 | | |
240 | | // found the numbers of page and update the page |
241 | 0 | int numPages = 1; |
242 | 0 | auto input=getInput(); |
243 | 0 | input->seek(0, librevenge::RVNG_SEEK_SET); |
244 | 0 | while (!input->isEnd()) { |
245 | 0 | if (input->readULong(1)==0xc) |
246 | 0 | ++numPages; |
247 | 0 | } |
248 | | |
249 | | // create the page list |
250 | 0 | MWAWPageSpan ps(getPageSpan()); |
251 | 0 | ps.setPageSpan(numPages+1); |
252 | 0 | auto it=m_state->m_idToZones.find(257); |
253 | 0 | if (it==m_state->m_idToZones.end()) { |
254 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::createDocument: can not find the main zone\n")); |
255 | 0 | throw (libmwaw::ParseException()); |
256 | 0 | } |
257 | | |
258 | 0 | for (int wh=0; wh<2; ++wh) { |
259 | 0 | if (!it->second.m_hfIds[wh]) |
260 | 0 | continue; |
261 | 0 | MWAWHeaderFooter header(wh==0 ? MWAWHeaderFooter::HEADER : MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL); |
262 | 0 | header.m_subDocument.reset(new JazzWriterParserInternal::SubDocument(*this, getInput(), rsrcInput(), it->second.m_hfIds[wh])); |
263 | 0 | ps.setHeaderFooter(header); |
264 | 0 | } |
265 | |
|
266 | 0 | std::vector<MWAWPageSpan> pageList(1,ps); |
267 | 0 | MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface)); |
268 | 0 | setTextListener(listen); |
269 | 0 | listen->startDocument(); |
270 | 0 | } |
271 | | |
272 | | |
273 | | //////////////////////////////////////////////////////////// |
274 | | // |
275 | | // Intermediate level |
276 | | // |
277 | | //////////////////////////////////////////////////////////// |
278 | | bool JazzWriterParser::createZones() |
279 | 0 | { |
280 | 0 | MWAWRSRCParserPtr rsrcParser = getRSRCParser(); |
281 | 0 | if (!rsrcParser) { |
282 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::createZones: can not find the entry map\n")); |
283 | 0 | return false; |
284 | 0 | } |
285 | | |
286 | 0 | auto const &entryMap = rsrcParser->getEntriesMap(); |
287 | 0 | MWAWInputStreamPtr input = rsrcInput(); |
288 | 0 | libmwaw::DebugFile &ascFile = rsrcAscii(); |
289 | |
|
290 | 0 | char const *zNames[] = {"LFRF", "LLNK", "WDOC", "WPPD"}; |
291 | 0 | char const *what[]= {"FileRef", "Link", "Zone", "Paragraph"}; |
292 | 0 | libmwaw::DebugStream f; |
293 | 0 | for (int wh=0; wh<4; ++wh) { |
294 | 0 | auto it = entryMap.lower_bound(zNames[wh]); |
295 | 0 | while (it != entryMap.end()) { |
296 | 0 | if (it->first != zNames[wh] || !it->second.valid()) |
297 | 0 | break; |
298 | 0 | MWAWEntry const &entry = it++->second; |
299 | 0 | if (!input->checkPosition(entry.end())) { |
300 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::createZones: find bad entry\n")); |
301 | 0 | continue; |
302 | 0 | } |
303 | 0 | entry.setParsed(true); |
304 | 0 | bool ok=false, done=false; |
305 | 0 | int val; |
306 | 0 | f.str(""); |
307 | 0 | f << "Entries(" << what[wh] << ")[" << entry.id() << "]:"; |
308 | 0 | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
309 | 0 | switch (wh) { |
310 | 0 | case 0: // link name |
311 | 0 | f << entry.name() << ","; // the file name |
312 | 0 | ok=true; |
313 | 0 | if (entry.length()!=2) { |
314 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::createZones[lref]: the entry length seems bad\n")); |
315 | 0 | f << "###"; |
316 | 0 | break; |
317 | 0 | } |
318 | 0 | val=int(input->readLong(2)); |
319 | 0 | if (val!=1) f << "f0=" << val << ","; |
320 | 0 | break; |
321 | 0 | case 1: { |
322 | 0 | librevenge::RVNGString text; |
323 | 0 | ok=true; |
324 | 0 | if (!readString(input, text, entry.end())) { |
325 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::createZones[lnk]: can not find the text\n")); |
326 | 0 | f << "###"; |
327 | 0 | break; |
328 | 0 | } |
329 | 0 | if (!text.empty()) |
330 | 0 | f << text.cstr() << ","; |
331 | 0 | long cPos=input->tell(); |
332 | 0 | if (cPos+2>entry.end()) { |
333 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::createZones[lnk]: can not find the file ref\n")); |
334 | 0 | f << "###"; |
335 | 0 | break; |
336 | 0 | } |
337 | 0 | f << "file[ref]=" << input->readULong(2) << ","; |
338 | 0 | break; |
339 | 0 | } |
340 | 0 | case 2: |
341 | 0 | done=readZone(entry); |
342 | 0 | break; |
343 | 0 | case 3: |
344 | 0 | done=readParagraph(entry); |
345 | 0 | break; |
346 | 0 | default: |
347 | 0 | break; |
348 | 0 | } |
349 | 0 | if (done) |
350 | 0 | continue; |
351 | 0 | if (!ok) |
352 | 0 | f << "###"; |
353 | 0 | ascFile.addPos(entry.begin()-4); |
354 | 0 | ascFile.addNote(f.str().c_str()); |
355 | 0 | } |
356 | 0 | } |
357 | 0 | return checkZones(); |
358 | 0 | } |
359 | | |
360 | | bool JazzWriterParser::checkZones() |
361 | 0 | { |
362 | 0 | auto it=m_state->m_idToZones.find(257); |
363 | 0 | if (it==m_state->m_idToZones.end()) { |
364 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkZones: can not find the main zone\n")); |
365 | 0 | return false; |
366 | 0 | } |
367 | 0 | unsigned const zones[3]= {it->second.m_hfIds[0],it->second.m_hfIds[1],257}; |
368 | 0 | std::set<unsigned> seens; |
369 | 0 | long pos=0; |
370 | 0 | for (auto const &id : zones) { |
371 | 0 | it=m_state->m_idToZones.find(id); |
372 | 0 | if (it==m_state->m_idToZones.end()) { |
373 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkZones: can not find the %x zone\n", id)); |
374 | 0 | return false; |
375 | 0 | } |
376 | 0 | long length=0; |
377 | 0 | if (!checkParagraphs(it->second.m_paragraphId, length, seens)) |
378 | 0 | return false; |
379 | 0 | MWAWEntry entry; |
380 | 0 | entry.setBegin(pos); |
381 | 0 | entry.setLength(length); |
382 | 0 | it->second.m_entry=entry; |
383 | 0 | pos+=length; |
384 | 0 | } |
385 | 0 | if (!getInput() || getInput()->size()<pos) { |
386 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::createZones: the data fork seems too short\n")); |
387 | 0 | return false; |
388 | 0 | } |
389 | 0 | getInput()->seek(0, librevenge::RVNG_SEEK_SET); |
390 | |
|
391 | 0 | return true; |
392 | 0 | } |
393 | | |
394 | | bool JazzWriterParser::checkParagraphs(unsigned id, long &num, std::set<unsigned> &seens) const |
395 | 0 | { |
396 | 0 | if (id==0) |
397 | 0 | return true; |
398 | 0 | if (seens.find(id) != seens.end()) { |
399 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkParagraphs: paragraph %x is already seen\n", id)); |
400 | 0 | return false; |
401 | 0 | } |
402 | 0 | seens.insert(id); |
403 | 0 | auto it=m_state->m_idToParagraphs.find(id); |
404 | 0 | if (it==m_state->m_idToParagraphs.end()) { |
405 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkParagraphs: can not find paragraph %x\n", id)); |
406 | 0 | return false; |
407 | 0 | } |
408 | 0 | long n=0; |
409 | 0 | if (!const_cast<JazzWriterParser *>(this)->countCharactersInPLC(it->second.m_plcId, n)) |
410 | 0 | return false; |
411 | 0 | num+=n; |
412 | 0 | checkParagraphs(it->second.m_nextParagraphId, num, seens); |
413 | 0 | return true; |
414 | 0 | } |
415 | | |
416 | | bool JazzWriterParser::countCharactersInPLC(unsigned plcId, long &n) |
417 | 0 | { |
418 | 0 | MWAWRSRCParserPtr rsrcParser = getRSRCParser(); |
419 | 0 | if (!rsrcParser) { |
420 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::countCharactersInPLC: can not find the rsrc parser\n")); |
421 | 0 | return false; |
422 | 0 | } |
423 | 0 | MWAWInputStreamPtr input = rsrcInput(); |
424 | 0 | MWAWEntry entry=rsrcParser->getEntry("WSCR", int(plcId)); |
425 | 0 | if (!entry.valid() || !input || !input->checkPosition(entry.end())) { |
426 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::countCharactersInPLC: can not find the %x WSCR\n", plcId)); |
427 | 0 | return false; |
428 | 0 | } |
429 | 0 | n=0; |
430 | 0 | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
431 | 0 | long endPos=entry.end(); |
432 | 0 | while (input->tell()+6<=endPos) { |
433 | 0 | long pos=input->tell(); |
434 | 0 | int type=int(input->readULong(2)); |
435 | 0 | switch (type) { |
436 | 0 | case 1: |
437 | 0 | n+=int(input->readULong(4)); |
438 | 0 | break; |
439 | 0 | case 3: |
440 | 0 | input->seek(3, librevenge::RVNG_SEEK_CUR); |
441 | 0 | n+=int(input->readULong(1)); |
442 | 0 | break; |
443 | 0 | case 5: |
444 | 0 | pos+=6; |
445 | 0 | break; |
446 | 0 | default: |
447 | 0 | break; |
448 | 0 | } |
449 | 0 | input->seek(pos+6, librevenge::RVNG_SEEK_SET); |
450 | 0 | } |
451 | 0 | return true; |
452 | 0 | } |
453 | | |
454 | | bool JazzWriterParser::readParagraph(MWAWEntry const &entry) |
455 | 0 | { |
456 | 0 | if (entry.length()!=122) { |
457 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readParagraph: unexpected size\n")); |
458 | 0 | return false; |
459 | 0 | } |
460 | 0 | MWAWInputStreamPtr input = rsrcInput(); |
461 | 0 | libmwaw::DebugFile &ascFile = rsrcAscii(); |
462 | 0 | libmwaw::DebugStream f; |
463 | |
|
464 | 0 | JazzWriterParserInternal::Paragraph para; |
465 | 0 | f << "Entries(Paragraph)[" << entry.id() << "]:"; |
466 | 0 | int val; |
467 | 0 | para.m_nextParagraphId=unsigned(input->readLong(2)); |
468 | 0 | if (para.m_nextParagraphId) f << "next[para]=" << para.m_nextParagraphId << ","; |
469 | 0 | val=int(input->readLong(2)); |
470 | 0 | if (val) f << "f0=" << val << ","; |
471 | 0 | val=int(input->readLong(2)); |
472 | 0 | if (val!=2) |
473 | 0 | f << "ID1=" << input->readULong(2) << ","; |
474 | 0 | else |
475 | 0 | f << "ID1=" << input->readULong(2) << "[" << val << "],"; |
476 | 0 | f << "dim0=" << input->readLong(2) << ","; |
477 | 0 | int dims[2]; |
478 | 0 | for (auto &d : dims) d=int(input->readLong(2)); |
479 | 0 | para.m_dimension=MWAWVec2i(dims[1],dims[0]); |
480 | 0 | f << "dim=" << para.m_dimension << ","; |
481 | 0 | val=int(input->readLong(2)); |
482 | 0 | if (val==2) |
483 | 0 | f << "ID2=" << input->readULong(2) << ","; |
484 | 0 | else |
485 | 0 | f << "ID2=" << input->readULong(2) << "[" << val << "],"; |
486 | 0 | for (int i=0; i<11; ++i) { // f5,f7 some heigths?, f8=0|600 |
487 | 0 | val=int(input->readLong(2)); |
488 | 0 | if (val) f << "f" << i+1 << "=" << val << ","; |
489 | 0 | } |
490 | 0 | f << "unk=" << input->readLong(2) << ","; // 1000|5000 |
491 | 0 | for (int i=0; i<2; ++i) { |
492 | 0 | val=int(input->readLong(2)); |
493 | 0 | if (val!=(i==1 ? 4 : 0)) |
494 | 0 | f << "g" << i << "=" << val << ","; |
495 | 0 | } |
496 | 0 | f << "fl=" << std::hex << input->readULong(2) << std::dec << ","; // 1002|1003 |
497 | 0 | val=int(input->readLong(2)); |
498 | 0 | if (val!=0xc00) f << "g2=" << val << ","; |
499 | 0 | for (int i=0; i<4; ++i) { // g3=0|8 |
500 | 0 | val=int(input->readULong(2)); |
501 | 0 | if (i==2 && (val&0x300)) { |
502 | 0 | f << "align=" << ((val>>8)&3) << ","; |
503 | 0 | switch ((val>>8)&3) { |
504 | 0 | case 1: |
505 | 0 | para.m_paragraph.m_justify=MWAWParagraph::JustificationRight; |
506 | 0 | break; |
507 | 0 | case 2: |
508 | 0 | para.m_paragraph.m_justify=MWAWParagraph::JustificationCenter; |
509 | 0 | break; |
510 | 0 | case 3: |
511 | 0 | para.m_paragraph.m_justify=MWAWParagraph::JustificationFull; |
512 | 0 | break; |
513 | 0 | default: |
514 | 0 | break; |
515 | 0 | } |
516 | 0 | val &= 0xfcff; |
517 | 0 | } |
518 | 0 | if (i==3 && (val&3)) { |
519 | 0 | f << "line spacing=" << 1+float(val&3)/2 << ","; |
520 | 0 | para.m_paragraph.setInterline(1+float(val&3)/2, librevenge::RVNG_PERCENT); |
521 | 0 | val &= 0xfffc; |
522 | 0 | } |
523 | 0 | if (!val) continue; |
524 | 0 | if (i==0) |
525 | 0 | f << "g3=" << std::hex << val << std::dec << ","; |
526 | 0 | else |
527 | 0 | f << "g" << i+3 << "=" << val << ","; |
528 | 0 | } |
529 | 0 | ascFile.addPos(entry.begin()-4); |
530 | 0 | ascFile.addNote(f.str().c_str()); |
531 | |
|
532 | 0 | input->seek(entry.begin()+58, librevenge::RVNG_SEEK_SET); |
533 | 0 | long pos=input->tell(); |
534 | 0 | f.str(""); |
535 | 0 | f << "Paragraph-A:"; |
536 | 0 | int N=int(input->readLong(2)); |
537 | 0 | f << "num[tabs]=" << N << ","; |
538 | 0 | if (N<0 || N>12) { |
539 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readParagraph: the number of tabs seems bads\n")); |
540 | 0 | f << "###"; |
541 | 0 | N=0; |
542 | 0 | } |
543 | 0 | para.m_paragraph.m_marginsUnit=librevenge::RVNG_POINT; |
544 | 0 | for (int i=0; i<3; ++i) { |
545 | 0 | val=int(input->readLong(2)); |
546 | 0 | int const expected[]= {72, 0x21c, 72}; |
547 | 0 | para.m_paragraph.m_margins[i==2 ? 0 : i+1]=val; |
548 | 0 | if (val==expected[i]) continue; |
549 | 0 | f << (i==0 ? "marg[left]" : i==1 ? "marg[right]" : "first[ident]") << "=" << val << ","; |
550 | 0 | } |
551 | 0 | *para.m_paragraph.m_margins[0]-=*para.m_paragraph.m_margins[1]; |
552 | 0 | para.m_paragraph.m_margins[2]=0; // margin from left, so ignore |
553 | 0 | f << "tabs["; |
554 | 0 | for (int i=0; i<N; ++i) { |
555 | 0 | MWAWTabStop tab; |
556 | 0 | val=int(input->readLong(2)); |
557 | 0 | if (val>=0) { |
558 | 0 | tab.m_position=double(val)/72.; |
559 | 0 | f << val << ","; |
560 | 0 | } |
561 | 0 | else { |
562 | 0 | tab.m_position=double(-val)/72.; |
563 | 0 | tab.m_alignment = MWAWTabStop::CENTER; |
564 | 0 | f << -val << "[C],"; |
565 | 0 | } |
566 | 0 | para.m_paragraph.m_tabs->push_back(tab); |
567 | 0 | } |
568 | 0 | f << "],"; |
569 | 0 | input->seek(pos+8+24, librevenge::RVNG_SEEK_SET); |
570 | 0 | for (int i=0; i<6; ++i) { |
571 | 0 | val=int(input->readLong(2)); |
572 | 0 | if (val) f << "g" << i << "=" << val << ","; |
573 | 0 | } |
574 | 0 | val=int(input->readLong(2)); |
575 | 0 | if (val!=2) |
576 | 0 | f << "h0=" << val << ","; |
577 | 0 | f << "ID1=" << input->readULong(2) << ","; |
578 | 0 | for (int i=0; i<8; ++i) { |
579 | 0 | val=int(input->readULong(2)); |
580 | 0 | if (!val) continue; |
581 | 0 | if (i==4) { |
582 | 0 | para.m_plcId=unsigned(val); |
583 | 0 | f << "plc[id]=" << val << ","; |
584 | 0 | } |
585 | 0 | else |
586 | 0 | f << "h" << i+1 << "=" << val << ","; |
587 | 0 | } |
588 | 0 | if (m_state->m_idToParagraphs.find(unsigned(entry.id()))==m_state->m_idToParagraphs.end()) |
589 | 0 | m_state->m_idToParagraphs[unsigned(entry.id())]=para; |
590 | 0 | else { |
591 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readParagraph: paragraph %d already exists\n", entry.id())); |
592 | 0 | f << "###id,"; |
593 | 0 | } |
594 | 0 | ascFile.addPos(pos); |
595 | 0 | ascFile.addNote(f.str().c_str()); |
596 | 0 | return true; |
597 | 0 | } |
598 | | |
599 | | bool JazzWriterParser::readZone(MWAWEntry const &entry) |
600 | 0 | { |
601 | 0 | if (entry.length()!=44) { |
602 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readZone: unexpected size\n")); |
603 | 0 | return false; |
604 | 0 | } |
605 | 0 | MWAWInputStreamPtr input = rsrcInput(); |
606 | 0 | libmwaw::DebugFile &ascFile = rsrcAscii(); |
607 | 0 | libmwaw::DebugStream f; |
608 | |
|
609 | 0 | JazzWriterParserInternal::Zone zone; |
610 | 0 | f << "Entries(Zone)[" << entry.id() << "]:"; |
611 | 0 | zone.m_paragraphId=unsigned(input->readULong(2)); |
612 | 0 | f << "para[id]=" << zone.m_paragraphId << ","; |
613 | 0 | int id1=int(input->readULong(2)); |
614 | 0 | f << "ID1=" << id1 << ","; |
615 | 0 | int val=int(input->readULong(2)); |
616 | 0 | if (unsigned(val)!=zone.m_paragraphId) |
617 | 0 | f << "para[id1]=" << val << ","; |
618 | 0 | val=int(input->readULong(2)); |
619 | 0 | if (val!=id1) |
620 | 0 | f << "ID2=" << val << ","; |
621 | 0 | for (int i=0; i<3; ++i) { |
622 | 0 | val=int(input->readLong(2)); |
623 | 0 | if (val) |
624 | 0 | f << "f" << i << "=" << val << ","; |
625 | 0 | } |
626 | 0 | int dims[2]; |
627 | 0 | for (auto &d : dims) d=int(input->readLong(2)); |
628 | 0 | f << "dim=" << MWAWVec2i(dims[1],dims[0]) << ","; |
629 | 0 | val=int(input->readLong(2)); |
630 | 0 | if (val) |
631 | 0 | f << "f0=" << val << ","; |
632 | 0 | zone.m_hfIds[0]=unsigned(input->readULong(2)); |
633 | 0 | if (zone.m_hfIds[0]) f << "zone[header]=" << zone.m_hfIds[0] << ","; |
634 | 0 | val=int(input->readULong(2)); |
635 | 0 | if (val) |
636 | 0 | f << "ID[header]=" << val << ","; |
637 | 0 | zone.m_hfIds[1]=unsigned(input->readULong(2)); |
638 | 0 | if (zone.m_hfIds[1]) f << "zone[footer]=" << zone.m_hfIds[1] << ","; |
639 | 0 | val=int(input->readULong(2)); |
640 | 0 | if (val) |
641 | 0 | f << "ID[footer]=" << val << ","; |
642 | 0 | for (int i=0; i<8; ++i) { // f3=1 header/footer, c:main? |
643 | 0 | val=int(input->readLong(2)); |
644 | 0 | if (val) f << "f" << i+1 << "=" << val << ","; |
645 | 0 | } |
646 | 0 | if (m_state->m_idToZones.find(unsigned(entry.id()))==m_state->m_idToZones.end()) |
647 | 0 | m_state->m_idToZones[unsigned(entry.id())]=zone; |
648 | 0 | else { |
649 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readZone: zone %d already exists\n", entry.id())); |
650 | 0 | f << "###id,"; |
651 | 0 | } |
652 | 0 | ascFile.addPos(entry.begin()-4); |
653 | 0 | ascFile.addNote(f.str().c_str()); |
654 | 0 | return true; |
655 | 0 | } |
656 | | |
657 | | //////////////////////////////////////////////////////////// |
658 | | // |
659 | | // Low level |
660 | | // |
661 | | //////////////////////////////////////////////////////////// |
662 | | bool JazzWriterParser::readString(MWAWInputStreamPtr input, librevenge::RVNGString &string, long endPos) |
663 | 0 | { |
664 | 0 | string.clear(); |
665 | 0 | if (!input) { |
666 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readString: can not find the input\n")); |
667 | 0 | return false; |
668 | 0 | } |
669 | 0 | auto fontConverter=getFontConverter(); |
670 | 0 | int defaultFont=3; |
671 | 0 | long pos=input->tell(); |
672 | 0 | int n=int(input->readULong(1)); |
673 | 0 | if (!input->checkPosition(pos+1+n) || pos+1+n>endPos) { |
674 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readString: can not read the string length\n")); |
675 | 0 | return false; |
676 | 0 | } |
677 | 0 | for (int i=0; i<n; ++i) { |
678 | 0 | char c=char(input->readULong(1)); |
679 | 0 | int unicode = fontConverter->unicode(defaultFont, static_cast<unsigned char>(c)); |
680 | 0 | if (unicode>0) |
681 | 0 | libmwaw::appendUnicode(uint32_t(unicode), string); |
682 | 0 | else { |
683 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readString: find unknown unicode for char=%d\n", int(c))); |
684 | 0 | } |
685 | 0 | } |
686 | 0 | return true; |
687 | 0 | } |
688 | | |
689 | | //////////////////////////////////////////////////////////// |
690 | | // read the header |
691 | | //////////////////////////////////////////////////////////// |
692 | | bool JazzWriterParser::checkHeader(MWAWHeader *header, bool /*strict*/) |
693 | 0 | { |
694 | 0 | *m_state = JazzWriterParserInternal::State(); |
695 | 0 | if (!getRSRCParser()) |
696 | 0 | return false; |
697 | | // check if the WDOC entry exists |
698 | 0 | MWAWEntry entry = getRSRCParser()->getEntry("WDOC", 257); |
699 | 0 | if (entry.begin()<=0) { // length can be not 0, so ... |
700 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkHeader: can not find the WDOC[257] resource\n")); |
701 | 0 | return false; |
702 | 0 | } |
703 | 0 | if (!getInput()->hasDataFork() || getInput()->size()<=0) { |
704 | | // checkme: is this possible when the document contains only a picture |
705 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkHeader: can not find any data fork\n")); |
706 | 0 | return false; |
707 | 0 | } |
708 | 0 | if (header) |
709 | 0 | header->reset(MWAWDocument::MWAW_T_JAZZLOTUS, 1); |
710 | |
|
711 | 0 | return true; |
712 | 0 | } |
713 | | |
714 | | //////////////////////////////////////////////////////////// |
715 | | // send the data |
716 | | //////////////////////////////////////////////////////////// |
717 | | bool JazzWriterParser::sendZone(unsigned zId) |
718 | 0 | { |
719 | 0 | if (!getTextListener()) { |
720 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkHeader: can not find the main listener\n")); |
721 | 0 | return false; |
722 | 0 | } |
723 | 0 | auto it=m_state->m_idToZones.find(zId); |
724 | 0 | if (it==m_state->m_idToZones.end()) { |
725 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkZones: can not find the %x zone\n", zId)); |
726 | 0 | return false; |
727 | 0 | } |
728 | | |
729 | 0 | auto input=getInput(); |
730 | 0 | input->seek(it->second.m_entry.begin(), librevenge::RVNG_SEEK_SET); |
731 | 0 | sendParagraph(it->second.m_paragraphId); |
732 | 0 | return true; |
733 | 0 | } |
734 | | |
735 | | bool JazzWriterParser::sendParagraph(unsigned pId) |
736 | 0 | { |
737 | 0 | auto it=m_state->m_idToParagraphs.find(pId); |
738 | 0 | if (it==m_state->m_idToParagraphs.end()) { |
739 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::checkZones: can not find the %x paragraph\n", pId)); |
740 | 0 | return false; |
741 | 0 | } |
742 | 0 | auto const ¶=it->second; |
743 | 0 | getTextListener()->setParagraph(para.m_paragraph); |
744 | 0 | sendPLC(para.m_plcId); |
745 | 0 | if (para.m_nextParagraphId) |
746 | 0 | sendParagraph(para.m_nextParagraphId); |
747 | 0 | return true; |
748 | 0 | } |
749 | | |
750 | | bool JazzWriterParser::sendPLC(unsigned pId) |
751 | 0 | { |
752 | 0 | MWAWRSRCParserPtr rsrcParser = getRSRCParser(); |
753 | 0 | if (!rsrcParser) { |
754 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::sendPLC: can not find the rsrc parser\n")); |
755 | 0 | return false; |
756 | 0 | } |
757 | 0 | auto listener=getTextListener(); |
758 | 0 | if (!listener) { |
759 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::sendPLC: can not find the text listener\n")); |
760 | 0 | return false; |
761 | 0 | } |
762 | 0 | MWAWInputStreamPtr input = getInput(); |
763 | 0 | MWAWInputStreamPtr rInput = rsrcInput(); |
764 | 0 | libmwaw::DebugFile &rAscFile = rsrcAscii(); |
765 | 0 | libmwaw::DebugStream f; |
766 | |
|
767 | 0 | MWAWEntry entry=rsrcParser->getEntry("WSCR", int(pId)); |
768 | 0 | if (!entry.valid()) { |
769 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::sendPLC: can not find the %x plc\n", pId)); |
770 | 0 | return false; |
771 | 0 | } |
772 | | |
773 | 0 | f << "Entries(PLC)[" << entry.id() << "]:"; |
774 | 0 | rAscFile.addPos(entry.begin()-4); |
775 | 0 | rAscFile.addNote(f.str().c_str()); |
776 | |
|
777 | 0 | rInput->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
778 | 0 | int N=int(entry.length()/6); |
779 | 0 | int val; |
780 | 0 | auto fontConverter=getFontConverter(); |
781 | 0 | for (int i=0; i<N; ++i) { |
782 | 0 | long rPos=rInput->tell(); |
783 | 0 | f.str(""); |
784 | 0 | f << "PLC-" << i << ":"; |
785 | 0 | int wh=int(rInput->readLong(2)); |
786 | 0 | int numChar=0; |
787 | 0 | switch (wh) { |
788 | 0 | case 1: |
789 | 0 | numChar=int(rInput->readULong(4)); |
790 | 0 | f << "num=" << numChar << ","; |
791 | 0 | break; |
792 | 0 | case 2: { |
793 | 0 | MWAWFont font; |
794 | 0 | f << "font,"; |
795 | 0 | f << "h=" << rInput->readULong(1) << ","; |
796 | 0 | font.setId(int(rInput->readULong(1))); |
797 | 0 | font.setSize(float(rInput->readULong(1))); |
798 | 0 | val=int(rInput->readULong(1)); |
799 | 0 | uint32_t flags = 0; |
800 | 0 | if (val&0x1) flags |= MWAWFont::boldBit; |
801 | 0 | if (val&0x2) flags |= MWAWFont::italicBit; |
802 | 0 | if (val&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple); |
803 | 0 | if (val&0x8) flags |= MWAWFont::embossBit; |
804 | 0 | if (val&0x10) flags |= MWAWFont::shadowBit; |
805 | 0 | font.setFlags(flags); |
806 | 0 | val &= 0xe0; |
807 | 0 | f << "font=[" << font.getDebugString(fontConverter) << "]"; |
808 | 0 | if (val) |
809 | 0 | f << "fl=" << std::hex << val << std::dec << ","; |
810 | 0 | listener->setFont(font); |
811 | 0 | break; |
812 | 0 | } |
813 | 0 | case 3: { |
814 | 0 | val=int(rInput->readULong(2)); |
815 | 0 | bool sent=false; |
816 | 0 | if (val>=0xFFF0) { |
817 | 0 | f << "field" << (0xFFFF-val) << ","; |
818 | 0 | sent=true; |
819 | 0 | switch (0xFFFF-val) { |
820 | 0 | case 0: { |
821 | 0 | MWAWField field(MWAWField::Date); |
822 | 0 | field.m_DTFormat="%B %d, %Y"; |
823 | 0 | listener->insertField(field); |
824 | 0 | break; |
825 | 0 | } |
826 | 0 | case 1: { |
827 | 0 | MWAWField field(MWAWField::Time); |
828 | 0 | field.m_DTFormat="%I:%M %p"; |
829 | 0 | listener->insertField(field); |
830 | 0 | break; |
831 | 0 | } |
832 | 0 | case 2: |
833 | 0 | listener->insertField(MWAWField(MWAWField::PageNumber)); |
834 | 0 | break; |
835 | 0 | default: |
836 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: find unknown field\n")); |
837 | 0 | f << "###"; |
838 | 0 | sent=false; |
839 | 0 | break; |
840 | 0 | } |
841 | 0 | } |
842 | 0 | else |
843 | 0 | f << "link[id]=" << val << ","; |
844 | 0 | f << "unk=" << int(rInput->readULong(1)) << ","; // 8-1e |
845 | 0 | numChar=int(rInput->readULong(1)); |
846 | 0 | f << "len=" << numChar << ","; |
847 | 0 | if (sent) { |
848 | 0 | input->seek(numChar, librevenge::RVNG_SEEK_CUR); |
849 | 0 | numChar=0; |
850 | 0 | } |
851 | 0 | break; |
852 | 0 | } |
853 | 0 | case 5: { |
854 | 0 | f << "pict[link],"; |
855 | 0 | if (i+1>=N) { |
856 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: the zone seems too short\n")); |
857 | 0 | f << "###"; |
858 | 0 | break; |
859 | 0 | } |
860 | 0 | f << "link[id]=" << int(rInput->readULong(2)) << ","; |
861 | 0 | unsigned pictId=unsigned(rInput->readULong(2)); |
862 | 0 | f << "pict[id]=" << pictId << ","; |
863 | 0 | int dim[2]; |
864 | 0 | for (auto &d : dim) d=int(rInput->readULong(2)); |
865 | 0 | f << "sz=" << MWAWVec2i(dim[1], dim[0]) << ","; |
866 | 0 | int xPos=int(rInput->readLong(2)); |
867 | 0 | f << "xPos=" << xPos << ","; // related to baseline? |
868 | 0 | ++i; |
869 | 0 | rPos+=6; |
870 | 0 | MWAWEmbeddedObject obj; |
871 | 0 | if (!getPicture(pictId, obj) || obj.isEmpty()) { |
872 | 0 | f << "##pictId"; |
873 | 0 | break; |
874 | 0 | } |
875 | | // TODO: use xPos |
876 | 0 | MWAWPosition position(MWAWVec2f(0,0), MWAWVec2f(float(dim[1]), float(dim[0])), librevenge::RVNG_POINT); |
877 | 0 | position.setRelativePosition(MWAWPosition::Char); |
878 | 0 | listener->insertPicture(position, obj); |
879 | 0 | listener->insertEOL(); // one picture by line |
880 | 0 | break; |
881 | 0 | } |
882 | 0 | default: |
883 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: find unknown type\n")); |
884 | 0 | f << "###type" << wh << ","; |
885 | 0 | break; |
886 | 0 | } |
887 | 0 | rAscFile.addPos(rPos); |
888 | 0 | rAscFile.addNote(f.str().c_str()); |
889 | 0 | if (rInput->tell()!=rPos+6) |
890 | 0 | rAscFile.addDelimiter(rInput->tell(),'|'); |
891 | 0 | rInput->seek(rPos+6, librevenge::RVNG_SEEK_SET); |
892 | | |
893 | | // now read/send the character |
894 | 0 | if (numChar<=0) continue; |
895 | 0 | long pos=input->tell(); |
896 | 0 | if (!input->checkPosition(pos+numChar)) { |
897 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: can not find some character\n")); |
898 | 0 | break; |
899 | 0 | } |
900 | 0 | for (int c=0; c<numChar; ++c) { |
901 | 0 | auto ch=(unsigned char)input->readULong(1); |
902 | 0 | switch (ch) { |
903 | 0 | case 0x9: |
904 | 0 | listener->insertChar(ch); |
905 | 0 | break; |
906 | 0 | case 0xc: |
907 | 0 | listener->insertBreak(MWAWTextListener::PageBreak); |
908 | 0 | break; |
909 | 0 | case 0xd: |
910 | 0 | listener->insertEOL(); |
911 | 0 | break; |
912 | 0 | default: |
913 | 0 | if (ch<=0x1f) { |
914 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: find bad character %d at pos=0x%lx\n", int(c), input->tell())); |
915 | 0 | break; |
916 | 0 | } |
917 | 0 | listener->insertCharacter(ch); |
918 | 0 | } |
919 | 0 | } |
920 | 0 | } |
921 | 0 | rAscFile.addPos(rInput->tell()); |
922 | 0 | rAscFile.addNote("PLC-end:"); |
923 | 0 | val=int(rInput->readLong(2)); |
924 | 0 | if (val) |
925 | 0 | f << "##f0=" << val << ","; |
926 | 0 | return true; |
927 | 0 | } |
928 | | |
929 | | bool JazzWriterParser::getPicture(unsigned pId, MWAWEmbeddedObject &obj) |
930 | 0 | { |
931 | 0 | MWAWRSRCParserPtr rsrcParser = getRSRCParser(); |
932 | 0 | if (!rsrcParser) { |
933 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::getPicture: can not find the rsrc parser\n")); |
934 | 0 | return false; |
935 | 0 | } |
936 | 0 | MWAWInputStreamPtr input = rsrcInput(); |
937 | 0 | libmwaw::DebugFile &ascFile = rsrcAscii(); |
938 | |
|
939 | 0 | MWAWEntry entry=rsrcParser->getEntry("PICT", int(pId)); |
940 | 0 | if (!entry.valid()) { |
941 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::getPicture: can not find the %x picture\n", pId)); |
942 | 0 | return false; |
943 | 0 | } |
944 | 0 | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
945 | 0 | std::shared_ptr<MWAWPict> pict(MWAWPictData::get(input, int(entry.length()))); |
946 | 0 | if (!pict || !pict->getBinary(obj)) { |
947 | 0 | MWAW_DEBUG_MSG(("JazzWriterParser::getPicture: can not read the %x picture\n", pId)); |
948 | 0 | return false; |
949 | 0 | } |
950 | | |
951 | 0 | ascFile.skipZone(entry.begin(), entry.end()); |
952 | 0 | return true; |
953 | 0 | } |
954 | | |
955 | | // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: |