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