/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
/* libmwaw
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* 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 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/
#include <iomanip>
#include <iostream>
#include <limits>
#include <set>
#include <sstream>
#include <librevenge/librevenge.h>
#include "MWAWDebug.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWParser.hxx"
#include "MWAWRSRCParser.hxx"
#include "BeagleWksStructManager.hxx"
/** Internal: the structures of a BeagleWksStructManager */
namespace BeagleWksStructManagerInternal
{
////////////////////////////////////////
//! Internal: the state of a BeagleWksStructManager
struct State {
//! constructor
State()
: m_fileIdFontIdList()
, m_header()
, m_footer()
, m_idFrameMap()
{
}
//! a list to get the correspondance between fileId and fontId
std::vector<int> m_fileIdFontIdList;
//! the header
MWAWEntry m_header;
//! the footer
MWAWEntry m_footer;
/** the map id to frame */
std::map<int, BeagleWksStructManager::Frame> m_idFrameMap;
};
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
BeagleWksStructManager::BeagleWksStructManager(MWAWParserStatePtr const &parserState)
: m_parserState(parserState)
, m_state(new BeagleWksStructManagerInternal::State())
{
}
BeagleWksStructManager::~BeagleWksStructManager()
{
}
int BeagleWksStructManager::getFontId(int fId) const
{
if (fId<0||fId>=int(m_state->m_fileIdFontIdList.size())) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::getFontId can not find the final font id\n"));
return 3;
}
return m_state->m_fileIdFontIdList[size_t(fId)];
}
void BeagleWksStructManager::getHeaderFooterEntries(MWAWEntry &header, MWAWEntry &footer) const
{
header=m_state->m_header;
footer=m_state->m_footer;
}
std::map<int,BeagleWksStructManager::Frame> const &BeagleWksStructManager::getIdFrameMap() const
{
return m_state->m_idFrameMap;
}
bool BeagleWksStructManager::getFrame(int fId, Frame &frame) const
{
auto it=m_state->m_idFrameMap.find(fId);
if (it==m_state->m_idFrameMap.end()) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::getFrame: can not find frame for id=%d\n",fId));
return false;
}
frame=it->second;
return true;
}
MWAWInputStreamPtr BeagleWksStructManager::getInput()
{
return m_parserState->m_input;
}
libmwaw::DebugFile &BeagleWksStructManager::ascii()
{
return m_parserState->m_asciiFile;
}
MWAWInputStreamPtr BeagleWksStructManager::rsrcInput()
{
return m_parserState->m_rsrcParser->getInput();
}
libmwaw::DebugFile &BeagleWksStructManager::rsrcAscii()
{
return m_parserState->m_rsrcParser->ascii();
}
////////////////////////////////////////////////////////////
// the frame
////////////////////////////////////////////////////////////
bool BeagleWksStructManager::readFrame(MWAWEntry const &entry)
{
if (entry.length()!=156*long(entry.id())) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readFrame: the entry seems bad\n"));
return false;
}
entry.setParsed(true);
MWAWInputStreamPtr input = getInput();
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
for (int i=0; i<entry.id(); ++i) {
Frame frame;
long pos=input->tell(), begPos=pos;
libmwaw::DebugStream f;
f << "Entries(Frame)[" << i << "]:";
auto type=static_cast<int>(input->readULong(2));
int val;
switch (type) {
case 0x8000: {
f << "picture,";
val=static_cast<int>(input->readLong(2)); // 1|8
if (val) f << "f0=" << val << ",";
ascii().addDelimiter(input->tell(),'|');
input->seek(pos+40, librevenge::RVNG_SEEK_SET);
ascii().addDelimiter(input->tell(),'|');
for (int j=0; j < 5; ++j) { // f1=5, f3=2, f5=e|13
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << j+1 << "=" << val << ",";
}
double dim[4];
for (auto &j : dim) j=double(input->readLong(4))/65536.;
f << "dim?=" << dim[1] << "x" << dim[0] << "<->" << dim[3] << "x" << dim[2] << ",";
val =static_cast<int>(input->readLong(2));
if (val) f << "f6=" << val << ",";
break;
}
case 0xffff: {
f << "attachment,";
for (int j=0; j<2; ++j) { // f0=0, f1=4ef8
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << j << "=" << val << ",";
}
auto fSz=static_cast<int>(input->readULong(1));
if (fSz>0 && fSz<32) {
std::string name("");
for (int c=0; c < fSz; c++)
name+=char(input->readLong(1));
f << name << ",";
}
else {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readFrame: the size seems bad\n"));
f << "#fSz=" << fSz << ",";
}
input->seek(pos+44, librevenge::RVNG_SEEK_SET);
for (int j=0; j<6; ++j)
f << "dim" << j << "?=" << input->readLong(2) << "x"
<< input->readLong(2) << ",";
break;
}
default:
MWAW_DEBUG_MSG(("BeagleWksStructManager::readFrame: unknown frame type\n"));
f << "type=" << std::hex << type << std::dec << ",";
break;
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
pos=input->tell();
f.str("");
val=static_cast<int>(input->readULong(2)); // afc, 1df, 1dd, f34, a5a8
f << "f0=" << std::hex << val << std::dec << ",";
f << "PTR=" << std::hex << input->readULong(4) << std::dec << ",";
float orig[2];
for (auto &j : orig) j=float(input->readLong(4))/65536.f;
frame.m_origin=MWAWVec2f(orig[1],orig[0]);
f << "PTR1=" << std::hex << input->readULong(4) << std::dec << ",";
frame.m_page=static_cast<int>(input->readLong(2));
float dim[2];
for (auto &j : dim) j=float(input->readLong(2));
frame.m_dim=MWAWVec2f(dim[1],dim[0]);
f << "dim=" << dim[1] << "x" << dim[0] << ",";
for (int j=0; j<4; ++j) { // f1=0|05b1 other 0
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << j+1 << "=" << std::hex << val << std::dec << ",";
}
frame.m_id=static_cast<int>(input->readLong(2));
for (int j=0; j<2; ++j) { // 0
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << j << "=" << std::hex << val << std::dec << ",";
}
frame.m_extra=f.str();
f.str("");
f << "Frame-II[" << i << "]:" << frame;
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "Frame-III[" << i << "]:";
val=int(input->readLong(2)); // 0|6|8|9
if (val) f << "f0=" << val << ",";
val=int(input->readULong(4)); // big number
f << "PTR=" << std::hex << val << std::dec << ",";
frame.m_border.m_width = double(input->readLong(2));
if (frame.m_border.m_width > 0)
f << "borderSize=" << frame.m_border.m_width << ",";
val=int(input->readLong(2)); // 0
if (val) f << "f1=" << val << ",";
val=int(input->readLong(4));
if (val) f << "offset=" << double(val)/65536. << ",";
val=int(input->readLong(2)); // 0
if (val) f << "f2=" << val << ",";
auto flags=static_cast<int>(input->readLong(2));
frame.m_wrap=(flags&3);
switch (frame.m_wrap) { // textaround
case 0: // none
f << "wrap=none,";
break;
case 1:
f << "wrap=rectangle,";
break;
case 2:
f << "wrap=irregular,";
break;
default:
f << "#wrap=3,";
break;
}
if (flags&0x8) {
frame.m_charAnchor = false;
f << "anchor=page,";
}
if (flags&0x10) {
f << "bord[all],";
frame.m_bordersSet=libmwaw::LeftBit|libmwaw::RightBit|
libmwaw::BottomBit|libmwaw::TopBit;
}
else if (flags&0x1E0) {
f << "bord[";
if (flags&0x20) {
f << "T";
frame.m_bordersSet |= libmwaw::TopBit;
}
if (flags&0x40) {
f << "L";
frame.m_bordersSet |= libmwaw::LeftBit;
}
if (flags&0x80) {
f << "B";
frame.m_bordersSet |= libmwaw::BottomBit;
}
if (flags&0x100) {
f << "R";
frame.m_bordersSet |= libmwaw::RightBit;
}
f << "],";
}
flags &= 0xFE04;
if (flags) f << "fl=" << std::hex << flags << std::dec << ",";
frame.m_pictId=static_cast<int>(input->readULong(2));
f << "pId=" << frame.m_pictId << ",";
ascii().addDelimiter(input->tell(),'|');
input->seek(18, librevenge::RVNG_SEEK_CUR);
ascii().addDelimiter(input->tell(),'|');
val=int(input->readLong(4));
if (val) f << "textAround[offsT/B]=" << double(val)/65536. << ",";
val=int(input->readLong(4));
if (val) f << "textAround[offsR/L]=" << double(val)/65536. << ",";
for (int j=0; j<2; ++j) { // g0,g1=0 or g0,g1=5c0077c (dim?)
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << j << "=" << val << ",";
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
if (m_state->m_idFrameMap.find(frame.m_id)!=m_state->m_idFrameMap.end()) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readFrame: frame %d already exists\n", frame.m_id));
}
else
m_state->m_idFrameMap[frame.m_id]=frame;
input->seek(begPos+156, librevenge::RVNG_SEEK_SET);
}
return true;
}
////////////////////////////////////////////////////////////
// the fonts
////////////////////////////////////////////////////////////
bool BeagleWksStructManager::readFontNames(MWAWEntry const &entry)
{
if (!entry.valid())
return (entry.length()==0&&entry.id()==0);
entry.setParsed(true);
MWAWInputStreamPtr input= getInput();
long pos=entry.begin(), endPos=entry.end();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
m_state->m_fileIdFontIdList.resize(0);
for (int i=0; i < entry.id(); ++i) {
pos = input->tell();
f.str("");
f << "Entries(FontNames)[" << i << "]:";
auto fSz=static_cast<int>(input->readULong(1));
if (pos+1+fSz>endPos) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readFontNames: can not read font %d\n", i));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return i>0;
}
std::string name("");
for (int c=0; c < fSz; ++c)
name+=char(input->readULong(1));
if (!name.empty())
m_state->m_fileIdFontIdList.push_back(m_parserState->m_fontConverter->getId(name));
f << "\"" << name << "\",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
pos = input->tell();
if (pos!=endPos) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readFontNames: find extra data\n"));
ascii().addPos(pos);
ascii().addNote("FontNames:###");
input->seek(endPos, librevenge::RVNG_SEEK_SET);
}
ascii().addPos(endPos);
ascii().addNote("_");
return true;
}
////////////////////////////////////////////////////////////
// the document info and preferences
////////////////////////////////////////////////////////////
bool BeagleWksStructManager::readDocumentInfo()
{
MWAWInputStreamPtr input= getInput();
long pos=input->tell();
libmwaw::DebugStream f;
f << "Entries(DocInfo):";
long dSz=static_cast<int>(input->readULong(2));
long endPos=pos+dSz+4;
if (dSz<0x226 || !input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readDocumentInfo: can not find the database zone\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
auto id=static_cast<int>(input->readLong(2));
if (id!=1) f << "id=" << id << ",";
std::string what("");
for (int i=0; i < 4; ++i) // dosu
what+=char(input->readLong(1));
f << what << ",";
int val;
for (int i=0; i < 3; ++i) { // 961/2101, 0, 0
val=static_cast<int>(input->readLong(2));
if ((i==0 && val!=0x961) || (i&&val))
f << "f" << i+2 << "=" << val << ",";
}
f << "ids=[";
for (int i=0; i < 2; ++i)
f << std::hex << long(input->readULong(4)) << std::dec << ",";
f << "],";
double margins[4];
f << "margins=[";
for (auto &margin : margins) {
margin=double(input->readLong(4))/72.;
f << margin << ",";
}
f << "],";
MWAWPageSpan &pageSpan=m_parserState->m_pageSpan;
if (margins[0]>=0&&margins[1]>=0&&margins[2]>=0&&margins[3]>=0&&
margins[0]+margins[1]<0.5*pageSpan.getFormLength() &&
margins[2]+margins[3]<0.5*pageSpan.getFormWidth()) {
pageSpan.setMarginTop(margins[0]);
pageSpan.setMarginBottom(margins[1]);
pageSpan.setMarginLeft(margins[3]);
pageSpan.setMarginRight(margins[2]);
}
else {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readDocumentInfo: the page margins seem bad\n"));
f << "###";
}
auto numRemains=int(endPos-512-input->tell());
f << "fls=[";
for (int i=0; i < numRemains; ++i) { // [_,_,_,_,_,1,]
val = static_cast<int>(input->readLong(1));
if (val) f << val << ",";
else f << "_,";
}
f << "],";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
for (int st=0; st<2; ++st) {
pos=input->tell();
f.str("");
if (st==0)
f << "DocInfo[header]:";
else
f << "DocInfo[footer]:";
auto fSz = static_cast<int>(input->readULong(1));
MWAWEntry &entry=st==0 ? m_state->m_header : m_state->m_footer;
entry.setBegin(input->tell());
entry.setLength(fSz);
std::string name("");
for (int i=0; i<fSz; ++i)
name+=char(input->readULong(1));
f << name;
input->seek(pos+256, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
return true;
}
bool BeagleWksStructManager::readDocumentPreferences()
{
MWAWInputStreamPtr input= getInput();
long pos=input->tell();
libmwaw::DebugStream f;
f << "Entries(Preferences):";
auto dSz=long(input->readULong(2));
long endPos=pos+dSz+4;
if (dSz<0x2e || !input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readDocumentInfo: can not find the database zone\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
auto id=static_cast<int>(input->readLong(2));
if (id!=0xa) f << "id=" << id << ",";
std::string what="";
for (int i=0; i < 4; ++i) // pref
what+=char(input->readLong(1));
f << what << ",";
for (int i=0; i < 3; ++i) { // always 0
auto val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+2 << "=" << val << ",";
}
f << "ids=[";
for (int i=0; i < 2; i++)
f << std::hex << long(input->readULong(4)) << std::dec << ",";
f << "],";
auto val=static_cast<int>(input->readULong(2)); // 0|22d8|4ead|e2c8
if (val)
f << "fl?=" << std::hex << val << std::dec << ",";
for (int i=0; i < 8; i++) {
static int const expectedValues[] = {1,4/*or 2*/,3,2,2,1,1,1 };
val=static_cast<int>(input->readLong(1));
if (val!=expectedValues[i])
f << "g" << i << "=" << val << ",";
}
for (int i=0; i < 8; ++i) { // 1,a|e, 0, 21, 3|4, 6|7|9, d|13, 3|5: related to font?
val=static_cast<int>(input->readLong(2));
if (val)
f << "h" << i << "=" << val << ",";
}
val=static_cast<int>(input->readULong(2)); //0|10|3e|50|c8|88|98
if (val)
f << "h8=" << std::hex << val << std::dec << ",";
input->seek(endPos, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// resource fork data
////////////////////////////////////////////////////////////
// read the windows position blocks
bool BeagleWksStructManager::readwPos(MWAWEntry const &entry)
{
if (!entry.valid() || entry.length() != 8) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readwPos: the entry is bad\n"));
return false;
}
long pos = entry.begin();
MWAWInputStreamPtr input = rsrcInput();
libmwaw::DebugFile &ascFile = rsrcAscii();
libmwaw::DebugStream f;
entry.setParsed(true);
input->seek(pos, librevenge::RVNG_SEEK_SET);
f << "Entries(Windows):";
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
f << "dim=" << dim[1] << "x" << dim[0] << "<->"
<< dim[3] << "x" << dim[2] << ",";
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
return true;
}
bool BeagleWksStructManager::readFontStyle(MWAWEntry const &entry)
{
if (!entry.valid() || entry.length() != 8) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readFontStyle: the entry is bad\n"));
return false;
}
long pos = entry.begin();
MWAWInputStreamPtr input = rsrcInput();
libmwaw::DebugFile &ascFile = rsrcAscii();
libmwaw::DebugStream f;
entry.setParsed(true);
input->seek(pos, librevenge::RVNG_SEEK_SET);
f << "Entries(FontStyle)[" << std::hex << entry.id() << std::dec << "]:";
auto fSz=static_cast<int>(input->readLong(2));
if (fSz) f << "fSz=" << fSz << ",";
auto fl=static_cast<int>(input->readLong(2));
if (fl) f << "flags=" << std::hex << fl << std::dec << ",";
auto id=static_cast<int>(input->readLong(2));
if (id) f << "fId=" << id << ",";
auto val=static_cast<int>(input->readLong(2));
if (val) f << "color?=" << val << ",";
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
return true;
}
bool BeagleWksStructManager::readPicture(int pId, librevenge::RVNGBinaryData &pict, bool readEDTP)
{
auto rsrcParser = m_parserState->m_rsrcParser;
if (!rsrcParser) {
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readPicture: need access to resource fork to retrieve picture content\n"));
first=false;
}
return true;
}
auto &entryMap = rsrcParser->getEntriesMap();
std::string const what(readEDTP ? "edtp" : "PICT");
auto it=entryMap.find(what);
MWAWEntry pictEntry;
while (it!=entryMap.end()) {
if (it->first!=what)
break;
MWAWEntry const &entry=it++->second;
if (entry.id()!=pId)
continue;
entry.setParsed(true);
pictEntry=entry;
break;
}
if (!pictEntry.valid()) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readPicture: can not find picture %d\n", pId));
return false;
}
MWAWInputStreamPtr input = rsrcInput();
input->seek(pictEntry.begin(), librevenge::RVNG_SEEK_SET);
pict.clear();
input->readDataBlock(pictEntry.length(), pict);
#ifdef DEBUG_WITH_FILES
libmwaw::DebugFile &ascFile = rsrcAscii();
libmwaw::DebugStream f;
static int volatile pictName = 0;
f << "PICT" << ++pictName << ".pct";
libmwaw::Debug::dumpFile(pict, f.str().c_str());
ascFile.addPos(pictEntry.begin()-4);
ascFile.addNote(f.str().c_str());
ascFile.skipZone(pictEntry.begin(),pictEntry.end()-1);
#endif
return true;
}
////////////////////////////////////////////////////////////
// formula data
////////////////////////////////////////////////////////////
bool BeagleWksStructManager::readCellInFormula(MWAWVec2i actPos, MWAWCellContent::FormulaInstruction &instr)
{
instr=MWAWCellContent::FormulaInstruction();
instr.m_type=MWAWCellContent::FormulaInstruction::F_Cell;
bool ok = true;
int pos[2];
bool absolute[2] = { true, true};
for (int dim = 0; dim < 2; dim++) {
int val = static_cast<int>(getInput()->readULong(2));
if ((val & 0xF000) == 0); // absolue value ?
else {
val &= 0x7FFF;
if (val & 0x4000) val = val - 0x8000;
val += actPos[dim];
absolute[dim] = false;
}
pos[dim] = val;
}
if (pos[0] < 0 || pos[1] < 0) {
std::stringstream f;
f << "###[" << pos[1] << "," << pos[0] << "]";
if (ok) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readCell: can not read cell position\n"));
}
return false;
}
instr.m_position[0]=MWAWVec2i(pos[0],pos[1]);
instr.m_positionRelative[0]=MWAWVec2b(!absolute[0],!absolute[1]);
return ok;
}
namespace BeagleWksStructManagerInternal
{
struct Functions {
char const *m_name;
int m_arity;
};
static Functions const s_listFunctions[] = {
{ "", 0} /*SPEC: number*/, {"", 0}/*SPEC: cell*/, {"", 0}/*SPEC: cells*/, {"=", 1} /*=*/,
{ "(", 1} /*SPEC: ()*/, {"", 0}/*SPEC: number*/, { "", -2} /*UNKN*/, {"", -2}/*UNKN*/,
{ "", -2} /*UNKN*/, {"+", 1}, {"-", 1}, {"+", 2},
{ "-", 2}, { "*", 2}, {"/", 2}, {"^", 2},
{ "", -2} /*UNKN*/,{ "&", 2}, { "=", 2},{ "<>", 2},
{ "<=", 2},{ ">=", 2}, { "<", 2},{ ">", 2},
{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "Error", -1},{ "False", -1},
{ "NA", -1},{ "Now", -1},{ "Rand", -1},{ "Pi", -1},
{ "True", -1},{ "IsError", -1},{ "Abs", -1},{ "Exp", -1},
{ "Int", -1},{ "LN", -1},{ "LOG10", -1},{ "Sign", -1},
{ "Sqrt", -1},{ "Acos", -1},{ "Asin", -1},{ "Atan", -1},
{ "Cos", -1},{ "Degrees", -1},{ "Radians", -1},{ "Sin", -1},
{ "Tan", -1},{ "Day", -1},{ "Hour", -1},{ "Minute", -1},
{ "Month", -1},{ "Second", -1},{ "Weekday", -1},{ "Year", -1},
{ "IsBlank", -1},{ "IsNa", -1},{ "Not", -1},{ "Type", -1},
{ "Text", -1},{ "Log", -1},{ "Mod", -1},{ "Round", -1},
{ "Atan2", -1},{ "IRR", -1},{ "Lookup", -1},{ "Match", -1},
{ "Date", -1},{ "Time", -1},{ "If", -1},{ "MIRR", -1},
{ "HLookup", -1},{ "Index", -1},{ "VLookup", -1},{ "FV", -1},
{ "NPER", -1},{ "PV", -1},{ "PMT", -1},{ "Rate", -1},
{ "Count", -1},{ "Average", -1},{ "Max", -1},{ "Min", -1},
{ "StDev", -1},{ "Sum", -1},{ "", -2}/*UNKN*/,{ "Var", -1},
{ "And", -1},{ "Choose", -1},{ "Or", -1},{ "NPV", -1},
{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
};
}
bool BeagleWksStructManager::readFormula(long endPos, MWAWVec2i const &position,
std::vector<MWAWCellContent::FormulaInstruction> &formula, std::string &error)
{
MWAWInputStreamPtr input=getInput();
formula.resize(0);
error = "";
std::stringstream f;
std::vector<std::vector<MWAWCellContent::FormulaInstruction> > stack;
bool ok = true;
bool const isSheet=m_parserState->m_kind==MWAWDocument::MWAW_K_SPREADSHEET;
while (long(input->tell()) != endPos) {
double val;
long pos = input->tell();
if (pos > endPos) return false;
auto wh = static_cast<int>(input->readULong(1));
int arity = 0;
bool isNan;
MWAWCellContent::FormulaInstruction instr;
f.str("");
switch (wh) {
case 0x0:
if (endPos-pos<11 || !input->readDouble10(val, isNan)) {
f << "###number";
error=f.str();
ok = false;
break;
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_Double;
instr.m_doubleValue=val;
break;
case 0x1: // code in spreadsheet
case 0x1a: // code in database
if ((wh==1 && !isSheet) || (wh==0x1a && isSheet)) {
f << "###unexpected cell code";
ok=false;
break;
}
if (endPos-pos<5) {
f << "###cell short";
error=f.str();
ok = false;
break;
}
ok = readCellInFormula(position, instr);
break;
case 0x2: {
if (!isSheet) {
f << "###list cell in database";
ok=false;
break;
}
if (endPos-pos< 9 || !readCellInFormula(position, instr)) {
f << "###list cell short";
error=f.str();
ok = false;
break;
}
MWAWCellContent::FormulaInstruction instr2;
if (!readCellInFormula(position, instr2)) {
ok = false;
f << "###list cell short";
error=f.str();
break;
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList;
instr.m_position[1]=instr2.m_position[0];
instr.m_positionRelative[1]=instr2.m_positionRelative[0];
break;
}
case 0x5:
instr.m_type=MWAWCellContent::FormulaInstruction::F_Long;
instr.m_longValue=double(input->readLong(4));
break;
case 0x6: {
instr.m_type=MWAWCellContent::FormulaInstruction::F_Text;
auto fSz=static_cast<int>(input->readULong(1));
if (input->tell()+fSz > endPos) {
ok=false;
break;
}
for (int i=0; i<fSz; ++i) {
auto c = char(input->readULong(1));
if (c==0) {
ok = i+1==fSz;
break;
}
instr.m_content += c;
}
break;
}
default:
if (wh >= 0x70 || (wh < 0x20 && BeagleWksStructManagerInternal::s_listFunctions[wh].m_arity == -2)) {
f.str("");
f << "##Funct" << std::hex << wh << std::dec;
error=f.str();
ok = false;
break;
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_Function;
if (BeagleWksStructManagerInternal::s_listFunctions[wh].m_arity == -2) {
std::stringstream s;
s << "Funct" << std::hex << wh << std::dec;
instr.m_content=s.str();
arity = static_cast<int>(input->readLong(1));
}
else {
instr.m_content=BeagleWksStructManagerInternal::s_listFunctions[wh].m_name;
ok=!instr.m_content.empty();
arity = BeagleWksStructManagerInternal::s_listFunctions[wh].m_arity;
if (arity == -1) arity = static_cast<int>(input->readLong(1));
}
break;
}
if (!ok) break;
std::vector<MWAWCellContent::FormulaInstruction> child;
if (instr.m_type!=MWAWCellContent::FormulaInstruction::F_Function) {
child.push_back(instr);
stack.push_back(child);
continue;
}
size_t numElt = stack.size();
if (static_cast<int>(numElt) < arity) {
f.str("");
f << instr.m_content << "[##" << arity << "]";
error=f.str();
ok = false;
break;
}
if ((instr.m_content[0] >= 'A' && instr.m_content[0] <= 'Z') || instr.m_content[0] == '(') {
if (instr.m_content[0] != '(')
child.push_back(instr);
instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
instr.m_content="(";
child.push_back(instr);
for (int i = 0; i < arity; i++) {
if (i) {
instr.m_content=";";
child.push_back(instr);
}
auto const &node=stack[size_t(static_cast<int>(numElt)-arity+i)];
child.insert(child.end(), node.begin(), node.end());
}
instr.m_content=")";
child.push_back(instr);
stack.resize(size_t(static_cast<int>(numElt)-arity+1));
stack[size_t(static_cast<int>(numElt)-arity)] = child;
continue;
}
if (arity==1) {
instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
stack[numElt-1].insert(stack[numElt-1].begin(), instr);
if (wh==0x3 && pos+2==endPos)
break;
continue;
}
if (arity==2) {
instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
stack[numElt-2].push_back(instr);
stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end());
stack.resize(numElt-1);
continue;
}
ok=false;
error = "### unexpected arity";
break;
}
if (!ok) ;
else if (stack.size()==1 && stack[0].size()>1 && stack[0][0].m_content=="=") {
formula.insert(formula.begin(),stack[0].begin()+1,stack[0].end());
return true;
}
else
error = "###stack problem";
static bool first = true;
if (first) {
MWAW_DEBUG_MSG(("BeagleWksStructManager::readFormula: I can not read some formula\n"));
first = false;
}
f.str("");
for (auto const &i : stack) {
for (auto const &j : i)
f << j << ",";
}
f << error;
error = f.str();
return false;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: