/src/libmwaw/src/lib/MsWksParser.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 <sstream> |
38 | | |
39 | | #include <librevenge/librevenge.h> |
40 | | |
41 | | |
42 | | #include "MWAWTextListener.hxx" |
43 | | #include "MWAWHeader.hxx" |
44 | | #include "MWAWFont.hxx" |
45 | | #include "MWAWFontConverter.hxx" |
46 | | #include "MWAWPictMac.hxx" |
47 | | #include "MWAWPrinter.hxx" |
48 | | #include "MWAWSubDocument.hxx" |
49 | | |
50 | | #include "MsWksGraph.hxx" |
51 | | #include "MsWksDocument.hxx" |
52 | | #include "MsWks3Text.hxx" |
53 | | #include "MsWks4Zone.hxx" |
54 | | |
55 | | #include "MsWksParser.hxx" |
56 | | |
57 | | /** Internal: the structures of a MsWksParser */ |
58 | | namespace MsWksParserInternal |
59 | | { |
60 | | //////////////////////////////////////// |
61 | | //! Internal: the state of a MsWksParser |
62 | | struct State { |
63 | | //! constructor |
64 | | State() |
65 | 181k | : m_mn0Parser() |
66 | 181k | , m_actPage(0) |
67 | 181k | , m_numPages(0) |
68 | 181k | { |
69 | 181k | } |
70 | | |
71 | | std::shared_ptr<MsWks4Zone> m_mn0Parser /**parser of main text ole (v4 document)*/; |
72 | | int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */; |
73 | | }; |
74 | | } |
75 | | |
76 | | |
77 | | //////////////////////////////////////////////////////////// |
78 | | // constructor/destructor, ... |
79 | | //////////////////////////////////////////////////////////// |
80 | | MsWksParser::MsWksParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header) |
81 | 181k | : MWAWTextParser(input, rsrcParser, header) |
82 | 181k | , m_state(new MsWksParserInternal::State) |
83 | 181k | , m_listZones() |
84 | 181k | , m_document() |
85 | 181k | { |
86 | 181k | if (input && input->isStructured()) { // potential v4 file |
87 | 14.6k | MWAWInputStreamPtr mainOle = getInput()->getSubStreamByName("MN0"); |
88 | 14.6k | if (!mainOle) return; |
89 | | |
90 | 14.6k | m_state->m_mn0Parser.reset(new MsWks4Zone(mainOle, getParserState(), *this, "MN0")); |
91 | 14.6k | m_document=m_state->m_mn0Parser->m_document; |
92 | 14.6k | } |
93 | 166k | else |
94 | 166k | m_document.reset(new MsWksDocument(input, *this)); |
95 | 181k | m_document->m_newPage=static_cast<MsWksDocument::NewPage>(&MsWksParser::newPage); |
96 | 181k | init(); |
97 | 181k | } |
98 | | |
99 | | MsWksParser::~MsWksParser() |
100 | 181k | { |
101 | 181k | } |
102 | | |
103 | | void MsWksParser::init() |
104 | 181k | { |
105 | 181k | resetTextListener(); |
106 | 181k | setAsciiName("main-1"); |
107 | | |
108 | | // reduce the margin (in case, the page is not defined) |
109 | 181k | getPageSpan().setMargins(0.1); |
110 | 181k | } |
111 | | |
112 | | //////////////////////////////////////////////////////////// |
113 | | // new page |
114 | | //////////////////////////////////////////////////////////// |
115 | | void MsWksParser::newPage(int number, bool softBreak) |
116 | 33.8k | { |
117 | 33.8k | if (m_state->m_mn0Parser) // v4 document |
118 | 2.72k | return m_state->m_mn0Parser->newPage(number, softBreak); |
119 | | |
120 | 31.1k | if (number <= m_state->m_actPage || number > m_state->m_numPages) |
121 | 0 | return; |
122 | | |
123 | 70.3k | while (m_state->m_actPage < number) { |
124 | 39.2k | m_state->m_actPage++; |
125 | 39.2k | if (!getTextListener() || m_state->m_actPage == 1) |
126 | 8.12k | continue; |
127 | 31.1k | if (softBreak) |
128 | 26.8k | getTextListener()->insertBreak(MWAWTextListener::SoftPageBreak); |
129 | 4.24k | else |
130 | 4.24k | getTextListener()->insertBreak(MWAWTextListener::PageBreak); |
131 | 31.1k | } |
132 | 31.1k | } |
133 | | |
134 | | //////////////////////////////////////////////////////////// |
135 | | // the parser |
136 | | //////////////////////////////////////////////////////////// |
137 | | void MsWksParser::parse(librevenge::RVNGTextInterface *docInterface) |
138 | 66.8k | { |
139 | 66.8k | if (!checkHeader(nullptr) || !m_document || !m_document->getInput()) throw(libmwaw::ParseException()); |
140 | | |
141 | 66.8k | int const vers=version(); |
142 | 66.8k | bool ok = true; |
143 | 66.8k | try { |
144 | | // create the asciiFile for v1-v3 document |
145 | 66.8k | if (vers<=3) |
146 | 59.8k | m_document->initAsciiFile(asciiName()); |
147 | | |
148 | 66.8k | checkHeader(nullptr); |
149 | 66.8k | ok = createZones(); |
150 | 66.8k | if (ok) { |
151 | 66.4k | createDocument(docInterface); |
152 | 66.4k | if (vers<=3) |
153 | 59.8k | m_document->sendZone(MsWksDocument::Z_MAIN); |
154 | 6.55k | else |
155 | 6.55k | m_state->m_mn0Parser->readContentZones(MWAWEntry(), true); |
156 | 66.4k | flushExtra(); |
157 | 66.4k | } |
158 | 66.8k | m_document->ascii().reset(); |
159 | 66.8k | } |
160 | 66.8k | catch (...) { |
161 | 369 | MWAW_DEBUG_MSG(("MsWksParser::parse: exception catched when parsing\n")); |
162 | 369 | ok = false; |
163 | 369 | } |
164 | | |
165 | 66.8k | resetTextListener(); |
166 | 66.8k | if (!ok) throw(libmwaw::ParseException()); |
167 | 66.8k | } |
168 | | |
169 | | //////////////////////////////////////////////////////////// |
170 | | // flush the extra data |
171 | | //////////////////////////////////////////////////////////// |
172 | | void MsWksParser::flushExtra() |
173 | 66.4k | { |
174 | 66.4k | MWAWTextListenerPtr listener=getTextListener(); |
175 | 66.4k | if (!listener) return; |
176 | | |
177 | 66.4k | if (version()<=3) { |
178 | 59.8k | m_document->getTextParser3()->flushExtra(); |
179 | 59.8k | m_document->getGraphParser()->flushExtra(); |
180 | 59.8k | return; |
181 | 59.8k | } |
182 | | |
183 | | // send the final data |
184 | 6.55k | std::vector<std::string> unparsedOLEs=m_document->getUnparsedOLEZones(); |
185 | 6.55k | if (unparsedOLEs.empty()) return; |
186 | | |
187 | 1.94k | bool first = true; |
188 | 3.24k | for (auto &name : unparsedOLEs) { |
189 | 3.24k | MWAWInputStreamPtr ole = getInput()->getSubStreamByName(name.c_str()); |
190 | 3.24k | if (!ole.get()) { |
191 | 0 | MWAW_DEBUG_MSG(("MsWksParser::flushExtra: error: can not find OLE part: \"%s\"\n", name.c_str())); |
192 | 0 | continue; |
193 | 0 | } |
194 | | |
195 | 3.24k | std::shared_ptr<MsWks4Zone> newParser(new MsWks4Zone(ole, getParserState(), *this, name)); |
196 | 3.24k | bool ok = true; |
197 | 3.24k | try { |
198 | 3.24k | ok = newParser->createZones(false); |
199 | 3.24k | if (ok) { |
200 | | // FIXME: add a message here |
201 | 1.81k | if (first) { |
202 | 1.37k | first = false; |
203 | 1.37k | listener->setFont(MWAWFont(20,20)); |
204 | 1.37k | librevenge::RVNGString message("--------- The original document has some extra ole: -------- "); |
205 | 1.37k | listener->insertUnicodeString(message); |
206 | 1.37k | listener->insertEOL(); |
207 | 1.37k | } |
208 | 1.81k | newParser->readContentZones(MWAWEntry(), false); |
209 | 1.81k | } |
210 | 3.24k | } |
211 | 3.24k | catch (...) { |
212 | 311 | ok = false; |
213 | 311 | } |
214 | | |
215 | 3.24k | if (ok) continue; |
216 | 1.43k | MWAW_DEBUG_MSG(("MsWksParser::flushExtra: error: can not parse OLE: \"%s\"\n", name.c_str())); |
217 | 1.43k | } |
218 | 1.94k | } |
219 | | |
220 | | //////////////////////////////////////////////////////////// |
221 | | // create the document |
222 | | //////////////////////////////////////////////////////////// |
223 | | void MsWksParser::createDocument(librevenge::RVNGTextInterface *documentInterface) |
224 | 66.4k | { |
225 | 66.4k | if (!documentInterface || getTextListener()) { |
226 | 0 | MWAW_DEBUG_MSG(("MsWksParser::createDocument: listener already exist\n")); |
227 | 0 | throw (libmwaw::ParseException()); |
228 | 0 | } |
229 | | |
230 | 66.4k | if (m_state->m_mn0Parser) { |
231 | 6.55k | MWAWTextListenerPtr listener = m_state->m_mn0Parser->createListener(documentInterface); |
232 | 6.55k | if (!listener) { |
233 | 0 | MWAW_DEBUG_MSG(("MsWksParser::parse: can not create a listener\n")); |
234 | 0 | throw (libmwaw::ParseException()); |
235 | 0 | } |
236 | 6.55k | setTextListener(listener); |
237 | 6.55k | listener->startDocument(); |
238 | 6.55k | return; |
239 | 6.55k | } |
240 | | |
241 | 59.8k | std::vector<MWAWPageSpan> pageList; |
242 | 59.8k | m_state->m_actPage = 0; |
243 | 59.8k | m_document->getPageSpanList(pageList, m_state->m_numPages); |
244 | 59.8k | MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface)); |
245 | 59.8k | setTextListener(listen); |
246 | 59.8k | listen->startDocument(); |
247 | | // time to send page information the graph parser and the text parser |
248 | 59.8k | m_document->getGraphParser()->setPageLeftTop |
249 | 59.8k | (MWAWVec2f(72.f *float(getPageSpan().getMarginLeft()), |
250 | 59.8k | 72.f *float(getPageSpan().getMarginTop())+m_document->getHeaderFooterHeight(true))); |
251 | 59.8k | } |
252 | | |
253 | | //////////////////////////////////////////////////////////// |
254 | | // |
255 | | // Intermediate level |
256 | | // |
257 | | //////////////////////////////////////////////////////////// |
258 | | bool MsWksParser::createZones() |
259 | 66.8k | { |
260 | 66.8k | MWAWInputStreamPtr input = m_document->getInput(); |
261 | 66.8k | if (!input) return false; |
262 | | |
263 | 66.8k | int const vers=version(); |
264 | 66.8k | if (vers==4) { |
265 | 6.97k | if (!m_state->m_mn0Parser || !m_state->m_mn0Parser->createZones(true)) |
266 | 49 | return false; |
267 | 6.92k | m_state->m_mn0Parser->m_document->createOLEZones(getInput()); |
268 | 6.92k | return true; |
269 | 6.97k | } |
270 | | |
271 | 59.8k | long pos = input->tell(); |
272 | 59.8k | if (vers>=3) { |
273 | 29.8k | bool ok = true; |
274 | 29.8k | if (m_document->hasHeader()) |
275 | 20.4k | ok = m_document->readGroupHeaderFooter(true,99); |
276 | 29.8k | if (ok) pos = input->tell(); |
277 | 4.88k | else input->seek(pos, librevenge::RVNG_SEEK_SET); |
278 | 29.8k | if (ok && m_document->hasFooter()) |
279 | 13.6k | ok = m_document->readGroupHeaderFooter(false,99); |
280 | 29.8k | if (!ok) input->seek(pos, librevenge::RVNG_SEEK_SET); |
281 | 29.8k | } |
282 | | |
283 | 59.8k | auto &typeZoneMap=m_document->getTypeZoneMap(); |
284 | 59.8k | auto mainId=int(typeZoneMap.size()); // FIXME: use m_document->getNewZoneId(); |
285 | 59.8k | typeZoneMap.insert(std::multimap<int, MsWksDocument::Zone>::value_type |
286 | 59.8k | (MsWksDocument::Z_MAIN,MsWksDocument::Zone(MsWksDocument::Z_MAIN, mainId))); |
287 | 59.8k | auto &mainZone = typeZoneMap.find(MsWksDocument::Z_MAIN)->second; |
288 | | |
289 | 59.8k | libmwaw::DebugFile &ascFile = m_document->ascii(); |
290 | | /* now normally |
291 | | in v1: a list of pictures, document info zone, ..., without any way to know the limit |
292 | | in v2: a group zone (which regroup the graphic) and a document info zone |
293 | | in v3: a document info zone and a group zone (the graphic zone) |
294 | | */ |
295 | 345k | while (!input->isEnd()) { |
296 | 343k | pos = input->tell(); |
297 | 343k | if (!m_document->readZone(mainZone)) { |
298 | 55.9k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
299 | 55.9k | break; |
300 | 55.9k | } |
301 | 287k | if (input->tell() <= pos) { |
302 | 1.84k | MWAW_DEBUG_MSG(("MsWksParser::createZones: no data were read from zone at pos %ld\n", pos)); |
303 | 1.84k | break; |
304 | 1.84k | } |
305 | 287k | } |
306 | | |
307 | 59.8k | mainZone.m_textId = m_document->getTextParser3()->createZones(-1, true); |
308 | | |
309 | 59.8k | pos = input->tell(); |
310 | | |
311 | 59.8k | if (!input->isEnd()) { |
312 | 56.2k | ascFile.addPos(pos); |
313 | 56.2k | ascFile.addNote("Entries(End)"); |
314 | 56.2k | ascFile.addPos(pos+100); |
315 | 56.2k | ascFile.addNote("_"); |
316 | 56.2k | } |
317 | | |
318 | | // ok, prepare the data |
319 | 59.8k | m_state->m_numPages = 1; |
320 | 59.8k | std::vector<int> linesH, pagesH; |
321 | 59.8k | if (m_document->getTextParser3()->getLinesPagesHeight(mainZone.m_textId, linesH, pagesH)) |
322 | 22.8k | m_document->getGraphParser()->computePositions(mainId, linesH, pagesH); |
323 | | |
324 | 59.8k | return true; |
325 | 66.8k | } |
326 | | |
327 | | |
328 | | //////////////////////////////////////////////////////////// |
329 | | // |
330 | | // Low level |
331 | | // |
332 | | //////////////////////////////////////////////////////////// |
333 | | |
334 | | //////////////////////////////////////////////////////////// |
335 | | // read the header |
336 | | //////////////////////////////////////////////////////////// |
337 | | bool MsWksParser::checkHeader(MWAWHeader *header, bool strict) |
338 | 248k | { |
339 | 248k | MWAWInputStreamPtr &input= getInput(); |
340 | 248k | if (!input || !input->hasDataFork() || !m_document) |
341 | 0 | return false; |
342 | | |
343 | 248k | if (!input->isStructured()) { |
344 | | // first check v1-v3 file |
345 | 226k | if (!m_document->checkHeader3(header, strict)) return false; |
346 | 185k | if (m_document->getKind() != MWAWDocument::MWAW_K_TEXT) |
347 | 0 | return false; |
348 | 185k | if (version() < 1 || version() > 3) |
349 | 146 | return false; |
350 | 185k | return true; |
351 | 185k | } |
352 | | |
353 | | // now check for version 4 file |
354 | 21.6k | MWAWInputStreamPtr mmOle = input->getSubStreamByName("MM"); |
355 | 21.6k | if (!mmOle) return false; |
356 | 21.5k | mmOle->seek(0, librevenge::RVNG_SEEK_SET); |
357 | 21.5k | if (mmOle->readULong(2) != 0x444e) return false; |
358 | | |
359 | 21.3k | MWAWInputStreamPtr mainOle = m_document->getInput(); |
360 | 21.3k | if (!mainOle) return false; |
361 | 21.3k | mainOle->seek(0, librevenge::RVNG_SEEK_SET); |
362 | 21.3k | if (mainOle->readULong(4)!=0x43484e4b) // CHNKINK |
363 | 0 | return false; |
364 | 21.3k | setVersion(4); |
365 | 21.3k | if (header) |
366 | 7.45k | header->reset(MWAWDocument::MWAW_T_MICROSOFTWORKS, 4); |
367 | 21.3k | return true; |
368 | | |
369 | 21.3k | } |
370 | | |
371 | | // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: |