/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* libwps
* Version: MPL 2.0 / LGPLv2.1+
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Major Contributor(s):
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU Lesser General Public License Version 2.1 or later
* (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
* applicable instead of those above.
*/
#include <stdlib.h>
#include <string.h>
#include <cmath>
#include <sstream>
#include <librevenge-stream/librevenge-stream.h>
#include "libwps_internal.h"
#include "libwps_tools_win.h"
#include "WKSContentListener.h"
#include "WKSSubDocument.h"
#include "WPSCell.h"
#include "WPSEntry.h"
#include "WPSFont.h"
#include "WPSHeader.h"
#include "WPSPageSpan.h"
#include "LotusGraph.h"
#include "LotusSpreadsheet.h"
#include "LotusStyleManager.h"
#include "Lotus.h"
using namespace libwps;
//! Internal: namespace to define internal class of LotusParser
namespace LotusParserInternal
{
//! the font of a LotusParser
struct Font : public WPSFont
{
//! constructor
Font(libwps_tools_win::Font::Type type) : WPSFont(), m_type(type)
{
}
//! font encoding type
libwps_tools_win::Font::Type m_type;
};
//! Internal: the subdocument of a LotusParser
class SubDocument : public WKSSubDocument
{
public:
//! constructor for a text entry
SubDocument(RVNGInputStreamPtr input, LotusParser &pars, bool header) :
WKSSubDocument(input, &pars), m_header(header) {}
//! destructor
~SubDocument() {}
//! operator==
virtual bool operator==(shared_ptr<WKSSubDocument> const &doc) const
{
if (!doc || !WKSSubDocument::operator==(doc))
return false;
SubDocument const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
if (!sDoc) return false;
return m_header == sDoc->m_header;
}
//! the parser function
void parse(shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType subDocumentType);
//! a flag to known if we need to send the header or the footer
bool m_header;
};
void SubDocument::parse(shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType)
{
if (!listener.get())
{
WPS_DEBUG_MSG(("LotusParserInternal::SubDocument::parse: no listener\n"));
return;
}
if (!dynamic_cast<WKSContentListener *>(listener.get()))
{
WPS_DEBUG_MSG(("LotusParserInternal::SubDocument::parse: bad listener\n"));
return;
}
LotusParser *pser = m_parser ? dynamic_cast<LotusParser *>(m_parser) : 0;
if (!pser)
{
listener->insertCharacter(' ');
WPS_DEBUG_MSG(("LotusParserInternal::SubDocument::parse: bad parser\n"));
return;
}
#if 0
pser->sendHeaderFooter(m_header);
#endif
}
//! the state of LotusParser
struct State
{
//! constructor
State(libwps_tools_win::Font::Type fontType) : m_eof(-1), m_fontType(fontType), m_version(-1),
m_inMainContentBlock(false), m_fontsMap(), m_pageSpan(), m_maxSheet(0), m_actPage(0), m_numPages(0),
m_headerString(""), m_footerString("")
{
}
//! return the default font style
libwps_tools_win::Font::Type getDefaultFontType() const
{
if (m_fontType != libwps_tools_win::Font::UNKNOWN)
return m_fontType;
return libwps_tools_win::Font::WIN3_WEUROPE;
}
//! returns a default font (Courier12) with file's version to define the default encoding */
WPSFont getDefaultFont() const
{
WPSFont res;
if (m_version <= 2)
res.m_name="Courier";
else
res.m_name="Times New Roman";
res.m_size=12;
return res;
}
//! the last file position
long m_eof;
//! the user font type
libwps_tools_win::Font::Type m_fontType;
//! the file version
int m_version;
//! a flag used to know if we are in the main block or no
bool m_inMainContentBlock;
//! the fonts map
std::map<int, Font> m_fontsMap;
//! the actual document size
WPSPageSpan m_pageSpan;
//! the last sheet number
int m_maxSheet;
int m_actPage /** the actual page*/, m_numPages /* the number of pages */;
//! the header string
std::string m_headerString;
//! the footer string
std::string m_footerString;
};
}
// constructor, destructor
LotusParser::LotusParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
libwps_tools_win::Font::Type encoding) :
WKSParser(input, header), m_listener(), m_state(), m_styleManager(), m_graphParser(), m_spreadsheetParser()
{
m_state.reset(new LotusParserInternal::State(encoding));
m_styleManager.reset(new LotusStyleManager(*this));
m_graphParser.reset(new LotusGraph(*this));
m_spreadsheetParser.reset(new LotusSpreadsheet(*this));
}
LotusParser::~LotusParser()
{
}
int LotusParser::version() const
{
return m_state->m_version;
}
bool LotusParser::checkFilePosition(long pos)
{
if (m_state->m_eof < 0)
{
RVNGInputStreamPtr input = getInput();
long actPos = input->tell();
input->seek(0, librevenge::RVNG_SEEK_END);
m_state->m_eof=input->tell();
input->seek(actPos, librevenge::RVNG_SEEK_SET);
}
return pos <= m_state->m_eof;
}
//////////////////////////////////////////////////////////////////////
// interface
//////////////////////////////////////////////////////////////////////
libwps_tools_win::Font::Type LotusParser::getDefaultFontType() const
{
return m_state->getDefaultFontType();
}
bool LotusParser::getFont(int id, WPSFont &font, libwps_tools_win::Font::Type &type) const
{
if (m_state->m_fontsMap.find(id)==m_state->m_fontsMap.end())
{
WPS_DEBUG_MSG(("LotusParser::getFont: can not find font %d\n", id));
return false;
}
LotusParserInternal::Font const &ft=m_state->m_fontsMap.find(id)->second;
font=ft;
type=ft.m_type;
return true;
}
bool LotusParser::hasGraphics(int sheetId) const
{
return m_graphParser->hasGraphics(sheetId);
}
void LotusParser::sendGraphics(int sheetId)
{
m_graphParser->sendGraphics(sheetId);
}
//////////////////////////////////////////////////////////////////////
// parsing
//////////////////////////////////////////////////////////////////////
// main function to parse the document
void LotusParser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
{
RVNGInputStreamPtr input=getInput();
if (!input)
{
WPS_DEBUG_MSG(("LotusParser::parse: does not find main ole\n"));
throw (libwps::ParseException());
}
if (!checkHeader(0L, true)) throw(libwps::ParseException());
bool ok=false;
try
{
ascii().setStream(input);
ascii().open("MN0");
if (checkHeader(0L) && readZones())
m_listener=createListener(documentInterface);
if (m_listener)
{
m_styleManager->updateState();
m_spreadsheetParser->updateState();
m_graphParser->setListener(m_listener);
m_spreadsheetParser->setListener(m_listener);
m_listener->startDocument();
for (int i=0; i<=m_state->m_maxSheet; ++i)
m_spreadsheetParser->sendSpreadsheet(i);
m_listener->endDocument();
m_listener.reset();
ok = true;
}
}
catch (...)
{
WPS_DEBUG_MSG(("LotusParser::parse: exception catched when parsing MN0\n"));
throw (libwps::ParseException());
}
ascii().reset();
if (!ok)
throw(libwps::ParseException());
}
shared_ptr<WKSContentListener> LotusParser::createListener(librevenge::RVNGSpreadsheetInterface *interface)
{
std::vector<WPSPageSpan> pageList;
WPSPageSpan ps(m_state->m_pageSpan);
if (!m_state->m_headerString.empty())
{
WPSSubDocumentPtr subdoc(new LotusParserInternal::SubDocument
(getInput(), *this, true));
ps.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::ALL, subdoc);
}
if (!m_state->m_footerString.empty())
{
WPSSubDocumentPtr subdoc(new LotusParserInternal::SubDocument
(getInput(), *this, false));
ps.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::ALL, subdoc);
}
int numPages=m_state->m_maxSheet+1;
if (numPages<=0) numPages=1;
for (int i=0; i<numPages; ++i) pageList.push_back(ps);
return shared_ptr<WKSContentListener>(new WKSContentListener(pageList, interface));
}
////////////////////////////////////////////////////////////
// low level
////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool LotusParser::checkHeader(WPSHeader *header, bool strict)
{
*m_state = LotusParserInternal::State(m_state->m_fontType);
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
if (!checkFilePosition(12))
{
WPS_DEBUG_MSG(("LotusParser::checkHeader: file is too short\n"));
return false;
}
input->seek(0,librevenge::RVNG_SEEK_SET);
int firstOffset = (int) libwps::readU8(input);
int type = (int) libwps::read8(input);
int val=(int) libwps::read16(input);
f << "FileHeader:";
if (firstOffset == 0 && type == 0 && val==0x1a)
{
m_state->m_version=1;
f << "DOS,";
}
else
{
WPS_DEBUG_MSG(("LotusParser::checkHeader: find unexpected first data\n"));
return false;
}
val=(int) libwps::readU16(input);
if (val>=0x1000 && val<=0x1002)
{
WPS_DEBUG_MSG(("LotusParser::checkHeader: find lotus123 file\n"));
m_state->m_version=(val-0x1000)+1;
f << "lotus123[" << m_state->m_version << "],";
}
#ifdef DEBUG
else if (val>0x1002 && val<=0x1005)
{
WPS_DEBUG_MSG(("LotusParser::checkHeader: find lotus123 file\n"));
m_state->m_version=(val-0x1000)+1;
f << "lotus123[" << m_state->m_version << "],";
}
else if (val==0x8007)
{
WPS_DEBUG_MSG(("LotusParser::checkHeader: find lotus file format, sorry parsing this file is only implemented for debugging, not output will be created\n"));
f << "lotus123[FMT],";
}
#endif
else
{
WPS_DEBUG_MSG(("LotusParser::checkHeader: unknown lotus 123 header\n"));
return false;
}
input->seek(0, librevenge::RVNG_SEEK_SET);
if (strict)
{
for (int i=0; i < 4; ++i)
{
if (!readZone()) return false;
}
}
ascii().addPos(0);
ascii().addNote(f.str().c_str());
if (header)
{
header->setMajorVersion(uint8_t(100+m_state->m_version));
header->setCreator(libwps::WPS_LOTUS);
header->setKind(libwps::WPS_SPREADSHEET);
header->setNeedEncoding(true);
}
return true;
}
bool LotusParser::readZones()
{
RVNGInputStreamPtr input = getInput();
// reset data
m_styleManager->cleanState();
m_graphParser->cleanState();
m_spreadsheetParser->cleanState();
input->seek(0, librevenge::RVNG_SEEK_SET);
bool mainDataRead=false;
// data, format and ?
for (int wh=0; wh<2; ++wh)
{
if (input->isEnd())
break;
while (readZone()) ;
//
// look for ending
//
long pos = input->tell();
if (!checkFilePosition(pos+4))
break;
int type = (int) libwps::readU16(input); // 1
int length = (int) libwps::readU16(input);
if (type==1 && length==0)
{
ascii().addPos(pos);
ascii().addNote("Entries(EOF)");
if (!mainDataRead)
mainDataRead=m_state->m_inMainContentBlock;
// end of block, look for other blocks
continue;
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
while (!input->isEnd())
{
long pos=input->tell();
int id = (int) libwps::readU8(input);
int type = (int) libwps::readU8(input);
long sz = (long) libwps::readU16(input);
if ((type>0x2a) || sz<0 || !checkFilePosition(pos+4+sz))
{
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
libwps::DebugStream f;
f << "Entries(UnknZon" << std::hex << id << "):";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
}
if (!input->isEnd())
{
ascii().addPos(input->tell());
ascii().addNote("Entries(Unknown)");
}
return mainDataRead || m_spreadsheetParser->hasSomeSpreadsheetData();
}
bool LotusParser::readZone()
{
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
long pos = input->tell();
int id = (int) libwps::readU8(input);
int type = (int) libwps::readU8(input);
long sz = (long) libwps::readU16(input);
long endPos=pos+4+sz;
if ((type>0x2a) || sz<0 || !checkFilePosition(endPos))
{
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
f << "Entries(Lotus";
if (type) f << std::hex << type << std::dec << "A";
f << std::hex << id << std::dec << "E):";
bool ok = true, isParsed = false, needWriteInAscii = false;
int val;
input->seek(pos, librevenge::RVNG_SEEK_SET);
switch (type)
{
case 0:
switch (id)
{
case 0:
{
if (sz!=26)
{
ok=false;
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
f.str("");
f << "Entries(BOF):";
val=(int) libwps::readU16(input);
m_state->m_inMainContentBlock=false;
if (val==0x8007)
f << "FMT,";
else if (val>=0x1000 && val <= 0x1005)
{
m_state->m_inMainContentBlock=true;
f << "version=" << (val-0x1000) << ",";
}
else
f << "#version=" << std::hex << val << std::dec << ",";
for (int i=0; i<4; ++i) // f0=4, f3 a small number
{
val=(int) libwps::read16(input);
if (val)
f << "f" << i << "=" << val << ",";
}
val=(int) libwps::readU8(input);
if (m_state->m_inMainContentBlock)
{
m_spreadsheetParser->setLastSpreadsheetId(val);
m_state->m_maxSheet=val;
}
if (val && m_state->m_inMainContentBlock)
f << "max[sheet]=" << val << ",";
else if (val)
f << "max[fmt]=" << val << ",";
for (int i=0; i<7; ++i) // g0/g1=0..fd, g2=0|4, g3=0|5|7|1e|20|30, g4=0|8c|3d, g5=1|10, g6=2|a
{
val=(int) libwps::readU8(input);
if (val)
f << "g" << i << "=" << std::hex << val << std::dec << ",";
}
isParsed=needWriteInAscii=true;
break;
}
case 0x1: // EOF
ok = false;
break;
case 0x3:
if (sz!=6)
{
ok=false;
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
for (int i=0; i<3; ++i) // f0=1, f2=1|32
{
val=(int) libwps::read16(input);
if (val)
f << "f" << i << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0x4:
if (sz!=28)
{
ok=false;
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
for (int i=0; i<2; ++i) // f0=1|3, f1=1
{
val=(int) libwps::read8(input);
if (val!=1)
f << "f" << i << "=" << val << ",";
}
for (int i=0; i<2; ++i) // f2=1-3, f1=0|1
{
val=(int) libwps::read16(input);
if (val)
f << "f" << i+1 << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0x5:
{
f.str("");
f << "Entries(SheetUnknA):";
if (sz!=16)
{
ok=false;
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
val=(int) libwps::readU8(input);
if (val)
f << "sheet[id]=" << val << ",";
val=(int) libwps::read8(input); // always 0?
if (val)
f << "f0=" << val << ",";
isParsed=needWriteInAscii=true;
break;
}
case 0x6: // one by sheet
f.str("");
f << "Entries(SheetUnknB):";
if (sz!=5)
{
ok=false;
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
val=(int) libwps::readU8(input);
if (val)
f << "sheet[id]=" << val << ",";
for (int i=0; i<4; ++i) // f0=0, f2=0|1, f3=7-9
{
val=(int) libwps::read8(input); // always 0?
if (val)
f << "f" << i << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0x7:
ok=isParsed=m_spreadsheetParser->readColumnSizes();
break;
case 0x9:
ok=isParsed=m_spreadsheetParser->readCellName();
break;
case 0xa:
ok=isParsed=readLinkZone();
break;
case 0xb: // 0,1,-1
case 0x1e: // always with 0
case 0x21:
if (sz!=1)
{
ok=false;
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
val=(int) libwps::read8(input);
if (val==1)
f << "true,";
else if (val)
f << "val=" << val << ",";
break;
case 0xc: // find 0 or 4 int with value 0|1|ff
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
for (int i=0; i<sz; ++i)
{
val=(int) libwps::read8(input);
if (val==1) f << "f" << i << ",";
else if (val) f << "f" << i << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0xe:
if (sz<30)
{
ok=false;
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
for (int i=0; i<30; ++i) // f7=0|f, f8=0|60, f9=0|54, f17=80, f18=0|ff, f19=3f|40, f26=0|f8, f27=80|ff, f28=b|c,f29=40
{
val=(int) libwps::read8(input);
if (val) f << "f" << i << "=" << val << ",";
}
if (sz>=32)
{
val=(int) libwps::read16(input); // always 1?
if (val!=1) f << "f30=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0xf:
if (sz<0x56)
{
ok=false;
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
val=(int) libwps::read8(input); // 1|2
if (val!=1) f << "f0=" << val << ",";
for (int i=0; i<3; ++i)
{
long actPos=input->tell();
std::string name("");
for (int j=0; j<16; ++j)
{
char c=(char) libwps::readU8(input);
if (!c) break;
name += c;
}
if (!name.empty())
f << "str" << i << "=" << name << ",";
input->seek(actPos+16, librevenge::RVNG_SEEK_SET);
}
for (int i=0; i<17; ++i) // f2=f11=1,f15=0|1, f16=0|2, f17=0|1|2
{
val=(int) libwps::read8(input);
if (val) f << "f" << i+1 << "=" << val << ",";
}
for (int i=0; i<10; ++i) // g0=0|1,g1=Ø|1, g2=4|a, g3=4c|50|80, g4=g5=0|2, g6=42, g7=41|4c, g8=3c|42|59
{
val=(int) libwps::read16(input);
if (val) f << "g" << i << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0x10: // CHECKME
{
if (sz<3)
{
ok=false;
break;
}
f.str("");
f << "Entries(Macro):";
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
for (int i=0; i<2; ++i)
{
val=(int) libwps::readU8(input);
if (val) f << "f" << i << "=" << val << ",";
}
std::string data("");
for (int i=2; i<sz; ++i)
{
char c=(char) libwps::readU8(input);
if (!c) break;
data += c;
}
if (!data.empty())
f << "data=" << data << ",";
if (input->tell()!=endPos && input->tell()+1!=endPos)
{
WPS_DEBUG_MSG(("LotusParser::readZone: the string zone %d seems too short\n", id));
f << "###";
}
isParsed=needWriteInAscii=true;
break;
}
case 0x11:
ok=isParsed=readChartDefinition();
break;
case 0x12:
ok=isParsed=readChartName();
break;
case 0x13:
isParsed=m_spreadsheetParser->readRowFormats();
break;
case 0x15:
case 0x1d:
if (sz!=4)
{
WPS_DEBUG_MSG(("LotusParser::readZone: size of zone%d seems bad\n", id));
f << "###";
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
val=(int) libwps::read16(input); // small number 6-c maybe a style
if (val) f << "f0=" << val << ",";
for (int i=0; i<2; ++i) // zone15: f1=3, f2=2-5, zone 1d: always 0
{
val=(int) libwps::readU8(input);
if (val) f << "f" << i+1 << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0x16: // the cell text
case 0x17: // double10 cell
case 0x18: // uint16 double cell
case 0x19: // double10+formula
case 0x1a: // text formula result cell
case 0x25: // uint32 double cell
case 0x26: // comment cell
case 0x27: // double8 cell
case 0x28: // double8+formula
ok=isParsed=m_spreadsheetParser->readCell();
break;
case 0x1b:
isParsed=readDataZone();
break;
case 0x1c: // always 00002d000000
if (sz!=6)
{
WPS_DEBUG_MSG(("LotusParser::readZone: size of zone%d seems bad\n", id));
f << "###";
break;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
for (int i=0; i<6; ++i) // some int
{
val=(int) libwps::readU8(input);
if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0x1f:
isParsed=ok=m_spreadsheetParser->readColumnDefinition();
break;
case 0x23:
isParsed=ok=m_spreadsheetParser->readSheetName();
break;
// case 13: big structure
//
// format:
//
case 0xae:
isParsed=readFMTFontName();
break;
default:
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
break;
}
break;
default:
// checkme: maybe <5 is ok
if (version()<=2)
{
ok=false;
break;
}
break;
}
if (!ok)
{
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
if (sz && input->tell()!=pos && input->tell()!=endPos)
ascii().addDelimiter(input->tell(),'|');
input->seek(endPos, librevenge::RVNG_SEEK_SET);
if (!isParsed || needWriteInAscii)
{
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
return true;
}
bool LotusParser::readDataZone()
{
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
long pos = input->tell();
int type = (int) libwps::readU16(input);
long sz = (long) libwps::readU16(input);
long endPos=pos+4+sz;
if (type!=0x1b || sz<2)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the zone seems odd\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
type = (int) libwps::readU16(input);
f << "Entries(Data" << std::hex << type << std::dec << "E):";
bool isParsed=false, needWriteInAscii=false;
sz-=2;
int val;
switch (type)
{
//
// mac windows
//
case 0x7d2:
{
f.str("");
f << "Entries(WindowsMacDef):";
if (sz<26)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the windows definition seems bad\n"));
f << "###";
break;
}
val=(int) libwps::readU8(input);
if (val) f << "id=" << val << ",";
val=(int) libwps::read8(input); // find 0|2
if (val) f << "f0=" << val << ",";
int dim[4];
for (int i=0; i<4; ++i)
{
dim[i]=(int) libwps::read16(input);
val=(int) libwps::read16(input);
if (!val) continue;
if (i)
f << "num[split]=" << val << ",";
else
f << "dim" << i << "[h]=" << val << ",";
}
f << "dim=" << Box2i(Vec2i(dim[0],dim[1]),Vec2i(dim[2],dim[3])) << ",";
for (int i=0; i<8; ++i) // small value or 100
{
val=(int) libwps::read8(input);
if (val) f << "f" << i+1 << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
int remain=int(sz-26);
if (remain<=1) break;
std::string name("");
for (int i=0; i<remain; ++i)
name+=(char) libwps::readU8(input);
f << name << ",";
break;
}
case 0x7d3:
{
f.str("");
f << "Entries(WindowsMacSplit):";
if (sz<24)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the windows split seems bad\n"));
f << "###";
break;
}
val=(int) libwps::readU8(input);
if (val) f << "id=" << val << ",";
val=(int) libwps::readU8(input);
if (val) f << "split[id]=" << val << ",";
for (int i=0; i<3; ++i) // 0 or 1
{
val=(int) libwps::read8(input);
if (val) f << "f" << i+1 << "=" << val << ",";
}
int dim[4];
for (int i=0; i<4; ++i)
{
val=(int) libwps::read16(input);
dim[i]=(int) libwps::read16(input);
if (val) f << "dim" << i <<"[h]=" << val << ",";
}
f << "dim=" << Box2i(Vec2i(dim[0],dim[1]),Vec2i(dim[2],dim[3])) << ",";
for (int i=0; i<3; ++i)
{
static int const expected[]= {0,-1,25};
val=(int) libwps::read8(input);
if (val!=expected[i]) f << "g" << i << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
}
case 0x7d4:
{
f.str("");
f << "Entries(WindowsMacUnkn0)";
if (sz<5)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the windows unkn0 seems bad\n"));
f << "###";
break;
}
for (int i=0; i<4; ++i) // always 2,1,1,2 ?
{
val=(int) libwps::read8(input);
if (val) f << "f" << i << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
int remain=int(sz-4);
if (remain<=1) break;
std::string name("");
for (int i=0; i<remain; ++i) // always LMBCS 1.2?
name+=(char) libwps::readU8(input);
f << name << ",";
break;
}
case 0x7d5: // frequently followed by Lotus13 block and SheetRow, ...
f.str("");
f << "Entries(SheetBegin):";
if (sz!=11)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the sheet begin zone seems bad\n"));
f << "###";
break;
}
// time to update the style manager state
m_styleManager->updateState();
val=(int) libwps::readU8(input);
if (val) f << "sheet[id]=" << val << ",";
// then always 0a3fff00ffff508451ff ?
isParsed=needWriteInAscii=true;
break;
case 0x7d7:
isParsed=m_spreadsheetParser->readRowSizes(endPos);
break;
case 0x7d8:
case 0x7d9:
{
f.str("");
int dataSz=type==0x7d8 ? 1 : 2;
if (type==0x7d8)
f << "Entries(ColMacBreak):";
else
f << "Entries(RowMacBreak):";
if (sz<4 || (sz%dataSz))
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the page mac break seems bad\n"));
f << "###";
break;
}
val=(int) libwps::readU8(input);
if (val) f << "sheet[id]=" << val << ",";
val=(int) libwps::readU8(input); // always 0
if (val) f << "f0=" << val << ",";
f << "break=[";
int N=int((sz-2)/dataSz);
for (int i=0; i<N; ++i)
{
if (dataSz==1)
f << (int) libwps::readU8(input) << ",";
else
f << libwps::readU16(input) << ",";
}
f << "],";
isParsed=needWriteInAscii=true;
break;
}
//
// selection
//
case 0xbb8:
f.str("");
f << "Entries(MacSelect):";
if (sz!=18)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the mac selection seems bad\n"));
f << "###";
break;
}
for (int i=0; i<3; ++i) // f0=0, f1=f2=1
{
val=(int) libwps::read16(input);
if (val) f << "f" << i << "=" << val << ",";
}
for (int i=0; i<3; ++i)
{
int row=(int) libwps::readU16(input);
int sheet=(int) libwps::readU8(input);
int col=(int) libwps::readU8(input);
f << "C" << col << "-" << row;
if (sheet) f << "[" << sheet << "],";
else f << ",";
}
isParsed=needWriteInAscii=true;
break;
//
// style
//
case 0xfa0:
isParsed=m_styleManager->readFontStyle(endPos);
break;
case 0xfaa:
isParsed=m_styleManager->readLineStyle(endPos);
break;
case 0xfb4:
isParsed=m_styleManager->readColorStyle(endPos);
break;
case 0xfbe:
isParsed=m_styleManager->readFormatStyle(endPos);
break;
case 0xfc8:
isParsed=m_styleManager->readGraphicStyle(endPos);
break;
case 0xfd2:
isParsed=m_styleManager->readCellStyle(endPos);
break;
case 0xfdc:
isParsed=readMacFontName(endPos);
break;
// 0xfd2: id, ..., colorid
//
// graphic
//
case 0x2328:
isParsed=m_graphParser->readZoneBegin(endPos);
break;
case 0x2332: // line
case 0x2346: // rect, rectoval, rect
case 0x2350: // arac
case 0x2352: // rect shadow
case 0x23f0: // frame
isParsed=m_graphParser->readZoneData(endPos, type);
break;
case 0x23fa: // textbox data
isParsed=m_graphParser->readTextBoxData(endPos);
break;
//
// mac pict
//
case 0x240e:
isParsed=m_graphParser->readPictureDefinition(endPos);
break;
case 0x2410:
isParsed=m_graphParser->readPictureData(endPos);
break;
//
// mac printer
//
case 0x2af8:
isParsed=readDocumentInfoMac(endPos);
break;
case 0x2afa:
f.str("");
f << "Entries(PrinterMacUnkn1):";
if (sz!=3)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the printer unkn1 seems bad\n"));
f << "###";
break;
}
for (int i=0; i<3; ++i)
{
val=(int) libwps::readU8(input);
static int const expected[]= {0x1f, 0xe0, 0/*or 1*/};
if (val!=expected[i])
f << "f" << i << "=" << val << ",";
}
isParsed=needWriteInAscii=true;
break;
case 0x2afb:
{
f.str("");
f << "Entries(PrinterMacName):";
if (sz<3)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the printername seems bad\n"));
f << "###";
break;
}
val=(int) libwps::read16(input);
if (val!=20) f << "f0=" << val << ",";
std::string name("");
for (int i=4; i<sz; ++i)
{
char c=(char) libwps::readU8(input);
if (!c) break;
name+=c;
}
f << name << ",";
isParsed=needWriteInAscii=true;
break;
}
case 0x2afc:
f.str("");
f << "Entries(PrintMacInfo):";
if (sz<120)
{
WPS_DEBUG_MSG(("LotusParser::readDataZone: the printinfo seems bad\n"));
f << "###";
break;
}
isParsed=needWriteInAscii=true;
break;
//
// 4268, 4269
//
default:
break;
}
if (!isParsed || needWriteInAscii)
{
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
if (input->tell()!=endPos)
ascii().addDelimiter(input->tell(),'|');
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// generic
////////////////////////////////////////////////////////////
bool LotusParser::readMacFontName(long endPos)
{
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
const int vers=version();
long pos = input->tell();
long sz=endPos-pos;
f << "Entries(MacFontName):";
if ((vers<=1 && sz<7) || (vers>1 && sz!=42))
{
WPS_DEBUG_MSG(("LotusParser::readMacFontName: the zone size seems bad\n"));
f << "###";
ascii().addPos(pos-6);
ascii().addNote(f.str().c_str());
return true;
}
if (vers<=1)
{
// seems only to exist in a lotus mac file, so revert the default encoding to MacRoman if undef
if (m_state->m_fontType==libwps_tools_win::Font::UNKNOWN)
m_state->m_fontType=libwps_tools_win::Font::MAC_ROMAN;
int id=(int) libwps::readU16(input);
f << "FN" << id << ",";
int val=(int) libwps::readU16(input); // always 0?
if (val)
f << "f0=" << val << ",";
val=(int) libwps::read16(input); // find -1, 30 (Geneva), 60 (Helvetica)
if (val)
f << "f1=" << val << ",";
std::string name("");
bool nameOk=true;
for (int i=0; i<sz-6; ++i)
{
char c=(char) libwps::readU8(input);
if (!c) break;
if (nameOk && !(c==' ' || (c>='0'&&c<='9') || (c>='a'&&c<='z') || (c>='A'&&c<='Z')))
{
nameOk=false;
WPS_DEBUG_MSG(("LotusParser::readMacFontName: find odd character in name\n"));
f << "#";
}
name += c;
}
f << name << ",";
if (m_state->m_fontsMap.find(id)!=m_state->m_fontsMap.end())
{
WPS_DEBUG_MSG(("LotusParser::readMacFontName: a font with id=%d already exists\n", id));
f << "###id,";
}
else if (nameOk && !name.empty())
{
libwps_tools_win::Font::Type encoding=name!="Symbol" ?
libwps_tools_win::Font::MAC_ROMAN : libwps_tools_win::Font::MAC_SYMBOL;
LotusParserInternal::Font font(encoding);
font.m_name=name;
m_state->m_fontsMap.insert(std::map<int, LotusParserInternal::Font>::value_type(id,font));
}
ascii().addPos(pos-6);
ascii().addNote(f.str().c_str());
return true;
}
for (int i=0; i<4; ++i)
{
int val=(int) libwps::read8(input); // 0|1
if (val)
f << "fl" << i << "=" << val << ",";
}
for (int i=0; i<2; ++i) // f1=0|1288
{
int val=(int) libwps::read16(input);
if (val)
f << "f" << i << "=" << val << ",";
}
std::string name("");
for (int i=0; i<8; ++i)
{
char c=(char) libwps::read8(input);
if (!c) break;
name+=c;
}
f << name << ",";
input->seek(pos+16, librevenge::RVNG_SEEK_SET);
if (input->tell()!=endPos)
{
ascii().addDelimiter(input->tell(),'|');
input->seek(endPos, librevenge::RVNG_SEEK_SET);
}
ascii().addPos(pos-6);
ascii().addNote(f.str().c_str());
return true;
}
bool LotusParser::readFMTFontName()
{
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
long pos = input->tell();
int type = (int) libwps::read16(input);
if (type!=0xae)
{
WPS_DEBUG_MSG(("LotusParser::readFMTFontName: not a font name definition\n"));
return false;
}
long sz = (long) libwps::readU16(input);
long endPos=pos+4+sz;
f << "Entries(FontFMTName):";
if (sz < 2)
{
WPS_DEBUG_MSG(("LotusParser::readFMTFontName: the zone is too short\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
int id=(int) libwps::readU8(input);
f << "id=" << id << ",";
bool nameOk=true;
std::string name("");
for (long i=1; i<sz; ++i)
{
char c=(char) libwps::readU8(input);
if (!c) break;
if (nameOk && !(c==' ' || (c>='0'&&c<='9') || (c>='a'&&c<='z') || (c>='A'&&c<='Z')))
{
nameOk=false;
WPS_DEBUG_MSG(("LotusParser::readFMTFontName: find odd character in name\n"));
f << "#";
}
name += c;
}
f << name << ",";
if (input->tell()!=endPos)
{
WPS_DEBUG_MSG(("LotusParser::readFMTFontName: find extra data\n"));
f << "###extra";
input->seek(endPos, librevenge::RVNG_SEEK_SET);
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
bool LotusParser::readLinkZone()
{
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
long pos = input->tell();
int type = (int) libwps::read16(input);
if (type!=0xa)
{
WPS_DEBUG_MSG(("LotusParser::readLinkZone: not a link definition\n"));
return false;
}
long sz = (long) libwps::readU16(input);
f << "Entries(Link):";
if (sz < 19)
{
WPS_DEBUG_MSG(("LotusParser::readLinkZone: the zone is too short\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
type=(int) libwps::read8(input);
if (type==0)
f << "chart,";
else if (type==1)
f << "file,";
else
{
WPS_DEBUG_MSG(("LotusParser::readLinkZone: find unknown type\n"));
f << "##type=" << type << ",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
// maybe too int
f << "ID=" << std::hex << libwps::readU16(input) << std::dec << ",";
f << "id=" << (int) libwps::readU8(input) << ",";
std::string name("");
for (int i=0; i<14; ++i)
{
char c=(char) libwps::readU8(input);
if (!c) break;
name += c;
}
f << "\"" << name << "\",";
input->seek(pos+4+18, librevenge::RVNG_SEEK_SET);
switch (type)
{
case 0:
if (sz<26)
{
WPS_DEBUG_MSG(("LotusParser::readLinkZone: the chart zone seems too short\n"));
f << "###";
break;
}
for (int i=0; i<2; ++i)
{
int row=(int) libwps::readU16(input);
int table=(int) libwps::readU8(input);
int col=(int) libwps::readU8(input);
f << "C" << col << "-" << row;
if (table) f << "[" << table << "]";
if (i==0)
f << "<->";
else
f << ",";
}
break;
case 1:
name="";
for (int i=18; i<sz; ++i)
{
char c=(char) libwps::readU8(input);
if (!c) break;
name += c;
}
f << "link=" << name << ",";
break;
default:
WPS_DEBUG_MSG(("LotusParser::readLinkZone: find unknown type\n"));
f << "###";
break;
}
if (input->tell()!=pos+4+sz && input->tell()+1!=pos+4+sz)
{
WPS_DEBUG_MSG(("LotusParser::readLinkZone: the zone seems too short\n"));
f << "##";
ascii().addDelimiter(input->tell(), '|');
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
// ----------------------------------------------------------------------
// Header/Footer/PageDim
// ----------------------------------------------------------------------
bool LotusParser::readDocumentInfoMac(long endPos)
{
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
long pos = input->tell();
f << "Entries(DocMacInfo):";
if (endPos-pos!=51)
{
WPS_DEBUG_MSG(("LotusParser::readDocumentInfoMac: the zone size seems bad\n"));
f << "###";
ascii().addPos(pos-6);
ascii().addNote(f.str().c_str());
return true;
}
int dim[7];
for (int i=0; i<7; ++i)
{
int val=(int) libwps::read8(input);
if (i==0)
f << "dim[unkn]=";
else if (i==1)
f << "margins=[";
else if (i==5)
f << "pagesize=[";
dim[i]=(int) libwps::read16(input);
f << dim[i];
if (val) f << "[" << val << "]";
val=(int) libwps::read8(input); // always 0
if (val) f << "[" << val << "]";
f << ",";
if (i==4 || i==6) f << "],";
}
// check order
if (dim[5]>dim[1]+dim[3] && dim[6]>dim[2]+dim[4])
{
m_state->m_pageSpan.setFormWidth(dim[5]);
m_state->m_pageSpan.setFormLength(dim[6]);
m_state->m_pageSpan.setMarginLeft(dim[1]);
m_state->m_pageSpan.setMarginTop(dim[2]);
m_state->m_pageSpan.setMarginRight(dim[3]);
m_state->m_pageSpan.setMarginBottom(dim[4]);
}
else
f << "###";
f << "unkn=[";
for (int i=0; i<5; ++i) // 1,1,1,100|inf,1
{
int val=(int) libwps::read16(input);
if (val==9999)
f << "inf,";
else if (val)
f << val << ",";
else
f << "_,";
}
f << "],";
for (int i=0; i<13; ++i) // always 0?
{
int val=(int) libwps::read8(input);
if (val)
f << "g" << i << "=" << val << ",";
}
ascii().addPos(pos-6);
ascii().addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// chart
////////////////////////////////////////////////////////////
bool LotusParser::readChartDefinition()
{
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
long pos = input->tell();
long type = (long) libwps::read16(input);
if (type != 0x11)
{
WPS_DEBUG_MSG(("LotusParser::readChartDefinition: not a chart name\n"));
return false;
}
long sz = (long) libwps::readU16(input);
long endPos=pos+4+sz;
f << "Entries(ChartDef):";
if (sz != 0xB2)
{
WPS_DEBUG_MSG(("LotusParser::readChartDefinition: chart name is too short\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
f << "id=" << (int) libwps::readU8(input) << ",";
std::string name("");
for (int i=0; i<16; ++i)
{
char c=(char) libwps::readU8(input);
if (!c) break;
name += c;
}
if (!name.empty()) f << name << ",";
input->seek(pos+4+17, librevenge::RVNG_SEEK_SET);
for (int i=0; i<43; ++i) // small number
{
int val=(int) libwps::read8(input);
if (val) f << "f" << i << "=" << val << ",";
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "ChartDef-A:";
for (int i=0; i<28; ++i) // small number expect f24=0|4|64
{
int val=(int) libwps::read8(input);
if (val) f << "f" << i << "=" << val << ",";
}
for (int i=0; i<9; ++i) // small number expect g0=1|2, g1=g2=g3=1|14|20, g4=g5=0|1
{
int val=(int) libwps::read16(input);
if (val) f << "g" << i << "=" << val << ",";
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "ChartDef-B:";
if (input->tell()!=endPos)
input->seek(endPos, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
bool LotusParser::readChartName()
{
libwps::DebugStream f;
RVNGInputStreamPtr input = getInput();
long pos = input->tell();
long type = (long) libwps::read16(input);
if (type != 0x12)
{
WPS_DEBUG_MSG(("LotusParser::readChartName: not a chart name\n"));
return false;
}
long sz = (long) libwps::readU16(input);
f << "Entries(ChartName):";
if (sz < 3)
{
WPS_DEBUG_MSG(("LotusParser::readChartName: chart name is too short\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
int val=(int) libwps::readU8(input);
f << "chart[id]=" << val << ",";
int id=(int) libwps::readU8(input);
f << "data[id]=" << id << ",";
std::string name("");
for (int i = 0; i < sz-2; i++)
{
char c = (char) libwps::readU8(input);
if (c == '\0') break;
name += c;
}
f << name << ",";
if (input->tell()!=pos+4+sz && input->tell()+1!=pos+4+sz)
{
WPS_DEBUG_MSG(("LotusParser::readChartName: the zone seems too short\n"));
f << "##";
ascii().addDelimiter(input->tell(), '|');
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// Unknown
////////////////////////////////////////////////////////////
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */