/* -*- 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 <map>
#include <set>
#include <sstream>
#include <librevenge/librevenge.h>
#include "MWAWCell.hxx"
#include "MWAWTextListener.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWHeader.hxx"
#include "MWAWPosition.hxx"
#include "MWAWPictMac.hxx"
#include "MWAWPrinter.hxx"
#include "MWAWRSRCParser.hxx"
#include "MWAWSubDocument.hxx"
#include "FullWrtGraph.hxx"
#include "FullWrtStruct.hxx"
#include "FullWrtText.hxx"
#include "FullWrtParser.hxx"
/** Internal: the structures of a FullWrtParser */
namespace FullWrtParserInternal
{
//! Internal and low level: a structure used to define the list of zone in Zone 0 data of a FullWrite file
struct DocZoneStruct {
//! constructor
DocZoneStruct()
: m_pos(-1)
, m_structType(0)
, m_type(-1)
, m_nextId(0)
, m_fatherId(-1)
, m_childList() {}
//! the operator<<
friend std::ostream &operator<<(std::ostream &o, DocZoneStruct const &dt)
{
switch (dt.m_structType) {
case 0:
o << "empty,";
break;
case 1: // normal
break;
case 4: // hidden?
o << "hidden,";
break;
default:
o << "#structType=" << dt.m_structType << ",";
break;
}
if (dt.m_type!=-1)
o << FullWrtStruct::getTypeName(dt.m_type);
if (dt.m_nextId) o << "nId=" << dt.m_nextId << ",";
if (dt.m_fatherId>=0) o << "fId=" << dt.m_fatherId << ",";
if (!dt.m_childList.empty()) {
o << "childs=[";
for (auto id : dt.m_childList)
o << id << ",";
o << "],";
}
return o;
}
//! the file position
long m_pos;
//! the struct type
int m_structType;
//! the type
int m_type;
//! the next id
int m_nextId;
//! the father id
int m_fatherId;
//! the list of child id
std::vector<int> m_childList;
};
////////////////////////////////////////
//! Internal: the sidebar of a FullWrtParser
struct SideBar final : public FullWrtStruct::ZoneHeader {
//! constructor
explicit SideBar(FullWrtStruct::ZoneHeader &)
: m_box()
, m_page(0)
{
}
//! destructor
~SideBar() final;
//! the position (in point)
MWAWBox2f m_box;
//! the page
int m_page;
};
SideBar::~SideBar()
{
}
////////////////////////////////////////
//! Internal: the reference data call of a FullWrtParser
struct ReferenceCalledData {
// constructor
ReferenceCalledData()
: m_id(-1)
{
for (auto &val : m_values) val=0;
}
//! the operator<<
friend std::ostream &operator<<(std::ostream &o, ReferenceCalledData const &dt)
{
if (dt.m_id >= 0) o << "refId=" << dt.m_id << ",";
for (int i = 0; i < 5; i++) {
if (dt.m_values[i])
o << "f" << i << "=" << dt.m_values[i] << ",";
}
return o;
}
//! the reference id
int m_id;
//! some unknown values
int m_values[5];
};
////////////////////////////////////////
//! Internal: the state of a FullWrtParser
struct State {
//! constructor
State()
: m_pageSpanSet(false)
, m_fileZoneList()
, m_fileZoneFlagsList()
, m_docZoneList()
, m_docFileIdMap()
, m_fileDocIdMap()
, m_biblioId(-1)
, m_entryMap()
, m_variableRedirectMap()
, m_referenceRedirectMap()
, m_actPage(0)
, m_numPages(0)
, m_headerHeight(0)
, m_footerHeight(0)
{
for (auto &fl : m_zoneFlagsId) fl = -1;
}
//! insert a docId fileId in the correspondance map
bool addCorrespondance(int docId, int fileId)
{
if (m_docFileIdMap.find(docId) != m_docFileIdMap.end() ||
m_fileDocIdMap.find(fileId) != m_fileDocIdMap.end()) {
MWAW_DEBUG_MSG(("FullWrtParserInternal::State::addCorrespondance can not insert %d<->%d\n", docId, fileId));
return false;
}
m_fileDocIdMap[fileId]=docId;
m_docFileIdMap[docId]=fileId;
// update the zone type ( if possible )
if (docId >= 0 && docId < int(m_docZoneList.size()) &&
m_entryMap.find(fileId) != m_entryMap.end() &&
m_entryMap.find(fileId)->second)
m_entryMap.find(fileId)->second->m_type = m_docZoneList[size_t(docId)].m_type;
else {
MWAW_DEBUG_MSG(("FullWrtParserInternal::State::addCorrespondance can not update the zone type for %d<->%d\n", docId, fileId));
}
return true;
}
//! return the file zone id ( if found or -1)
int getFileZoneId(int docId) const
{
auto it = m_docFileIdMap.find(docId);
if (it == m_docFileIdMap.end()) {
MWAW_DEBUG_MSG(("FullWrtParserInternal::State::getFileZoneId can not find %d\n", docId));
return -1;
}
return it->second;
}
//! a flag to know if the page span has been filled
bool m_pageSpanSet;
//! the list of main zone flags id
int m_zoneFlagsId[3];
//! the list of file zone position
FullWrtStruct::EntryPtr m_fileZoneList;
//! the list of file zone flags
FullWrtStruct::EntryPtr m_fileZoneFlagsList;
//! the list of the documents zone list
std::vector<DocZoneStruct> m_docZoneList;
//! the correspondance doc id -> file id
std::map<int,int> m_docFileIdMap;
//! the correspondance file id -> doc id
std::map<int,int> m_fileDocIdMap;
//! the bibliography id
int m_biblioId;
//! zoneId -> entry
std::multimap<int, FullWrtStruct::EntryPtr > m_entryMap;
//! redirection docId -> variable docId
std::map<int,int> m_variableRedirectMap;
//! redirection docId -> reference docId
std::map<int,ReferenceCalledData> m_referenceRedirectMap;
int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
int m_headerHeight /** the header height if known */,
m_footerHeight /** the footer height if known */;
};
////////////////////////////////////////
//! Internal: the subdocument of a FullWrtParser
class SubDocument final : public MWAWSubDocument
{
public:
SubDocument(FullWrtParser &pars, MWAWInputStreamPtr const &input, int zoneId)
: MWAWSubDocument(&pars, input, MWAWEntry())
, m_id(zoneId) {}
//! destructor
~SubDocument() final {}
//! operator!=
bool operator!=(MWAWSubDocument const &doc) const final
{
if (MWAWSubDocument::operator!=(doc)) return true;
auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
if (!sDoc) return true;
if (m_id != sDoc->m_id) return true;
return false;
}
//! the parser function
void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
protected:
//! the subdocument id
int m_id;
};
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
{
if (!listener.get()) {
MWAW_DEBUG_MSG(("FullWrtParserInternall::SubDocument::parse: no listener\n"));
return;
}
auto *parser=dynamic_cast<FullWrtParser *>(m_parser);
if (!parser) {
MWAW_DEBUG_MSG(("FullWrtParserInternall::SubDocument::parse: no parser\n"));
return;
}
long pos = m_input->tell();
parser->send(m_id);
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
FullWrtParser::FullWrtParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
: MWAWTextParser(input, rsrcParser, header)
, m_state()
, m_graphParser()
, m_textParser()
{
init();
}
FullWrtParser::~FullWrtParser()
{
for (auto it : m_state->m_entryMap) {
FullWrtStruct::EntryPtr zone = it.second;
if (zone) zone->closeDebugFile();
}
}
void FullWrtParser::init()
{
resetTextListener();
setAsciiName("main-1");
m_state.reset(new FullWrtParserInternal::State);
// reduce the margin (in case, the page is not defined)
getPageSpan().setMargins(0.1);
m_graphParser.reset(new FullWrtGraph(*this));
m_textParser.reset(new FullWrtText(*this));
}
////////////////////////////////////////////////////////////
// new page, interface function
////////////////////////////////////////////////////////////
void FullWrtParser::newPage(int number)
{
if (number <= m_state->m_actPage || number > m_state->m_numPages)
return;
while (m_state->m_actPage < number) {
m_state->m_actPage++;
if (!getTextListener() || m_state->m_actPage == 1)
continue;
getTextListener()->insertBreak(MWAWTextListener::PageBreak);
}
}
MWAWVec2f FullWrtParser::getPageLeftTop() const
{
return MWAWVec2f(float(getPageSpan().getMarginLeft()),
float(getPageSpan().getMarginTop()+m_state->m_headerHeight/72.0));
}
bool FullWrtParser::getBorder(int bId, FullWrtStruct::Border &border) const
{
return m_graphParser->getBorder(bId, border);
}
int FullWrtParser::getNumDocZoneStruct() const
{
return int(m_state->m_docZoneList.size());
}
std::string FullWrtParser::getDocumentTypeName(int id) const
{
if (id<0||id >= int(m_state->m_docZoneList.size()))
return "";
return FullWrtStruct::getTypeName(m_state->m_docZoneList[size_t(id)].m_type);
}
void FullWrtParser::sendGraphic(int id)
{
if (id < 0 || id >= int(m_state->m_docZoneList.size())) {
MWAW_DEBUG_MSG(("FullWrtParser::sendGraphic: can not find graphic data for zone %d\n", id));
}
else {
auto const &data = m_state->m_docZoneList[size_t(id)];
if (data.m_type != 0x15) {
MWAW_DEBUG_MSG(("FullWrtParser::sendGraphic: call for zone[%x]\n", static_cast<unsigned int>(data.m_type)));
}
}
m_graphParser->sendGraphic(m_state->getFileZoneId(id));
}
bool FullWrtParser::send(int fileId, MWAWColor fontColor)
{
if (fileId < 0) {
if (getTextListener()) getTextListener()->insertChar(' ');
return true;
}
return m_textParser->send(fileId, fontColor);
}
////////////////////////////////////////////////////////////
// the parser
////////////////////////////////////////////////////////////
void FullWrtParser::parse(librevenge::RVNGTextInterface *docInterface)
{
if (!getInput().get() || !checkHeader(nullptr)) throw(libmwaw::ParseException());
bool ok = true;
try {
#ifdef DEBUG
// just test if a rsrc exists
if (getRSRCParser()) {
MWAW_DEBUG_MSG(("FullWrtParser::parse: find a ressource fork\n"));
getRSRCParser()->getEntry("STR ", 700);
}
#endif
// create the asciiFile
ascii().setStream(getInput());
ascii().open(asciiName());
checkHeader(nullptr);
ok = createZones();
if (ok) {
createDocument(docInterface);
m_graphParser->sendPageGraphics();
m_textParser->sendMainText();
m_graphParser->flushExtra();
#ifdef DEBUG
m_textParser->flushExtra();
#endif
}
bool first = true;
libmwaw::DebugStream f;
for (auto it : m_state->m_entryMap) {
FullWrtStruct::EntryPtr zone = it.second;
if (!zone || !zone->valid() || zone->isParsed()) continue;
f.str("");
if (zone->hasType("UnknownZone"))
f << "Entries(NotParsed)";
else
f << "Entries(" << zone->type() << ")";
if (zone->hasType("Biblio")) {
MWAW_DEBUG_MSG(("FullWrtParser::parse: find some biblio zone unparsed!!!\n"));
}
else if (first) {
f << "###";
first = false;
MWAW_DEBUG_MSG(("FullWrtParser::parse: find some unparsed zone!!!\n"));
}
if (zone->m_nextId != -2) f << "[" << zone->m_nextId << "]";
f << "|" << *zone << ":";
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
asciiFile.addPos(zone->begin());
asciiFile.addNote(f.str().c_str());
asciiFile.addPos(zone->end());
asciiFile.addNote("_");
zone->closeDebugFile();
}
ascii().reset();
}
catch (...) {
MWAW_DEBUG_MSG(("FullWrtParser::parse: exception catched when parsing\n"));
ok = false;
}
resetTextListener();
if (!ok) throw(libmwaw::ParseException());
}
////////////////////////////////////////////////////////////
// create the document
////////////////////////////////////////////////////////////
void FullWrtParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
{
if (!documentInterface) return;
if (getTextListener()) {
MWAW_DEBUG_MSG(("FullWrtParser::createDocument: listener already exist\n"));
return;
}
// update the page
m_state->m_actPage = 0;
int numPage = m_textParser->numPages();
if (m_graphParser->numPages()>numPage)
numPage=m_graphParser->numPages();
m_state->m_numPages = numPage;
// create the page list
int actHeaderId = -1, actFooterId = -1;
std::shared_ptr<FullWrtParserInternal::SubDocument> headerSubdoc, footerSubdoc;
std::vector<MWAWPageSpan> pageList;
for (int i = 0; i < m_state->m_numPages;) {
int numSim[2]= {1,1};
int headerId = m_textParser->getHeaderFooterId(true,i+1, numSim[0]);
if (headerId != actHeaderId) {
actHeaderId = headerId;
if (actHeaderId == -1)
headerSubdoc.reset();
else
headerSubdoc.reset
(new FullWrtParserInternal::SubDocument(*this, getInput(), headerId));
}
int footerId = m_textParser->getHeaderFooterId(false, i+1, numSim[1]);
if (footerId != actFooterId) {
actFooterId = footerId;
if (actFooterId == -1)
footerSubdoc.reset();
else
footerSubdoc.reset
(new FullWrtParserInternal::SubDocument(*this, getInput(), footerId));
}
MWAWPageSpan ps(getPageSpan());
if (headerSubdoc) {
MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
header.m_subDocument=headerSubdoc;
ps.setHeaderFooter(header);
}
if (footerSubdoc) {
MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
footer.m_subDocument=footerSubdoc;
ps.setHeaderFooter(footer);
}
if (numSim[1] < numSim[0]) numSim[0]=numSim[1];
if (numSim[0] < 1) numSim[0]=1;
ps.setPageSpan(numSim[0]);
i+=numSim[0];
pageList.push_back(ps);
}
// retrieve the header/footer
MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
setTextListener(listen);
listen->startDocument();
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool FullWrtParser::createFileZones()
{
/* FileZonePos: define a list of zone in the file and a link between these zone
FileZoneFlags: define the list of contents zone and link each to the first file zone
*/
// first creates the zone (entry are mapped by number of the zones in the file)
if (m_state->m_fileZoneList)
readFileZonePos(m_state->m_fileZoneList);
// update each main filezone, ie. move appart the 3 main zone and sets their final ids
if (m_state->m_fileZoneFlagsList)
readFileZoneFlags(m_state->m_fileZoneFlagsList);
// finally, remapped the enry by fId
std::vector<FullWrtStruct::EntryPtr > listZones;
for (auto it : m_state->m_entryMap)
listZones.push_back(it.second);
m_state->m_entryMap.clear();
for (auto &entry : listZones) {
if (!entry->valid() || entry->isParsed()) continue;
int fId = entry->id();
if (entry->m_typeId == -1) fId=-fId-1;
if (m_state->m_entryMap.find(fId) != m_state->m_entryMap.end()) {
MWAW_DEBUG_MSG(("FullWrtParser::createFileZones: can not find generic zone id %d\n",fId));
}
else
m_state->m_entryMap.insert
(std::multimap<int, FullWrtStruct::EntryPtr >::value_type(fId, entry));
}
return true;
}
bool FullWrtParser::createZones()
{
createFileZones();
// first treat the main zones
std::vector<FullWrtStruct::EntryPtr > mainZones;
mainZones.resize(3);
for (auto it : m_state->m_entryMap) {
FullWrtStruct::EntryPtr &zone = it.second;
if (!zone || !zone->valid() || zone->isParsed()) continue;
if (zone->m_typeId != -1 || zone->id() < 0 || zone->id() >= 3)
continue;
auto zId = size_t(zone->id());
if (mainZones[size_t(zId)]) {
MWAW_DEBUG_MSG(("FullWrtParser::createZones: Oops main zone %d already founded\n", int(zId)));
continue;
}
mainZones[zId] = zone;
}
if (!mainZones[1] || !readDocZoneStruct(mainZones[1])) {
MWAW_DEBUG_MSG(("FullWrtParser::createZones: can not read the docZoneStruct zone\n"));
}
if (!mainZones[0] || !readDocZoneData(mainZones[0])) {
MWAW_DEBUG_MSG(("FullWrtParser::createZones: can not read the docZoneData zone\n"));
}
if (!mainZones[2] || !readDocInfo(mainZones[2])) {
MWAW_DEBUG_MSG(("FullWrtParser::createZones: can not read the document information zone\n"));
}
// now treat the other zones
for (auto it : m_state->m_entryMap) {
FullWrtStruct::EntryPtr &zone = it.second;
if (!zone || !zone->valid() || zone->isParsed()) continue;
if (zone->m_typeId >= 0) {
// first use the zone type
bool done = false;
switch (zone->m_type) {
case 0x15:
done = m_graphParser->readGraphic(zone);
break;
case 0xa:
case 0xb:
case 0xc:
case 0xd:
case 0xe:
case 0xf:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x18:
done = m_textParser->readTextData(zone);
break;
default:
break;
}
if (done) continue;
// unknown, so try all possibilities
if (m_graphParser->readGraphic(zone)) continue;
if (m_textParser->readTextData(zone)) continue;
}
else if (zone->m_typeId == -1) {
if (zone->id()>=0 && zone->id()< 3) {
MWAW_DEBUG_MSG(("FullWrtParser::createZones: Oops find an unparsed main zone %d\n", zone->id()));
}
else if (zone->hasType("Biblio")) {
MWAW_DEBUG_MSG(("FullWrtParser::createZones: find a bibliography zone: unparsed\n"));
}
else {
MWAW_DEBUG_MSG(("FullWrtParser::createZones: find unexpected general zone\n"));
}
}
}
m_textParser->prepareData();
return true;
}
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool FullWrtParser::checkHeader(MWAWHeader *header, bool /*strict*/)
{
*m_state = FullWrtParserInternal::State();
MWAWInputStreamPtr input = getInput();
if (!input || !input->hasDataFork())
return false;
int const minSize=50;
input->seek(minSize,librevenge::RVNG_SEEK_SET);
if (int(input->tell()) != minSize) {
MWAW_DEBUG_MSG(("FullWrtParser::checkHeader: file is too short\n"));
return false;
}
if (!readDocPosition())
return false;
input->seek(0,librevenge::RVNG_SEEK_SET);
if (header)
header->reset(MWAWDocument::MWAW_T_FULLWRITE, 1);
return true;
}
bool FullWrtParser::readDocInfo(FullWrtStruct::EntryPtr zone)
{
if (zone->length() < 0x4b2)
return false;
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
long pos = zone->begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto N = static_cast<int>(input->readLong(2));
if (!N) return false;
input->seek(4, librevenge::RVNG_SEEK_CUR);
auto val = static_cast<int>(input->readLong(2));
if (N < val-2 || N > val+2) return false;
input->seek(2, librevenge::RVNG_SEEK_CUR);
auto nC = static_cast<int>(input->readULong(1));
if (!nC || nC > 70) return false;
for (int i = 0; i < nC; i++) {
auto c = static_cast<int>(input->readULong(1));
if (c < 0x20) return false;
}
zone->setParsed(true);
input->seek(pos+2, librevenge::RVNG_SEEK_SET);
f << "Entries(DocInfo)|" << *zone << ":";
f << "N0=" << N << ",";
f << "unkn0=" << std::hex << input->readULong(2) << std::dec << ","; // big number
for (int i = 0; i < 2; i++) { // 0|1|a7|ff followed by a small number
val = static_cast<int>(input->readULong(1));
if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
}
val = static_cast<int>(input->readLong(2)); // almost always N
if (val != N) f << "N1=" << val << ",";
f << "unkn1=" << std::hex << input->readULong(2) << std::dec << ","; // big number
std::string title("");
nC = static_cast<int>(input->readULong(1));
for (int i = 0; i < nC; i++)
title += char(input->readLong(1));
f << "title=" << title << ",";
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
pos += 10+70; // checkme
input->seek(pos, librevenge::RVNG_SEEK_SET);
f.str("");
f << "DocInfo-A:";
for (int i = 0; i < 4; i++) { // 0, 1, 2, 80
val = static_cast<int>(input->readULong(1));
if (val)
f << "fl" << i << "=" << std::hex << val << std::dec << ",";
}
val = static_cast<int>(input->readLong(2)); // almost always 0, but find also 53, 64, ba, f4
if (val) f << "f0=" << val << ",";
f << "unkn=["; // 2 big number which seems realted
for (int i = 0; i < 2; i++)
f << std::hex << input->readULong(4) << std::dec << ",";
f << "],";
for (int i = 4; i < 6; i++) { // always 1, 0 ?
val = static_cast<int>(input->readULong(1));
if (val!=5-i) f << "fl" << i << "=" << std::hex << val << std::dec << ",";
}
// 5 relative big number : multiple of 4
auto ptr = long(input->readULong(4));
f << "unkn2=[" << std::hex << ptr << "," << std::dec;
for (int i = 0; i < 4; i++)
f << long(input->readULong(4))-ptr << ",";
f << "],";
val = static_cast<int>(input->readLong(2)); // almost always -2, but find also 6
if (val != -2)
f << "f1=" << val << ",";
for (int i = 0; i < 2; i++) { // always 0,0
val = static_cast<int>(input->readULong(2));
if (val) f << "f" << i+2 << "=" << std::hex << val << std::dec << ",";
}
for (int st=0; st < 2; st++) {
long actPos = input->tell();
val = static_cast<int>(input->readULong(2)); // big number
if (val) f << "g" << st << "=" << std::hex << val << std::dec << ",";
nC = static_cast<int>(input->readULong(1));
if (nC > 32) {
MWAW_DEBUG_MSG(("FullWrtParser::readDocInfo: can not read user name\n"));
nC = 0;
}
std::string s("");
for (int i = 0; i < nC; i++) s+=char(input->readLong(1));
if (nC)
f << "Username" << st << "=" << s << ",";
input->seek(actPos+36, librevenge::RVNG_SEEK_SET);
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
pos = input->tell();
asciiFile.addPos(pos);
asciiFile.addNote("DocInfo-B");
pos+=196;
input->seek(pos, librevenge::RVNG_SEEK_SET);
for (int i = 0; i < 6; i++) {
pos = input->tell();
f.str("");
f << "DocInfo-C" << i << ":" << input->readLong(2);
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+48, librevenge::RVNG_SEEK_SET);
}
for (int i = 0; i < 9; i++) {
pos = input->tell();
f.str("");
f << "DocInfo-D" << i << ":";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+38, librevenge::RVNG_SEEK_SET);
}
pos = input->tell();
asciiFile.addPos(pos);
asciiFile.addNote("DocInfo-E");
input->seek(pos+182, librevenge::RVNG_SEEK_SET);
// this part in v1 and v2
pos = input->tell();
f.str("");
f << "DocInfo-F:";
bool ok = true;
if (version()==2) {
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+436, librevenge::RVNG_SEEK_SET);
pos = input->tell();
if (!readEndDocInfo(zone)) {
asciiFile.addPos(pos);
asciiFile.addNote("DocInfoII#");
ok = false;
}
}
else {
if (pos+18 < zone->end()) {
val = static_cast<int>(input->readLong(2)); // always 0
if (val) f << "f0=" << val << ",";
val = static_cast<int>(input->readLong(4)); // can be a big number (often neg )
if (val) f << "f1=" << val << ",";
// always 0 except one time g0=a,g1=1d,g5=50fe
for (int i = 0; i < 6; i++) {
val = static_cast<int>(input->readULong(2));
if (val) f << "g" << i << "=" << std::hex << val << std::dec << ",";
}
}
else {
ok = false;
f << "#";
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
if (ok) {
pos=input->tell();
f.str("");
f << "DocInfo-G:";
auto type=static_cast<int>(input->readULong(2));
f << "f0=" << std::hex << type << std::dec << ",";
int dim[4];
for (auto &d : dim) d=static_cast<int>(input->readLong(2));
f << "dim=" << dim[1] << "x" << dim[0] << "<->"
<< dim[3] << "x" << dim[2] << ",";
// checkme: 3: followed by 2 int, 6-b: followed by 3 int, e-f: by 4, other?
int num=(type&0xF000)>=0xE000 ? 4:(type&0xF000)>0x3000 ? 3:2;
for (int i=0; i< num; ++i) { // f1=0x100, f3=3
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+1 << "=" << val << ",";
}
for (int st=0; st<2; ++st) { // probably left/right page
int margins[4];
for (auto &margin : margins) margin=static_cast<int>(input->readLong(2));
f << "pageDim" << st << "=" << margins[1] << "x" << margins[0] << "<->"
<< margins[3] << "x" << margins[2] << ",";
if (st==1)
break;
if (margins[0]<margins[2] && margins[2]<dim[2] && 2*(margins[2]-margins[0]) > dim[2] &&
margins[1]<margins[3] && margins[3]<dim[3] && 2*(margins[3]-margins[1]) > dim[3] &&
dim[2]>100 && dim[2]<2000 && dim[3]>100 && dim[3]<2000) {
getPageSpan().setMarginTop(double(margins[0])/72.0);
getPageSpan().setMarginBottom(double(dim[2]-margins[2])/72.0);
getPageSpan().setMarginLeft(double(margins[1])/72.0);
getPageSpan().setMarginRight(double(dim[3]-margins[3])/72.0);
getPageSpan().setFormLength(double(dim[2])/72.);
getPageSpan().setFormWidth(double(dim[3])/72.);
m_state->m_pageSpanSet=true;
}
else {
MWAW_DEBUG_MSG(("FullWrtParser::readDocInfo:can not read document margins!\n"));
}
}
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
/* seems to end with
00000009000043686f6f73657200
000000100000436f6e74726f6c2050616e656c7300
0000000b000046696e642046696c6500
0000001500004772617068696e672043616c63756c61746f7200
0000000f00004a69677361772050757a7a6c6500
0000000a00004b6579204361707300
0000000a00004e6f74652050616400
0000001500000000000000000000000000006361000000000000
*/
}
// try to retrieve the last part: printInfo+3 int
input->seek(zone->end()-130, librevenge::RVNG_SEEK_SET);
if (readPrintInfo(zone)) {
pos = input->tell();
f.str("");
f << "DocInfo-End:";
if (pos == zone->end()-6) {
// f0=0, f1=0|1(in v2) but can also be a big number in v1, f2=0|1a
for (int i = 0; i < 3; i++) {
val = static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
}
else
f << "#";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
else {
MWAW_DEBUG_MSG(("FullWrtParser::readDocInfo: can not find print info\n"));
asciiFile.addPos(zone->end()-130);
asciiFile.addNote("DocInfo-G#");
}
zone->closeDebugFile();
return true;
}
////////////////////////////////////////////////////////////
// read the end of the zone data
bool FullWrtParser::readEndDocInfo(FullWrtStruct::EntryPtr zone)
{
if (version() < 2)
return false;
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
// at least 4 possible zones, maybe more...
for (int i = 0; i < 5; i++) {
long pos = input->tell();
bool ok = true;
std::string name("");
for (int j = 0; j < 4; j++) {
auto val=int(input->readULong(1));
if (val < 9) {
ok = false;
break;
}
name+=char(val);
}
if (!ok || input->readULong(1)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
ok = false;
if (name=="font") // block0 : unseen
;
else if (name=="bord")
ok = m_graphParser->readBorderDocInfo(zone);
else if (name=="extr")
ok = m_textParser->readParaModDocInfo(zone);
else if (name=="cite") // block3
ok = readCitationDocInfo(zone);
if (ok)
continue;
MWAW_DEBUG_MSG(("FullWrtParser::readEndDocInfo: can not read block %s\n", name.c_str()));
input->seek(pos+5, librevenge::RVNG_SEEK_SET);
long blckSz = input->readLong(4);
if (blckSz < 2 || pos+8+blckSz > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
auto num=int(input->readULong(2));
f.str("");
f << "Entries(Doc" << name << "):N=" << num << ",###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+9+blckSz, librevenge::RVNG_SEEK_SET);
}
return true;
}
bool FullWrtParser::readCitationDocInfo(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
long pos = input->tell();
if (input->readULong(4)!=0x63697465 || input->readULong(1)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
long blckSz = input->readLong(4);
long endData = pos+9+blckSz;
int num = static_cast<int>(input->readULong(2)), val;
f << "Entries(RefValues):N=" << num << ",";
if (blckSz <= 2 || endData > zone->end() || pos+num > endData) {
MWAW_DEBUG_MSG(("FullWrtParser::readCitationDocInfo: problem reading the data block or the number of data\n"));
f << "###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (endData <= zone->end()) {
input->seek(endData, librevenge::RVNG_SEEK_SET);
return true;
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
for (int i = 0; i < num; i++) {
f.str("");
f << "RefValues-" << i << ",";
pos = input->tell();
auto sz = int(input->readULong(1));
if (input->tell()+sz > endData)
break;
std::string name("");
bool ok = true;
for (int j = 0; j < sz; j++) {
val = int(input->readULong(1));
if (val < 0x9) {
ok = false;
break;
}
name+=char(val);
}
if (!ok) break;
f << "\"" << name << "\",";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
if (input->tell() != endData) {
f.str("");
f << "RefValues-##";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(endData, librevenge::RVNG_SEEK_SET);
}
return true;
}
////////////////////////////////////////////////////////////
// read the print info
////////////////////////////////////////////////////////////
bool FullWrtParser::readPrintInfo(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
long pos = input->tell();
if (input->readULong(2) != 0) return false;
auto sz = long(input->readULong(2));
if (sz != 0x78)
return false;
long endPos = pos+4+sz;
input->seek(endPos, librevenge::RVNG_SEEK_SET);
if (long(input->tell()) != endPos) {
MWAW_DEBUG_MSG(("FullWrtParser::readPrintInfo: file is too short\n"));
return false;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
// print info
libmwaw::PrinterInfo info;
if (!info.read(input)) {
// we know that the size is ok, so let try to continue
asciiFile.addPos(pos);
asciiFile.addNote("Entries(PrintInfo):##");
input->seek(endPos, librevenge::RVNG_SEEK_SET);
MWAW_DEBUG_MSG(("FullWrtParser::readPrintInfo: can not read print info, continue\n"));
return true;
}
f << "Entries(PrintInfo):"<< info;
MWAWVec2i paperSize = info.paper().size();
MWAWVec2i pageSize = info.page().size();
if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
paperSize.x() <= 0 || paperSize.y() <= 0) return false;
if (!m_state->m_pageSpanSet) {
// define margin from print info
MWAWVec2i lTopMargin= -1 * info.paper().pos(0);
MWAWVec2i rBotMargin=info.paper().size() - info.page().size();
// move margin left | top
int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0;
int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0;
lTopMargin -= MWAWVec2i(decalX, decalY);
rBotMargin += MWAWVec2i(decalX, decalY);
// decrease right | bottom
int rightMarg = rBotMargin.x() -50;
if (rightMarg < 0) rightMarg=0;
int botMarg = rBotMargin.y() -50;
if (botMarg < 0) botMarg=0;
getPageSpan().setMarginTop(lTopMargin.y()/72.0);
getPageSpan().setMarginBottom(botMarg/72.0);
getPageSpan().setMarginLeft(lTopMargin.x()/72.0);
getPageSpan().setMarginRight(rightMarg/72.0);
getPageSpan().setFormLength(paperSize.y()/72.);
getPageSpan().setFormWidth(paperSize.x()/72.);
}
if (long(input->tell()) !=endPos) {
input->seek(endPos, librevenge::RVNG_SEEK_SET);
f << ", #endPos";
asciiFile.addDelimiter(input->tell(), '|');
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// read the document zone data
bool FullWrtParser::readDocZoneData(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
// int vers = version();
long pos = zone->begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
zone->setParsed(true);
f << "Entries(DZoneData)|" << *zone << ":";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
int val, prevTypeOk=-1;
// first try to read normally the zone
size_t numDocTypes = m_state->m_docZoneList.size();
for (size_t z = 1; z < m_state->m_docZoneList.size(); z++) {
FullWrtParserInternal::DocZoneStruct const &doc = m_state->m_docZoneList[z];
if (doc.m_type < 0) continue;
pos = input->tell();
if (pos+2 > zone->end()) break;
bool done = false;
for (int st = 0; st < 4; st++) {
input->seek(pos+st, librevenge::RVNG_SEEK_SET);
FullWrtStruct::ZoneHeader docData;
std::shared_ptr<FullWrtStruct::ZoneHeader> res;
docData.m_type = doc.m_type;
switch (doc.m_type) {
case 0:
done = m_textParser->readColumns(zone);
break;
case 1:
done = m_textParser->readParagraphTabs(zone, int(z));
break;
case 2:
done = m_textParser->readItem(zone, int(z), doc.m_structType==4);
break;
case 3:
done = m_textParser->readStyle(zone);
break;
case 4: {
if (pos+st+4> zone->end()) break;
f.str("");
f << "Entries(DZone4):";
int numOk = 0;
for (int j = 0; j < 2; j++) { // always 0|0
val = int(input->readLong(2));
if (val >= -2 && val <= 0) numOk++;
if (val) f << "f" << j << "=" << val << ",";
}
if (!numOk) break;
asciiFile.addPos(pos+st);
asciiFile.addNote(f.str().c_str());
done = true;
break;
}
case 6:
if (pos+st+2> zone->end()) break;
val = int(input->readULong(2));
if (!val || val > 0x200) break;
f.str("");
f << "Entries(DZone6):" << doc << ",v=" << val;
asciiFile.addPos(pos+st);
asciiFile.addNote(f.str().c_str());
done = true;
break;
case 0x13:
case 0x14:
res=m_graphParser->readSideBar(zone, docData);
break;
case 0x15:
res=m_graphParser->readGraphicData(zone, docData);
break;
case 0x19: // reference data?
done=readReferenceData(zone);
break;
case 0x1a: {
if (pos+st+12> zone->end()) break;
FullWrtParserInternal::ReferenceCalledData refData;
refData.m_id = int(input->readULong(2));
if (refData.m_id < 0 || refData.m_id >= int(numDocTypes) ||
m_state->m_docZoneList[size_t(refData.m_id)].m_type != 0x19)
break;
// two small number and then f0=first id in refData, f1=RefValueId?
// after 0 except one time f2=0x71
for (auto &data : refData.m_values) data = int(input->readULong(2));
f.str("");
f << "Entries(RefCalled):docId=" << refData;
if (m_state->m_referenceRedirectMap.find(int(z)) == m_state->m_referenceRedirectMap.end())
m_state->m_referenceRedirectMap[int(z)]=refData;
else {
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: oops, reference redirection already exists for docId=%d\n", int(z)));
}
asciiFile.addPos(pos+st);
asciiFile.addNote(f.str().c_str());
input->seek(pos+st+12, librevenge::RVNG_SEEK_SET);
done = true;
break;
}
case 0x1e:
if (pos+st+2> zone->end()) break;
val = int(input->readULong(2));
if (val<=0 || val >= int(numDocTypes)) break;
f.str("");
// normally a type 15 or 18 zone
f << "Entries(VariableData):docId=" << val
<< "[" << std::hex << m_state->m_docZoneList[size_t(val)].m_type
<< std::dec << "],";
if (m_state->m_variableRedirectMap.find(int(z)) == m_state->m_variableRedirectMap.end())
m_state->m_variableRedirectMap[int(z)]=val;
else {
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: oops, variable redirection already exists for docId=%d\n", int(z)));
}
asciiFile.addPos(pos+st);
asciiFile.addNote(f.str().c_str());
done = true;
break;
case 0x1f:
done = m_textParser->readDataMod(zone, int(z));
break;
default:
done=doc.m_type<=0x18 && readGenericDocData(zone, docData);
break;
}
if (res)
done=true;
if (done) {
int docId=res ? res->m_docId : docData.m_docId;
if (docId >= 0 && docId != int(z)) {
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: unexpected id %d != %d\n", docId, int(z)));
done = false;
}
else {
if (res)
m_state->addCorrespondance(res->m_docId, res->m_fileId);
break;
}
}
input->seek(pos+st, librevenge::RVNG_SEEK_SET);
if (input->readLong(1)) break;
}
if (done) {
prevTypeOk = doc.m_type;
continue;
}
f.str("");
f << "Entries(DZoneData)##:" << doc;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: loose reading at zone %d[%d:%d]\n", int(z), doc.m_type, prevTypeOk));
if (prevTypeOk != -1) prevTypeOk = -1;
//
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
bool lastOk = false;
while (input->tell()+4 < zone->end()) {
bool prevLastOk = lastOk;
lastOk = true;
pos = input->tell();
done=m_textParser->readParagraphTabs(zone)||m_textParser->readColumns(zone);
if (done) continue;
FullWrtStruct::ZoneHeader docData;
done=docData.read(zone);
if (done) {
if (docData.m_docId >int(z) && docData.m_docId < int(m_state->m_docZoneList.size())) {
z = size_t(docData.m_docId-1);
input->seek(pos, librevenge::RVNG_SEEK_SET);
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: continue reading at zone %d\n", docData.m_docId));
break;
}
continue;
}
if (prevLastOk) {
asciiFile.addPos(pos);
asciiFile.addNote("DZoneData##:");
}
lastOk = false;
input->seek(pos+1, librevenge::RVNG_SEEK_SET);
}
}
return true;
}
bool FullWrtParser::readDocZoneStruct(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
long pos = zone->begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto N = static_cast<int>(input->readLong(2));
if ((N & 0xF)||N<=0) return false;
input->seek(pos+6, librevenge::RVNG_SEEK_SET);
for (int i = 0; i < N-1; i++) {
if (input->tell() >= zone->end())
return false;
long v = input->readLong(1);
if (v==0) continue;
if (v!=1 && v!=4) {
if (2*i > N) {
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: find only %d/%d entries\n", i, N));
break;
}
return false;
}
input->seek(4+v, librevenge::RVNG_SEEK_CUR);
}
if (input->tell() > zone->end())
return false;
zone->setParsed(true);
f << "Entries(DZoneStruct)|" << *zone <<":";
if (N%16) { // always a multiple of 16?
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: N(%d) seems odd\n", N));
f << "###";
}
f << "N=" << N << ",";
input->seek(pos+2, librevenge::RVNG_SEEK_SET);
f << "unkn=" << std::hex << input->readULong(4) << std::dec << ",";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
std::set<int> seenSet;
auto &zoneList = m_state->m_docZoneList;
zoneList.resize(size_t(N)+1);
for (int i = 0; i < N-1; i++) {
pos = input->tell();
auto type = static_cast<int>(input->readULong(1));
if (type > 1 && type!=4) {
asciiFile.addPos(pos);
asciiFile.addNote("DZoneStruct-###");
break;
}
FullWrtParserInternal::DocZoneStruct dt;
dt.m_structType = type;
dt.m_pos = pos;
f.str("");
f << "DZoneStruct-" << i+1 << ":";
if (type) {
dt.m_type = static_cast<int>(input->readULong(1)); // small number between 0 and 1f
dt.m_nextId = static_cast<int>(input->readLong(2));
dt.m_fatherId = static_cast<int>(input->readLong(2));
if (dt.m_nextId < 0 || dt.m_nextId > N) {
f << "#nId=" << dt.m_nextId <<",";
dt.m_nextId = 0;
}
if (dt.m_fatherId < 0 || dt.m_fatherId > N) {
f << "#fId=" << dt.m_fatherId <<",";
dt.m_fatherId = -1;
}
if (dt.m_nextId) {
if (seenSet.find(dt.m_nextId) != seenSet.end()) {
f << "##nId=" << dt.m_nextId << ",";
dt.m_nextId = 0;
}
else
seenSet.insert(dt.m_nextId);
}
if (type==4) {
asciiFile.addDelimiter(input->tell(),'|');
input->seek(3, librevenge::RVNG_SEEK_CUR);
}
f << dt;
}
zoneList[size_t(i)+1]=dt;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
if (input->tell() != zone->end()) {
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: end seems odd\n"));
}
// build child list
for (size_t i = 0; i <= size_t(N); i++) {
int fId = zoneList[i].m_fatherId;
int nId = zoneList[i].m_nextId;
if (nId && zoneList[size_t(nId)].m_fatherId != fId) {
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: find incoherent children: %d and %d\n", int(i), nId));
continue;
}
auto cId = int(i);
if (seenSet.find(int(cId))!=seenSet.end() || fId < 0) continue;
// insert the child and its siblings
while (cId > 0 && cId <= N) {
zoneList[size_t(fId)].m_childList.push_back(cId);
cId = zoneList[size_t(cId)].m_nextId;
}
}
// check that we have no dag or cycle
seenSet.clear();
std::vector<int> toDoList;
toDoList.push_back(0);
seenSet.insert(0);
while (toDoList.size()) {
int id = toDoList.back();
toDoList.pop_back();
auto &nd = zoneList[size_t(id)];
size_t c = 0;
while (c < nd.m_childList.size()) {
int cId = nd.m_childList[c++];
if (seenSet.find(cId)==seenSet.end()) {
seenSet.insert(cId);
toDoList.push_back(cId);
continue;
}
MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: oops, find a unexpected dag or cycle\n"));
c--;
nd.m_childList.erase(nd.m_childList.begin()+int(c));
}
}
for (auto const &nd : zoneList) {
if (nd.m_childList.empty()) continue;
f.str("");
f << "childs=[";
for (auto id : nd.m_childList)
f << id << ",";
f << "],";
asciiFile.addPos(nd.m_pos >= 0 ? nd.m_pos : zone->begin());
asciiFile.addNote(f.str().c_str());
}
zone->closeDebugFile();
return true;
}
////////////////////////////////////////////////////////////
// read the correspondance data
bool FullWrtParser::readGenericDocData(FullWrtStruct::EntryPtr zone, FullWrtStruct::ZoneHeader &doc)
{
MWAWInputStreamPtr input = zone->m_input;
long pos = input->tell();
if (!doc.read(zone)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
int const vers = version();
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
int val;
int numSzFollowBlock = 0;
switch (doc.m_type) {
case 0xc:
case 0xd:
case 0xf:
case 0x11:
case 0x12:
case 0x15:
break;
case 0xa:
case 0xb:
case 0xe:
case 0x10:
case 0x18:
numSzFollowBlock = 1;
break;
case 0x13:
numSzFollowBlock = 3;
break;
default:
MWAW_DEBUG_MSG(("FullWrtParser::readGenericDocData: called with type=%d\n",doc.m_type));
case -1:
break;
}
if (input->tell()+1 > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
f.str("");
if (doc.m_type > 0)
f << "Entries(DZone" << std::hex << doc.m_type << std::dec << "):";
else
f << "Entries(DZoneUnkn" << "):";
f << doc;
if (!m_state->addCorrespondance(doc.m_docId, doc.m_fileId))
f << "#";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
for (int i = 0; i < numSzFollowBlock; i++) {
f.str("");
f << "DZone" << std::hex << doc.m_type << std::dec << "[" << i << "]:";
pos = input->tell();
auto sz = long(input->readULong(4));
if (sz < 0 || pos+sz+4 > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
f << "#";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return true;
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (sz) input->seek(sz, librevenge::RVNG_SEEK_CUR);
}
if (doc.m_type==0xa) {
asciiFile.addPos(input->tell());
asciiFile.addNote("DZonea[1]#");
input->seek(vers==2 ? 8 : 66, librevenge::RVNG_SEEK_CUR);
}
val = int(input->readLong(1));
if (doc.m_type==0xa) ;
else if (val==1) {
pos = input->tell();
auto sz = long(input->readULong(4));
if (sz && input->tell()+sz <= zone->end()) {
f.str("");
f << "DZone" << std::hex << doc.m_type << std::dec << "[end]:";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(sz, librevenge::RVNG_SEEK_CUR);
}
else {
MWAW_DEBUG_MSG(("FullWrtParser::readGenericDocData: find bad end data\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
}
}
else if (val) {
MWAW_DEBUG_MSG(("FullWrtParser::readGenericDocData: find bad end data(II)\n"));
}
return true;
}
bool FullWrtParser::readReferenceData(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
long pos = input->tell();
if (pos+22 > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
f.str("");
f << "Entries(RefData):";
long val = int(input->readULong(2));
int numOk = 0;
if (val == 0xa || val == 0xc) numOk++;
f << "type?=" << val << ",";
f << "unkn=["; // 3 small number and 0, f0 or f2 is probably an Id, f1=0|1|2
for (int i = 0; i < 4; i++) {
val = long(input->readULong(2));
if (val)
f << val << ",";
else
f << "_,";
if (i==3) break;
if (val>0 && val < 0x100) numOk++;
}
f << "],";
if (numOk <= 2) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
f << "ptr=" << std::hex << input->readULong(4) << std::dec << ",";
for (int i = 0; i < 2; i++) { // always 0 ?
val = long(input->readULong(2));
if (val)
f << "f" << i << "=" << val << ",";
}
long sz = input->readLong(4);
if (sz < 0 || pos+22+sz > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
auto numZones=int(m_state->m_docZoneList.size());
f << "callerId=["; // normally zone of type 0x1a
for (int i = 0; i < sz/2; i++) {
auto id = int(input->readLong(2));
if (id < 0 || id >= numZones ||
m_state->m_docZoneList[size_t(id)].m_type != 0x1a)
f << "#";
f << id << ",";
}
f << "],";
input->seek(pos+22+sz, librevenge::RVNG_SEEK_SET);
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// read the zone flag and positions
////////////////////////////////////////////////////////////
bool FullWrtParser::readFileZoneFlags(FullWrtStruct::EntryPtr zone)
{
int vers = version();
int dataSz = vers==1 ? 22 : 16;
if (!zone || zone->length()%dataSz) {
MWAW_DEBUG_MSG(("FullWrtParser::readFileZoneFlags: size seems odd\n"));
return false;
}
zone->setParsed(true);
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
long numElt = zone->length()/dataSz;
input->seek(zone->begin(), librevenge::RVNG_SEEK_SET);
std::multimap<int, FullWrtStruct::EntryPtr >::iterator it;
int numNegZone=3;
for (long i = 0; i < numElt; i++) {
long pos = input->tell();
auto id = static_cast<int>(input->readLong(2));
it = m_state->m_entryMap.find(id);
FullWrtStruct::EntryPtr entry;
f.str("");
if (it == m_state->m_entryMap.end()) {
if (id != -2) {
MWAW_DEBUG_MSG(("FullWrtParser::readFileZoneFlags: can not find entry %d\n",id));
f << "###";
}
entry.reset(new FullWrtStruct::Entry(input));
entry->setId(1000+id); // false id
}
else
entry = it->second;
entry->setType("UnknownZone");
auto val = static_cast<int>(input->readLong(2)); // always -2 ?
if (val != -2) f << "g0=" << val << ",";
val = static_cast<int>(input->readLong(2)); // always 0 ?
if (val) f << "g1=" << val << ",";
// a small number between 1 and 0x100
entry->m_values[0] = static_cast<int>(input->readLong(2));
for (int j = 0; j < 2; j++) { // always 0
val = static_cast<int>(input->readLong(2));
if (val) f << "g" << j+2 << "=" << val << ",";
}
/** -1: generic, -2: null, other fId */
entry->m_typeId = static_cast<int>(input->readLong(2));
if (entry->m_typeId == -2) ;
else if (entry->m_typeId != -1)
entry->setId(int(i));
else {
bool find = false;
for (int j = 0; j < 3; j++) {
if (i!=m_state->m_zoneFlagsId[j]) continue;
find = true;
entry->setId(j);
break;
}
if (!find) {
MWAW_DEBUG_MSG(("FullWrtParser::readFileZoneFlags: can not find generic zone id %ld\n",i));
f << "#";
entry->setId(numNegZone);
}
numNegZone++;
}
// v2: always 0|0x14, v1 two small number or 0x7FFF
entry->m_values[1] = static_cast<int>(input->readLong(1));
entry->m_values[2] = static_cast<int>(input->readLong(1));
if (vers == 1) {
for (int j = 0; j < 3; j++) { // always 0, -2|0, 0 ?
val = static_cast<int>(input->readLong(2));
if ((j==1 && val !=-2) || (j!=1 && val))
f << "g" << j+4 << "=" << val << ",";
}
}
std::string extra = f.str();
f.str("");
if (i==0) f << "Entries(FZoneFlags):";
else f << "FZoneFlags-" << i << ":";
f << *entry << ",";
f << extra;
if (entry->id() < 0) {
if (entry->m_typeId != -2) {
MWAW_DEBUG_MSG(("FullWrtParser::readFileZoneFlags: find a null zone with unexpected type\n"));
}
}
input->seek(pos+dataSz, librevenge::RVNG_SEEK_SET);
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
asciiFile.addPos(zone->end());
asciiFile.addNote("Entries(ZoneAfter)");
return true;
}
bool FullWrtParser::readFileZonePos(FullWrtStruct::EntryPtr zone)
{
int vers = version();
int dataSz = vers==1 ? 10 : 8;
if (zone->length()%dataSz) {
MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: size seems odd\n"));
return false;
}
zone->setParsed(true);
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
auto numElt = int(zone->length()/dataSz);
input->seek(zone->begin(), librevenge::RVNG_SEEK_SET);
int val;
// read data
std::set<long> filePositions;
std::vector<FullWrtStruct::EntryPtr > listEntry;
if (numElt>0)
listEntry.resize(size_t(numElt));
for (int i = 0; i < numElt; i++) {
long pos = input->tell();
long fPos = input->readLong(4);
FullWrtStruct::EntryPtr entry(new FullWrtStruct::Entry(input));
if (i == m_state->m_biblioId)
entry->setType("Biblio");
else
entry->setType("Unknown");
entry->m_nextId = static_cast<int>(input->readLong(2));
auto id = static_cast<int>(input->readLong(2));
f << "realId=" << id << ",";
entry->setId(i);
if (fPos >= 0) {
filePositions.insert(fPos);
entry->setBegin(fPos);
}
f.str("");
if (entry->begin()>=0)
f << "pos=" << std::hex << entry->begin() << std::dec << ",";
if (entry->m_nextId != -2) f << "nextId=" << entry->m_nextId << ",";
if (vers==1) {
val = static_cast<int>(input->readLong(2));
if (val) f << "f0=" << val << ",";
}
input->seek(pos+dataSz, librevenge::RVNG_SEEK_SET);
entry->setExtra(f.str());
f.str("");
if (i == 0) f << "Entries(FZonePos):";
else f << "FZonePos" << i << ":";
f << *entry;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (id != -2 && (id < 1 || id >= numElt)) {
MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: entry id seems bad\n"));
}
listEntry[size_t(i)] = entry;
}
filePositions.insert(zone->begin());
// compute end of each entry
for (auto entry : listEntry) {
if (!entry || entry->begin() < 0)
continue;
auto it=filePositions.find(entry->begin());
if (it == filePositions.end()) {
MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: can not find my entry\n"));
continue;
}
if (++it == filePositions.end()) {
MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: can not find my entry\n"));
continue;
}
entry->setEnd(*it);
if (entry->m_nextId < 0) continue;
if (entry->m_nextId >= numElt) {
entry->m_nextId = -1;
MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: can not find the next entry\n"));
continue;
}
if (!listEntry[size_t(entry->m_nextId)] || listEntry[size_t(entry->m_nextId)]->isParsed()) {
entry->m_nextId = -1;
MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: next entry %d is already used\n",
entry->m_nextId));
continue;
}
listEntry[size_t(entry->m_nextId)]->setParsed(true);
}
for (int i = 0; i < numElt; i++) {
auto entry = listEntry[size_t(i)];
if (!entry || !entry->valid() || entry->isParsed()) continue;
m_state->m_entryMap.insert
(std::multimap<int, FullWrtStruct::EntryPtr >::value_type(i, entry));
if (entry->m_nextId < 0) {
entry->m_input = input;
entry->m_asciiFile = std::shared_ptr<libmwaw::DebugFile>
(&asciiFile, MWAW_shared_ptr_noop_deleter<libmwaw::DebugFile>());
continue;
}
// ok we must reconstruct a file
FullWrtStruct::EntryPtr actEnt = entry;
librevenge::RVNGBinaryData &data = entry->m_data;
while (1) {
if (!actEnt->valid()) break;
input->seek(actEnt->begin(), librevenge::RVNG_SEEK_SET);
unsigned long read;
const unsigned char *dt = input->read(size_t(actEnt->length()), read);
data.append(dt, read);
asciiFile.skipZone(actEnt->begin(), actEnt->end()-1);
if (actEnt->m_nextId < 0) break;
actEnt = listEntry[size_t(actEnt->m_nextId)];
if (actEnt) actEnt->setParsed(true);
}
entry->update();
}
asciiFile.addPos(zone->end());
asciiFile.addNote("_");
return true;
}
////////////////////////////////////////////////////////////
// read the document header
////////////////////////////////////////////////////////////
bool FullWrtParser::readDocPosition()
{
MWAWInputStreamPtr input = getInput();
if (!input->checkPosition(48))
return false;
libmwaw::DebugStream f;
input->seek(-48, librevenge::RVNG_SEEK_END);
long pos = input->tell();
f << "Entries(DocPosition):";
long val;
m_state->m_biblioId = static_cast<int>(input->readLong(2));
if (m_state->m_biblioId != -2)
f << "bibId=" << m_state->m_biblioId << ",";
for (int i = 0; i < 4; i++) { // always 0?
val = input->readLong(2);
if (val) f << "f" << i << "=" << val <<",";
}
long sz[2];
for (int i = 0; i < 2; i++) {
FullWrtStruct::EntryPtr zone(new FullWrtStruct::Entry(input));
zone->m_asciiFile = std::shared_ptr<libmwaw::DebugFile>
(&ascii(), MWAW_shared_ptr_noop_deleter<libmwaw::DebugFile>());
zone->setBegin(long(input->readULong(4)));
zone->setLength((sz[i]=long(input->readULong(4))));
if (!input->checkPosition(zone->end()) || !zone->valid())
return false;
if (i == 1) m_state->m_fileZoneList = zone;
else m_state->m_fileZoneFlagsList = zone;
}
f << "flZones=[";
for (int i = 0; i < 3; i++) {
m_state->m_zoneFlagsId[2-i] = static_cast<int>(input->readLong(2));
f << m_state->m_zoneFlagsId[2-i] << ",";
}
f << "],";
val = input->readLong(2); // always 0 ?
if (val) f << "g0=" << val << ",";
// a big number
f << std::hex << "unkn=" << input->readULong(2) << std::dec << ",";
val = long(input->readULong(4));
if (val != 1 && val != 0xbeecf54L) // always 1 in v1 and 0xbeecf54L in v2 ?
f << std::hex << "unkn2=" << val << std::dec << ",";
// always 1 ?
val = long(input->readULong(4));
if (val != 1) // always 1 in v1
f << "g1=" << val << ",";
val = long(input->readULong(4));
if (val==0x46575254L) {
if ((sz[0]%16)==0 && (sz[1]%8)==0)
setVersion(2);
else if ((sz[0]%22)==0 && (sz[1]%10)==0)
setVersion(1);
else
return false;
}
else {
if (val != 1) f << "g2=" << val << ",";
if ((sz[0]%22)==0 && (sz[1]%10)==0)
setVersion(1);
else
return false;
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// the variable part
////////////////////////////////////////////////////////////
void FullWrtParser::sendReference(int id)
{
if (!getTextListener()) return;
if (id < 0 || id >= int(m_state->m_docZoneList.size())) {
MWAW_DEBUG_MSG(("FullWrtParser::sendReference: can not find data for id=%d\n", id));
return;
}
if (m_state->m_docZoneList[size_t(id)].m_type != 0x1a) {
MWAW_DEBUG_MSG(("FullWrtParser::sendReference: find unexpected type for fieldDataRedirect=%x\n", static_cast<unsigned int>(m_state->m_docZoneList[size_t(id)].m_type)));
return;
}
if (m_state->m_referenceRedirectMap.find(id) == m_state->m_referenceRedirectMap.end())
return; // ok, we have not read the reference
int docId = m_state->m_referenceRedirectMap.find(id)->second.m_id;
if (docId < 0 || docId >= int(m_state->m_docZoneList.size()) ||
m_state->m_docZoneList[size_t(docId)].m_type != 0x19) {
MWAW_DEBUG_MSG(("FullWrtParser::sendReference: find unexpected redirection id[%d] for reference %d\n", docId, id));
return;
}
static bool first = true;
if (first) {
first = false;
MWAW_DEBUG_MSG(("FullWrtParser::sendReference: sorry, this function is not implemented\n"));
}
}
////////////////////////////////////////////////////////////
// the variable part
////////////////////////////////////////////////////////////
void FullWrtParser::sendVariable(int id)
{
if (!getTextListener()) return;
if (id < 0 || id >= int(m_state->m_docZoneList.size())) {
MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: can not find data for id=%d\n", id));
return;
}
if (m_state->m_docZoneList[size_t(id)].m_type != 0x1e) {
MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: find unexpected type for fieldDataRedirect=%x\n", static_cast<unsigned int>(m_state->m_docZoneList[size_t(id)].m_type)));
return;
}
if (m_state->m_variableRedirectMap.find(id) == m_state->m_variableRedirectMap.end()) {
MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: can not find redirection for id=%d\n", id));
return;
}
int docId = m_state->m_variableRedirectMap.find(id)->second;
if (docId < 0 || docId >= int(m_state->m_docZoneList.size())) {
MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: find unexpected redirection id[%d] for variable %d\n", docId, id));
return;
}
auto const &data = m_state->m_docZoneList[size_t(docId)];
if (data.m_type==0x15)
sendGraphic(docId);
else if (data.m_type == 0x18) {
/** in this case, the content seems to be a textbox which contains the field display,
but as in general this zone is not read correctly (ie. the field is not found ) and
as sending textbox is not implemented, better to stop here...
*/
static bool first = true;
if (first) {
first = false;
MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: sorry, send text/field variable is not implemented\n"));
}
}
else {
MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: find unexpected redirection type[%x] for variable %d\n", static_cast<unsigned int>(data.m_type), id));
}
}
////////////////////////////////////////////////////////////
// send a text zone
////////////////////////////////////////////////////////////
void FullWrtParser::sendText(int id, libmwaw::SubDocumentType type, MWAWNote::Type wh)
{
if (!getTextListener()) return;
if (id >= 0 && id < int(m_state->m_docZoneList.size())) {
auto const &data = m_state->m_docZoneList[size_t(id)];
int docType = data.m_type;
if (type==libmwaw::DOC_NOTE && (docType==0xc|| docType==0xd)) ;
else if (type == libmwaw::DOC_COMMENT_ANNOTATION && docType == 0xb) ;
else {
MWAW_DEBUG_MSG(("FullWrtParser::sendText: call with %d[%x]\n", int(type),static_cast<unsigned int>(docType)));
}
}
else {
MWAW_DEBUG_MSG(("FullWrtParser::sendText: can not find data for id=%d\n", id));
}
int fId = m_state->getFileZoneId(id);
MWAWSubDocumentPtr subdoc(new FullWrtParserInternal::SubDocument(*this, getInput(), fId));
if (type==libmwaw::DOC_NOTE)
getTextListener()->insertNote(MWAWNote(wh), subdoc);
else if (type==libmwaw::DOC_COMMENT_ANNOTATION)
getTextListener()->insertComment(subdoc);
else {
MWAW_DEBUG_MSG(("FullWrtParser::sendText: unexpected type\n"));
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: