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