/src/libmwaw/src/lib/MsWksDRParser.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 "MWAWGraphicListener.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 "MsWksDRParser.hxx" |
56 | | |
57 | | /** Internal: the structures of a MsWksDRParser */ |
58 | | namespace MsWksDRParserInternal |
59 | | { |
60 | | //////////////////////////////////////// |
61 | | //! Internal: the state of a MsWksDRParser |
62 | | struct State { |
63 | | //! constructor |
64 | | State() |
65 | 174k | : m_mainZoneId(0) |
66 | 174k | , m_actPage(0) |
67 | 174k | , m_numPages(0) |
68 | 174k | { |
69 | 174k | } |
70 | | |
71 | | /** the main zone */ |
72 | | int m_mainZoneId; |
73 | | int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */; |
74 | | }; |
75 | | } |
76 | | |
77 | | |
78 | | //////////////////////////////////////////////////////////// |
79 | | // constructor/destructor, ... |
80 | | //////////////////////////////////////////////////////////// |
81 | | MsWksDRParser::MsWksDRParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header) |
82 | 73.5k | : MWAWGraphicParser(input, rsrcParser, header) |
83 | 73.5k | , m_state() |
84 | 73.5k | , m_listZones() |
85 | 73.5k | , m_document() |
86 | 73.5k | { |
87 | 73.5k | MWAWInputStreamPtr mainInput=input; |
88 | 73.5k | if (input->isStructured()) { |
89 | 4.28k | MWAWInputStreamPtr mainOle = input->getSubStreamByName("MN0"); |
90 | 4.28k | if (mainOle) |
91 | 4.28k | mainInput=mainOle; |
92 | 4.28k | } |
93 | 73.5k | m_document.reset(new MsWksDocument(mainInput, *this)); |
94 | 73.5k | init(); |
95 | 73.5k | } |
96 | | |
97 | | MsWksDRParser::~MsWksDRParser() |
98 | 73.5k | { |
99 | 73.5k | } |
100 | | |
101 | | void MsWksDRParser::init() |
102 | 73.5k | { |
103 | 73.5k | resetGraphicListener(); |
104 | 73.5k | setAsciiName("main-1"); |
105 | | |
106 | 73.5k | m_state.reset(new MsWksDRParserInternal::State); |
107 | | |
108 | | // reduce the margin (in case, the page is not defined) |
109 | 73.5k | getPageSpan().setMargins(0.1); |
110 | | |
111 | 73.5k | m_document->m_newPage=static_cast<MsWksDocument::NewPage>(&MsWksDRParser::newPage); |
112 | 73.5k | } |
113 | | |
114 | | //////////////////////////////////////////////////////////// |
115 | | // new page |
116 | | //////////////////////////////////////////////////////////// |
117 | | void MsWksDRParser::newPage(int number, bool softBreak) |
118 | 3.42M | { |
119 | 3.42M | if (!getGraphicListener() || number < m_state->m_actPage || number > m_state->m_numPages) |
120 | 46 | return; |
121 | | |
122 | 3.42M | long pos = m_document->getInput()->tell(); |
123 | 3.42M | int const vers=version(); |
124 | 6.85M | while (m_state->m_actPage <= number) { |
125 | 3.42M | if (++m_state->m_actPage!=1) { |
126 | 3.42M | if (softBreak) |
127 | 0 | getGraphicListener()->insertBreak(MWAWGraphicListener::SoftPageBreak); |
128 | 3.42M | else |
129 | 3.42M | getGraphicListener()->insertBreak(MWAWGraphicListener::PageBreak); |
130 | 3.42M | } |
131 | | // first, send the background |
132 | 3.42M | if (vers==4) { |
133 | 947k | MsWksGraph::SendData sendData; |
134 | 947k | sendData.m_type = MsWksGraph::SendData::RBIL; |
135 | 947k | sendData.m_anchor = MWAWPosition::Page; |
136 | 947k | sendData.m_id = 0; |
137 | 947k | sendData.m_page = -1; |
138 | 947k | m_document->getGraphParser()->sendObjects(sendData); |
139 | 947k | } |
140 | 3.42M | MsWksGraph::SendData sendData; |
141 | 3.42M | sendData.m_type = MsWksGraph::SendData::RBDR; |
142 | 3.42M | sendData.m_anchor = MWAWPosition::Page; |
143 | 3.42M | sendData.m_page = m_state->m_actPage; |
144 | 3.42M | m_document->getGraphParser()->sendObjects(sendData); |
145 | 3.42M | } |
146 | 3.42M | m_document->getInput()->seek(pos, librevenge::RVNG_SEEK_SET); |
147 | 3.42M | } |
148 | | |
149 | | //////////////////////////////////////////////////////////// |
150 | | // the parser |
151 | | //////////////////////////////////////////////////////////// |
152 | | void MsWksDRParser::parse(librevenge::RVNGDrawingInterface *docInterface) |
153 | 27.0k | { |
154 | 27.0k | if (!checkHeader(nullptr) || !m_document || !m_document->getInput()) throw(libmwaw::ParseException()); |
155 | 27.0k | bool ok = true; |
156 | 27.0k | try { |
157 | | // create the asciiFile |
158 | 27.0k | m_document->initAsciiFile(asciiName()); |
159 | | |
160 | 27.0k | checkHeader(nullptr); |
161 | 27.0k | ok = createZones(); |
162 | 27.0k | if (ok) { |
163 | 9.67k | createDocument(docInterface); |
164 | 3.43M | for (int i=0; i< m_state->m_numPages; ++i) |
165 | 3.42M | newPage(i); |
166 | | #if 0 && defined(DEBUG) |
167 | | if (version()<=3) |
168 | | m_document->getTextParser3()->flushExtra(); |
169 | | m_document->getGraphParser()->flushExtra(); |
170 | | #endif |
171 | 9.67k | } |
172 | 27.0k | m_document->ascii().reset(); |
173 | 27.0k | } |
174 | 27.0k | catch (...) { |
175 | 0 | MWAW_DEBUG_MSG(("MsWksDRParser::parse: exception catched when parsing\n")); |
176 | 0 | ok = false; |
177 | 0 | } |
178 | | |
179 | 27.0k | resetGraphicListener(); |
180 | 27.0k | if (!ok) throw(libmwaw::ParseException()); |
181 | 27.0k | } |
182 | | |
183 | | //////////////////////////////////////////////////////////// |
184 | | // create the document |
185 | | //////////////////////////////////////////////////////////// |
186 | | void MsWksDRParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface) |
187 | 9.67k | { |
188 | 9.67k | if (!documentInterface) return; |
189 | 9.67k | if (getGraphicListener()) { |
190 | 0 | MWAW_DEBUG_MSG(("MsWksDRParser::createDocument: listener already exist\n")); |
191 | 0 | return; |
192 | 0 | } |
193 | | |
194 | 9.67k | std::vector<MWAWPageSpan> pageList; |
195 | 9.67k | m_state->m_actPage = 0; |
196 | 9.67k | m_document->getPageSpanList(pageList, m_state->m_numPages); |
197 | 9.67k | MWAWGraphicListenerPtr listen(new MWAWGraphicListener(*getParserState(), pageList, documentInterface)); |
198 | 9.67k | setGraphicListener(listen); |
199 | 9.67k | listen->startDocument(); |
200 | | // time to send page information the graph parser and the text parser |
201 | 9.67k | m_document->getGraphParser()->setPageLeftTop |
202 | 9.67k | (MWAWVec2f(72.f *float(getPageSpan().getMarginLeft()), |
203 | 9.67k | 72.f *float(getPageSpan().getMarginTop())+m_document->getHeaderFooterHeight(true))); |
204 | 9.67k | } |
205 | | |
206 | | //////////////////////////////////////////////////////////// |
207 | | // |
208 | | // Intermediate level |
209 | | // |
210 | | //////////////////////////////////////////////////////////// |
211 | | bool MsWksDRParser::createZones() |
212 | 27.0k | { |
213 | 27.0k | if (getInput()->isStructured()) |
214 | 1.95k | m_document->createOLEZones(getInput()); |
215 | 27.0k | MWAWInputStreamPtr input = m_document->getInput(); |
216 | 27.0k | long pos = input->tell(); |
217 | 27.0k | if (!m_document->readDocumentInfo(0x9a)) |
218 | 7.37k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
219 | 27.0k | if (m_document->hasHeader() && !m_document->readGroupHeaderFooter(true,99)) |
220 | 1.77k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
221 | 27.0k | pos = input->tell(); |
222 | 27.0k | if (m_document->hasFooter() && !m_document->readGroupHeaderFooter(false,99)) |
223 | 8.43k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
224 | | |
225 | 27.0k | if (!readDrawHeader()) return false; |
226 | | |
227 | 11.8k | libmwaw::DebugFile &ascFile = m_document->ascii(); |
228 | 11.8k | auto &typeZoneMap=m_document->getTypeZoneMap(); |
229 | 11.8k | MWAWEntry group; |
230 | | |
231 | | // now the main group of draw shape |
232 | 11.8k | m_state->m_mainZoneId= version()==4 ? 0 : m_document->getNewZoneId(); |
233 | 11.8k | typeZoneMap.insert(std::multimap<int, MsWksDocument::Zone>::value_type |
234 | 11.8k | (MsWksDocument::Z_MAIN,MsWksDocument::Zone(MsWksDocument::Z_MAIN, m_state->m_mainZoneId))); |
235 | 11.8k | if (version()==4) { |
236 | 3.43k | pos=input->tell(); |
237 | 3.43k | int id=m_document->getNewZoneId(); |
238 | 3.43k | typeZoneMap.insert(std::multimap<int, MsWksDocument::Zone>::value_type |
239 | 3.43k | (MsWksDocument::Z_NONE,MsWksDocument::Zone(MsWksDocument::Z_NONE, id))); |
240 | 3.43k | group.setId(m_state->m_mainZoneId); |
241 | 3.43k | group.setName("RBIL"); |
242 | 3.43k | if (!m_document->m_graphParser->readRB(input,group,1)) { |
243 | 571 | MWAW_DEBUG_MSG(("MsWksDRParser::createZones: can not read RBIL group\n")); |
244 | 571 | ascFile.addPos(pos); |
245 | 571 | ascFile.addNote("Entries(RBIL):###"); |
246 | 571 | return false; |
247 | 571 | } |
248 | 3.43k | } |
249 | | |
250 | 11.2k | pos=input->tell(); |
251 | 11.2k | group.setId(m_state->m_mainZoneId); |
252 | 11.2k | group.setName("RBDR"); |
253 | 11.2k | if (!m_document->m_graphParser->readRB(input,group,1)) { |
254 | 1.60k | MWAW_DEBUG_MSG(("MsWksDRParser::createZones: can not read RBDR group\n")); |
255 | 1.60k | ascFile.addPos(pos); |
256 | 1.60k | ascFile.addNote("Entries(RBDR):###"); |
257 | 1.60k | return false; |
258 | 1.60k | } |
259 | | |
260 | | // normally, the file is now parsed, let check for potential remaining zones |
261 | 9.67k | if (!input->isEnd()) { |
262 | 8.15k | MWAW_DEBUG_MSG(("MsWksDRParser::createZones: find some extra data\n")); |
263 | 57.5k | while (!input->isEnd()) { |
264 | 57.4k | pos = input->tell(); |
265 | 57.4k | MsWksDocument::Zone unknown; |
266 | 57.4k | if (!m_document->readZone(unknown) || input->tell()<=pos) { |
267 | 8.08k | ascFile.addPos(pos); |
268 | 8.08k | ascFile.addNote("Entries(End)"); |
269 | 8.08k | ascFile.addPos(pos+100); |
270 | 8.08k | ascFile.addNote("_"); |
271 | 8.08k | break; |
272 | 8.08k | } |
273 | 57.4k | } |
274 | 8.15k | } |
275 | | |
276 | 9.67k | std::vector<int> linesH, pagesH; |
277 | 9.67k | m_document->getGraphParser()->computePositions(m_state->m_mainZoneId, linesH, pagesH); |
278 | | |
279 | 9.67k | return true; |
280 | 11.2k | } |
281 | | |
282 | | //////////////////////////////////////////////////////////// |
283 | | // |
284 | | // Low level |
285 | | // |
286 | | //////////////////////////////////////////////////////////// |
287 | | |
288 | | //////////////////////////////////////////////////////////// |
289 | | // read the header |
290 | | //////////////////////////////////////////////////////////// |
291 | | bool MsWksDRParser::readDrawHeader() |
292 | 27.0k | { |
293 | 27.0k | MWAWInputStreamPtr input=m_document->getInput(); |
294 | | |
295 | 27.0k | int const vers=version(); |
296 | 27.0k | long pos = input->tell(); |
297 | 27.0k | auto N = static_cast<int>(input->readULong(2)); |
298 | 27.0k | int headerSize = vers == 3 ? 4 : 88; |
299 | 27.0k | int dataSize = vers == 3 ? 4 : 51; |
300 | 27.0k | libmwaw::DebugStream f; |
301 | 27.0k | f << "FileHeader(A)"; |
302 | 27.0k | if (!input->checkPosition(pos+headerSize+dataSize*N)) { |
303 | 11.5k | f << "###"; |
304 | 11.5k | MWAW_DEBUG_MSG(("MsWksDRParser::readDrawHeader: Unknown header find N=%d\n", N)); |
305 | 11.5k | m_document->ascii().addPos(pos); |
306 | 11.5k | m_document->ascii().addNote(f.str().c_str()); |
307 | 11.5k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
308 | 11.5k | return false; |
309 | 11.5k | } |
310 | | |
311 | 15.4k | f << "N = " << N << ", v0 = " << input->readLong(2); |
312 | | // version 4 |
313 | | // begin always by |
314 | | // v0=1, v1=[2|6|9], v2=180, v3=1, v6=0x300, v7=0x400, v8=1, v11=0x300, v12=0x400, v14=255 |
315 | | // followed by |
316 | | // v16=4003, v20=8, v31=100 |
317 | | // or v15=6, v16=-1, v17=-1, v19=0x300, v20=8, v21=1, v24=44, v25=6, v31=100 |
318 | 15.4k | if (vers == 4) { |
319 | 180k | for (int i = 1; i < 35; i++) { |
320 | 175k | auto val = static_cast<int>(input->readLong(2)); |
321 | 175k | if (val) f << ", v" << i << "=" << val; |
322 | 175k | } |
323 | | // [0,0,1,1,0,0,0,0,1,1,0,1,1,1,1,0,] |
324 | | // or [0,0,3,1,0,0,0,0,1,1,0,1,1,1,1,0,] |
325 | 5.17k | f << ",fl=["; |
326 | 87.9k | for (int i = 0; i < 16; i++) { |
327 | 82.7k | f << input->readLong(1) << ","; |
328 | 82.7k | } |
329 | 5.17k | f << "]"; |
330 | 5.17k | } |
331 | 15.4k | m_document->ascii().addPos(pos); |
332 | 15.4k | m_document->ascii().addNote(f.str().c_str()); |
333 | | |
334 | 15.4k | input->seek(pos+headerSize,librevenge::RVNG_SEEK_SET); |
335 | 17.3k | for (int i = 0; i < N; i++) { |
336 | 5.49k | pos = input->tell(); |
337 | 5.49k | f.str(""); |
338 | 5.49k | f << "FileHeader(A)[" << i << "]:"; |
339 | 5.49k | auto v = static_cast<int>(input->readULong(2)); // normally 0xe or 0x800e |
340 | 5.49k | auto id = static_cast<int>(input->readLong(2)); |
341 | 5.49k | f << std::hex << v << std::dec; |
342 | | |
343 | 5.49k | if (id != i+1) { |
344 | 3.61k | MWAW_DEBUG_MSG(("MsWksDRParser::readDrawHeader: bad data %i\n", id)); |
345 | 3.61k | f << "###"; |
346 | 3.61k | m_document->ascii().addPos(pos); |
347 | 3.61k | m_document->ascii().addNote(f.str().c_str()); |
348 | 3.61k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
349 | 3.61k | return false; |
350 | 3.61k | } |
351 | | |
352 | 1.87k | if (vers==4) { |
353 | | // always v0=255, v2=4003, v6=8, v16=-1, w2=3, w4=4 |
354 | 36.3k | for (int j = 0; j < 20; j++) { |
355 | 34.6k | auto val = static_cast<int>(input->readLong(2)); |
356 | 34.6k | if (val) f << ", v" << j << "=" << val; |
357 | 34.6k | } |
358 | 13.8k | for (int j = 0; j < 7; j++) { |
359 | 12.1k | auto val = static_cast<int>(input->readLong(1)); |
360 | 12.1k | if (val) f << ", w" << j << "=" << val; |
361 | 12.1k | } |
362 | 1.73k | } |
363 | | |
364 | 1.87k | m_document->ascii().addPos(pos); |
365 | 1.87k | m_document->ascii().addNote(f.str().c_str()); |
366 | 1.87k | input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET); |
367 | 1.87k | } |
368 | | |
369 | 11.8k | return true; |
370 | 15.4k | } |
371 | | |
372 | | //////////////////////////////////////////////////////////// |
373 | | // read the header |
374 | | //////////////////////////////////////////////////////////// |
375 | | bool MsWksDRParser::checkHeader(MWAWHeader *header, bool strict) |
376 | 100k | { |
377 | 100k | *m_state = MsWksDRParserInternal::State(); |
378 | 100k | if (!m_document || !m_document->checkHeader3(header, strict)) return false; |
379 | 83.6k | if (m_document->getKind() != MWAWDocument::MWAW_K_DRAW) |
380 | 1 | return false; |
381 | 83.6k | if (version() < 2 || version() > 4) |
382 | 130 | return false; |
383 | 83.5k | return true; |
384 | 83.6k | } |
385 | | |
386 | | // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: |