/* -*- 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 <librevenge-stream/librevenge-stream.h>
#include "libwps_internal.h"
#include "libwps_tools_win.h"
#include "WPSContentListener.h"
#include "WPSEntry.h"
#include "WPSHeader.h"
#include "WPSOLEParser.h"
#include "WPSPageSpan.h"
#include "WPSTextSubDocument.h"
#include "WPS4Graph.h"
#include "WPS4Text.h"
#include "WPS4.h"
namespace WPS4ParserInternal
{
//! Internal: the subdocument of a WPS4Parser
class SubDocument final : public WPSTextSubDocument
{
public:
//! type of an entry stored in textId
enum Type { Unknown, MN };
//! constructor for a text entry
SubDocument(RVNGInputStreamPtr const &input, WPS4Parser &pars, WPSEntry const &entry)
: WPSTextSubDocument(input, &pars), m_entry(entry) {}
//! destructor
~SubDocument() final {}
//! operator==
bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final
{
if (!doc || !WPSTextSubDocument::operator==(doc))
return false;
auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
if (!sDoc) return false;
return m_entry == sDoc->m_entry;
}
//! the parser function
void parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType subDocumentType) final;
//! the entry
WPSEntry m_entry;
};
void SubDocument::parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType subDocumentType)
{
if (!listener.get())
{
WPS_DEBUG_MSG(("WPS4ParserInternal::SubDocument::parse: no listener\n"));
return;
}
if (!dynamic_cast<WPSContentListener *>(listener.get()))
{
WPS_DEBUG_MSG(("WPS4ParserInternal::SubDocument::parse: bad listener\n"));
return;
}
auto &listen = reinterpret_cast<WPSContentListenerPtr &>(listener);
if (!m_parser)
{
listen->insertCharacter(' ');
WPS_DEBUG_MSG(("WPS4ParserInternal::SubDocument::parse: bad parser\n"));
return;
}
if (m_entry.isParsed() && subDocumentType != libwps::DOC_HEADER_FOOTER)
{
listen->insertCharacter(' ');
WPS_DEBUG_MSG(("WPS4ParserInternal::SubDocument::parse: this zone is already parsed\n"));
return;
}
m_entry.setParsed(true);
if (m_entry.type() != "TEXT")
{
listen->insertCharacter(' ');
WPS_DEBUG_MSG(("WPS4ParserInternal::SubDocument::parse: send not Text entry is not implemented\n"));
return;
}
if (!m_entry.valid())
{
if (subDocumentType != libwps::DOC_COMMENT_ANNOTATION)
{
WPS_DEBUG_MSG(("SubDocument::parse: empty document found...\n"));
}
listen->insertCharacter(' ');
return;
}
auto *mnParser = dynamic_cast<WPS4Parser *>(m_parser);
if (!mnParser)
{
WPS_DEBUG_MSG(("SubDocument::parse: bad parser...\n"));
listen->insertCharacter(' ');
return;
}
mnParser->send(m_entry, subDocumentType);
}
//! the state of WPS4
struct State
{
explicit State(libwps_tools_win::Font::Type fontType)
: m_isDosFile(false)
, m_eof(-1)
, m_fontType(fontType)
, m_OEMFontType(libwps_tools_win::Font::UNKNOWN)
, m_pageSpan()
, m_noFirstPageHeader(false)
, m_noFirstPageFooter(false)
, m_numColumns(1)
, m_actPage(0)
, m_numPages(0)
{
}
//! flag to know if the file is or not a dos file
bool m_isDosFile;
//! the last file position
long m_eof;
//! the user font type
libwps_tools_win::Font::Type m_fontType;
//! the OEM code page
libwps_tools_win::Font::Type m_OEMFontType;
//! the actual document size
WPSPageSpan m_pageSpan;
bool m_noFirstPageHeader /* true if the first page has no header */;
bool m_noFirstPageFooter /* true if the first page has no footer */;
int m_numColumns /** the number of columns */;
int m_actPage /** the actual page*/, m_numPages /* the number of pages */;
};
}
// constructor, destructor
WPS4Parser::WPS4Parser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
libwps_tools_win::Font::Type encoding)
: WPSParser(input, header)
, m_listener()
, m_graphParser()
, m_textParser()
, m_state()
{
m_state.reset(new WPS4ParserInternal::State(encoding));
m_graphParser.reset(new WPS4Graph(*this));
m_textParser.reset(new WPS4Text(*this, input));
}
WPS4Parser::~WPS4Parser()
{
}
// small funtion ( dimension, color, fileSize, ...)
float WPS4Parser::pageHeight() const
{
return float(m_state->m_pageSpan.getFormLength()-m_state->m_pageSpan.getMarginTop()-m_state->m_pageSpan.getMarginBottom());
}
float WPS4Parser::pageWidth() const
{
return float(m_state->m_pageSpan.getFormWidth()-m_state->m_pageSpan.getMarginLeft()-m_state->m_pageSpan.getMarginRight());
}
int WPS4Parser::numColumns() const
{
return m_state->m_numColumns;
}
libwps_tools_win::Font::Type WPS4Parser::getDefaultFontType() const
{
if (m_state->m_fontType != libwps_tools_win::Font::UNKNOWN)
return m_state->m_fontType;
return version()<=2 ? libwps_tools_win::Font::DOS_850 : libwps_tools_win::Font::WIN3_WEUROPE;
}
libwps_tools_win::Font::Type WPS4Parser::getOEMFontType() const
{
return m_state->m_OEMFontType;
}
bool WPS4Parser::getColor(int id, WPSColor &color) const
{
if (m_state->m_isDosFile)
{
static const uint32_t colorDosMap[]=
{
0x0, 0xFF0000, 0x00FF00, 0x0000FF,
0x00FFFF, 0xFF00FF, 0xFFFF00
};
if (id < 0 || id >= 7)
{
WPS_DEBUG_MSG(("WPS4Parser::getColor(): unknown Dos color id: %d\n",id));
return false;
}
color=WPSColor(colorDosMap[id]);
return true;
}
static const uint32_t colorMap[]=
{
// 0x00RRGGBB
0, // auto
0,
0x0000FF, 0x00FFFF,
0x00FF00, 0xFF00FF, 0xFF0000, 0xFFFF00,
0x808080, 0xFFFFFF, 0x000080, 0x008080,
0x008000, 0x800080, 0x800000, 0xC0C0C0
};
if (id < 0 || id >= 16)
{
WPS_DEBUG_MSG(("WPS4Parser::getColor(): unknown color id: %d\n",id));
return false;
}
color = WPSColor(colorMap[id]);
return true;
}
void WPS4Parser::setSizeFile(long sz)
{
if (sz > m_state->m_eof)
m_state->m_eof = sz;
}
bool WPS4Parser::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;
}
// listener, new page
void WPS4Parser::setListener(std::shared_ptr<WPSContentListener> const &listener)
{
m_listener = listener;
m_graphParser->setListener(m_listener);
m_textParser->setListener(m_listener);
}
std::shared_ptr<WPSContentListener> WPS4Parser::createListener(librevenge::RVNGTextInterface *interface)
{
std::vector<WPSPageSpan> pageList;
WPSPageSpan page1(m_state->m_pageSpan), ps(m_state->m_pageSpan);
WPSEntry ent = m_textParser->getHeaderEntry();
if (ent.valid())
{
WPSSubDocumentPtr subdoc(new WPS4ParserInternal::SubDocument
(getInput(), *this, ent));
ps.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::ALL, subdoc);
if (!m_state->m_noFirstPageHeader)
page1.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::ALL, subdoc);
}
ent = m_textParser->getFooterEntry();
if (ent.valid())
{
WPSSubDocumentPtr subdoc(new WPS4ParserInternal::SubDocument
(getInput(), *this, ent));
ps.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::ALL, subdoc);
if (!m_state->m_noFirstPageFooter)
page1.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::ALL, subdoc);
}
int numPages = m_textParser->numPages();
int graphPages = m_graphParser->numPages();
if (graphPages>=numPages) numPages = graphPages;
#ifdef DEBUG
// create all the pages + an empty page, if we have some remaining data...
numPages++;
#endif
pageList.push_back(page1);
for (int i = 1; i < numPages; i++) pageList.push_back(ps);
m_state->m_numPages=numPages;
return std::shared_ptr<WPSContentListener>
(new WPSContentListener(pageList, interface));
}
void WPS4Parser::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 (m_listener.get() == nullptr || m_state->m_actPage == 1)
continue;
m_listener->insertBreak(WPS_PAGE_BREAK);
m_graphParser->sendObjects(m_state->m_actPage);
}
}
////////////////////////////////////////////////////////////
// interface with the graph/text parser
////////////////////////////////////////////////////////////
int WPS4Parser::readObject(RVNGInputStreamPtr input, WPSEntry const &entry)
{
long actPos = input->tell();
int id=m_graphParser->readObject(input, entry);
input->seek(actPos, librevenge::RVNG_SEEK_SET);
return id;
}
void WPS4Parser::sendObject(WPSPosition const &position, int id)
{
return m_graphParser->sendObject(position, id);
}
void WPS4Parser::send(WPSEntry const &entry, libwps::SubDocumentType)
{
if (!entry.hasType("TEXT"))
{
WPS_DEBUG_MSG(("WPS4Parser::send: unknown entry type '%s'\n", entry.type().c_str()));
if (m_listener.get()) m_listener->insertCharacter(' ');
return;
}
RVNGInputStreamPtr input = getInput();
long actPos = input->tell();
m_textParser->readText(entry);
input->seek(actPos, librevenge::RVNG_SEEK_SET);
}
void WPS4Parser::createDocument(WPSEntry const &entry, libwps::SubDocumentType type)
{
if (m_listener.get() == nullptr) return;
WPSSubDocumentPtr subdoc(new WPS4ParserInternal::SubDocument
(getInput(), *this, entry));
if (type == libwps::DOC_COMMENT_ANNOTATION)
m_listener->insertComment(subdoc);
else
{
WPS_DEBUG_MSG(("WPS4Parser:createDocument error: unknown type: \"%d\"\n", int(type)));
}
}
void WPS4Parser::createNote(WPSEntry const &entry, librevenge::RVNGString const &label)
{
if (m_listener.get() == nullptr) return;
WPSSubDocumentPtr subdoc(new WPS4ParserInternal::SubDocument
(getInput(), *this, entry));
m_listener->insertLabelNote(WPSContentListener::FOOTNOTE, label, subdoc);
}
void WPS4Parser::createTextBox(WPSEntry const &entry, WPSPosition const &pos, librevenge::RVNGPropertyList &extras)
{
if (m_listener.get() == nullptr) return;
WPSSubDocumentPtr subdoc(new WPS4ParserInternal::SubDocument
(getInput(), *this, entry));
m_listener->insertTextBox(pos, subdoc, extras);
}
////////////////////////////////////////////////////////////
// main funtions to parse a document, its OLE structures and its main input
////////////////////////////////////////////////////////////
// main function to parse the document
void WPS4Parser::parse(librevenge::RVNGTextInterface *documentInterface)
{
RVNGInputStreamPtr input=getInput();
if (!input)
{
WPS_DEBUG_MSG(("WPS4Parser::parse: does not find main ole\n"));
throw (libwps::ParseException());
}
if (!checkHeader(nullptr, true))
throw (libwps::ParseException());
try
{
createOLEStructures();
}
catch (...)
{
WPS_DEBUG_MSG(("WPS4Parser::parse: exception catched when parsing secondary OLEs\n"));
}
ascii().setStream(input);
ascii().open("MN0");
try
{
createStructures();
}
catch (...)
{
WPS_DEBUG_MSG(("WPS4Parser::parse: exception catched when parsing MN0\n"));
throw (libwps::ParseException());
}
findDefaultEncoding();
setListener(createListener(documentInterface));
if (!m_listener)
{
WPS_DEBUG_MSG(("WPS4Parser::parse: can not create the listener\n"));
throw (libwps::ParseException());
}
m_listener->startDocument();
m_textParser->sendObjects(-1);
WPSEntry ent = m_textParser->getMainTextEntry();
if (ent.valid())
m_textParser->readText(ent);
else
{
WPS_DEBUG_MSG(("WPS4Parser::parse: can not find main text entry\n"));
throw (libwps::ParseException());
}
#ifdef DEBUG
m_textParser->flushExtra();
m_graphParser->sendObjects(-1);
#endif
m_listener->endDocument();
m_listener.reset();
ascii().reset();
}
// find and create all the zones ( normal/ole )
bool WPS4Parser::createStructures()
{
if (!findZones()) throw libwps::ParseException();
if (!m_textParser->readStructures()) throw libwps::ParseException();
m_graphParser->computePositions();
#ifdef DEBUG
WPS4Parser::NameMultiMap::iterator pos;
pos = getNameEntryMap().find("PRNT");
if (pos != getNameEntryMap().end()) readPrnt(pos->second);
pos = getNameEntryMap().find("DocWInfo");
if (pos != getNameEntryMap().end()) readDocWindowsInfo(pos->second);
#endif
return true;
}
bool WPS4Parser::createOLEStructures()
{
RVNGInputStreamPtr input=getFileInput();
if (!input) return false;
if (!input->isStructured()) return true;
WPSOLEParser oleParser("MN0", getDefaultFontType());
if (!oleParser.parse(input)) return false;
m_graphParser->storeObjects(oleParser.getObjectsMap());
#ifdef DEBUG
// there can remain some embedded Works subdocument ( WKS, ... )
// with name MN0 and some unknown picture ole
auto unparsed = oleParser.getNotParse();
for (auto const &name : unparsed)
{
if (name == "MN0")
continue;
WPS_DEBUG_MSG(("WPS4Parser::createOLEStructures:: Find unparsed ole: %s\n", name.c_str()));
#ifdef DEBUG_WITH_FILES
RVNGInputStreamPtr ole(input->getSubStreamByName(name.c_str()));
if (!ole.get())
{
WPS_DEBUG_MSG(("WPS4Parser::createOLEStructures: error: can find OLE part: \"%s\"\n", name.c_str()));
continue;
}
librevenge::RVNGBinaryData data;
if (libwps::readDataToEnd(ole,data))
libwps::Debug::dumpFile(data, name.c_str());
#endif
}
#endif
return true;
}
////////////////////////////////////////////////////////////
// low level
////////////////////////////////////////////////////////////
// ------------------------------------------------------------
// function to read/check the file header or to check that an entry is ok
// ------------------------------------------------------------
// parse a basic entry: ie offset + size and check if it is valid
bool WPS4Parser::parseEntry(std::string const &name)
{
RVNGInputStreamPtr input = getInput();
long actPos = input->tell();
WPSEntry zone;
zone.setBegin(long(libwps::readU32(input)));
zone.setLength(libwps::readU16(input));
zone.setType(name);
bool ok = zone.valid() && checkFilePosition(zone.end());
if (ok)
{
getNameEntryMap().insert(NameMultiMap::value_type(zone.type(), zone));
ascii().addPos(zone.begin());
std::string nm = "ZZ";
nm+= name;
ascii().addNote(nm.c_str());
ascii().addPos(zone.end());
ascii().addNote("_");
}
libwps::DebugStream f;
if (ok)
f << "Entries(ZZ"<< name << ")="
<< std::hex << zone.begin() << "(" << zone.length() << ")";
else
f << "___";
ascii().addPos(actPos);
ascii().addNote(f.str().c_str());
return ok;
}
// basic function to check if the header is ok
bool WPS4Parser::checkHeader(WPSHeader *header, bool /*strict*/)
{
RVNGInputStreamPtr input = getInput();
if (!input || !checkFilePosition(0x100))
{
WPS_DEBUG_MSG(("WPS4Parser::checkHeader: file is too short\n"));
return false;
}
/* let's do the strict minimum, we does not want to break old code */
input->seek(0x0, librevenge::RVNG_SEEK_SET);
uint8_t firstOffset = libwps::readU8(input);
uint8_t secondOffset = libwps::readU8(input);
if (secondOffset != 0xFE || firstOffset > 7) return false;
if (header)
{
if (firstOffset==1)
header->setNeedEncoding(true);
header->setMajorVersion(firstOffset<4 ? 2 : firstOffset<6 ? 3 : 4);
}
return true;
}
// read the document structure ...
bool WPS4Parser::findZones()
{
RVNGInputStreamPtr input = getInput();
if (!checkFilePosition(0x100))
{
WPS_DEBUG_MSG(("WPS4Parser::findZones: error: incomplete header\n"));
throw libwps::ParseException();
}
input->seek(0x0, librevenge::RVNG_SEEK_SET);
libwps::DebugStream f, f2;
f << "Entries(ZZHeader):";
int vers = libwps::read8(input);
long val = libwps::read8(input); // always 0xfe?
int apCreator = libwps::readU16(input);
int worksVersion = 0;
switch (vers)
{
case 1:
m_state->m_isDosFile = true;
switch (apCreator)
{
case 0xda1:
apCreator = 2;
WPS_FALLTHROUGH;
case 0:
case 1:
worksVersion = 1;
f << "vers=dos" << 1+apCreator << ",";
apCreator = 0;
break;
default:
// checkme
worksVersion= (apCreator<10000) ? 1 : 2;
f << "vers=dos3/win2,";
break;
}
break;
case 4:
worksVersion=3;
if (apCreator==0x4e27)
{
f << "vers=Win3.0,";
apCreator = 0;
}
else
f << "vers=Win3,";
break;
case 6:
worksVersion=4;
if (apCreator==0x5375)
{
f << "vers=Win4.0,";
apCreator = 0;
}
else
f << "vers=Win4,";
break;
default:
WPS_DEBUG_MSG(("WPS4Parser::findZones: can not read the version\n"));
f << "vers=unkn:" << vers << ",";
break;
}
if (worksVersion)
setVersion(worksVersion);
if (val != -2) f << "##unk=" << val << ",";
if (apCreator && apCreator != 0x4755)
f << "##apCreator=" << std::hex << apCreator << std::dec << ",";
f << "unkn1=("; // in general : same number appear two time
for (int i = 0; i < 2; i++)
f << std::hex << libwps::readU32(input) << ",";
f << "),dim?=(";
for (int i = 0; i < 2; i++)
f << libwps::readU16(input)/1440. << ",";
f << "),";
auto oem=int(libwps::readU16(input));
if ((oem>>4)&0x7ff)
{
auto type=libwps_tools_win::Font::getTypeForOEM((oem>>4)&0x7ff);
if (type != libwps_tools_win::Font::UNKNOWN)
{
m_state->m_OEMFontType = type;
if (version()<=1 && m_state->m_fontType==libwps_tools_win::Font::UNKNOWN)
m_state->m_fontType=type;
f << "codePage=" << libwps_tools_win::Font::getTypeName(type).cstr() << ",";
}
else
f << "#codePage=" << std::hex << ((oem>>4)&0x7ff) << std::dec << ",";
}
if (oem & 0x800f) f << "codePage[extra]=" << std::hex << (oem & 0x800f) << std::dec << ",";
val=libwps::read16(input);
if (val) f << "unkn2=" << val << ",";
f << "dim2=" << libwps::read16(input)/1440. << ",";
val = libwps::read32(input);
if (val) f << std::dec << "unkn3=" << val <<",";
ascii().addPos(0);
ascii().addNote(f.str().c_str());
f.str("");
// 0x1e -> 0x64
if (!m_textParser->readEntries()) return false;
// 0x64 -> 0x80
long actPos = 0x64;
input->seek(actPos, librevenge::RVNG_SEEK_SET);
readDocDim();
if (version() <= 1)
{
// CHECKME:
ascii().addPos(0x80);
ascii().addNote("ZZHeader-I");
ascii().addPos(0xd0);
ascii().addNote("ZZHeader-filename");
return true;
}
actPos = 0x80;
input->seek(actPos, librevenge::RVNG_SEEK_SET);
parseEntry("EOBJ");
actPos = 0x86;
input->seek(actPos, librevenge::RVNG_SEEK_SET);
f.str("");
f << std::hex;
// {-1,-1}|{0,0}, 0,0, 0x[08][03][235=2col 6=3col 8=4col], 0|425|720
for (int i = 0; i < 4; i++)
{
val = libwps::readU16(input);
if (!val)
{
f << "_,";
continue;
}
f << val << ",";
}
f << std::dec;
val = libwps::readU16(input);
if (val & 0xFF00) f << "#unkn=" << (val >> 8) << ",";
val &= 0xFF;
auto numCols = int(val/2);
if (numCols >= 1 && numCols <= 13)
{
if (version() >= 3)
m_state->m_numColumns = numCols;
else
{
// can this happen ?
WPS_DEBUG_MSG(("WPS4Parser::findZones: find some column in dos file: ignored\n"));
}
if (numCols != 1)
{
f << "numCols=" << numCols;
if (val & 1) f << "[borderLim]";
}
f << ",";
}
else if (val)
f << "##cols,";
int colSep = libwps::readU16(input);
if (colSep)
f << "colSep=" << colSep/1440. << ",";
ascii().addPos(actPos);
if (f.str().length())
{
f2.str("");
f2 << "ZZHeader-I(unkn):"<< f.str();
ascii().addNote(f2.str().c_str());
}
else ascii().addNote("___");
parseEntry("End1");
actPos = 0x98;
input->seek(actPos, librevenge::RVNG_SEEK_SET);
f.str("");
f << "ZZHeader-II:";
bool empty = true;
auto begP = long(libwps::readU32(input));
if (begP)
{
if (begP <= 0 || !checkFilePosition(begP)) f << "###";
else
{
ascii().addPos(begP);
ascii().addNote("EOText");
}
f << std::hex << "EOText=" << begP << ",";
empty = false;
}
int num = libwps::readU16(input);
long sz = libwps::readU16(input);
begP = long(libwps::readU32(input));
if (begP)
{
if (begP <= 0 || !checkFilePosition(begP)) f << "###";
else if (checkFilePosition(begP+num *sz))
{
WPSEntry zone;
zone.setType("PRNT");
for (int i = 0; i < num; i++)
{
zone.setBegin(begP + i*sz);
zone.setLength(sz);
zone.setId(i);
getNameEntryMap().insert(NameMultiMap::value_type(zone.type(), zone));
ascii().addPos(zone.begin());
std::string nm = "ZZPRNT(";
nm+= char('0'+i);
nm+=')';
ascii().addNote(nm.c_str());
ascii().addPos(zone.end());
ascii().addNote("_");
}
}
else
{
ascii().addPos(begP);
ascii().addNote("ZZPRNT");
}
f << std::hex << "Entries(ZZPRNT)=" << begP << "(" << sz << ")";
if (num != 1) f << "x" << num;
empty = false;
}
ascii().addPos(actPos);
if (empty) ascii().addNote("___");
else ascii().addNote(f.str().c_str());
parseEntry("DTTM");
parseEntry("DocWInfo");
actPos = 0xb0;
input->seek(actPos, librevenge::RVNG_SEEK_SET);
f.str("");
f << "ZZHeader-III:";
empty = true;
// ok, try to see if we can find other things...
int i = 0;
while (input->tell()+4 <= 0xd0)
{
val = long(libwps::readU32(input));
if (val)
{
f << "f" << std::dec << i << "=" << std::hex << val << ",";
empty = false;
}
i++;
}
ascii().addPos(actPos);
if (empty) ascii().addNote("___");
else ascii().addNote(f.str().c_str());
// find always a list of 0 here
ascii().addPos(0xd0);
ascii().addNote("ZZHeader-filename");
return true;
}
// ------------------------------------------------------------
// functions to read the document's dimension or printer structures
// ------------------------------------------------------------
// Read the page format from the file.
bool WPS4Parser::readDocDim()
{
WPSPageSpan page = WPSPageSpan();
RVNGInputStreamPtr &input = getInput();
input->seek(0x64, librevenge::RVNG_SEEK_SET);
long actPos = input->tell();
libwps::DebugStream f;
f << "Entries(DocDim):";
double margin[4], size[2];
for (double &i : margin) i = libwps::readU16(input)/1440.;
for (double &i : size) i = libwps::readU16(input)/1440.;
if (margin[0]+margin[1] > size[0] || margin[2]+margin[3] > size[1])
{
WPS_DEBUG_MSG(("WPS4Parser::readDocDim: error: the margins are too large for the page size\n"));
return false;
}
page.setMarginTop(margin[0]);
page.setMarginBottom(margin[1]);
page.setMarginLeft(margin[2]);
// decrease a little right margin if possible
double rMargin = margin[3] > 0.4 ? margin[3]-0.2 : 0.5*margin[3];
page.setMarginRight(rMargin);
page.setFormLength(size[0]);
page.setFormWidth(size[1]);
int unkns[8];
for (int &unkn : unkns) unkn = libwps::readU16(input);
int page_orientation = unkns[5];
if (page_orientation == 0)
page.setFormOrientation(WPSPageSpan::PORTRAIT);
else if (page_orientation == 1)
page.setFormOrientation(WPSPageSpan::LANDSCAPE);
else
{
WPS_DEBUG_MSG(("WPS4Parser::readDocDim: error: bad page orientation code\n"));
}
m_state->m_pageSpan = page;
f << "margin=("<< margin[2] << "x" << margin[0]
<< ", " << margin[3] << "x" << margin[1] << "), ";
f << "size=" << size[1] << "x" << size[0] << ",";
if (page_orientation) f << "orien=" << page_orientation << ",";
if (unkns[0]!=1) f << "firstPage=" << unkns[0] << ",";
if (unkns[1]==1)
{
m_state->m_noFirstPageHeader=true;
f << "noPage1Header,";
}
else if (unkns[1]) f << "#noPage1Header=" << unkns[1] << ",";
if (unkns[2]==1)
{
m_state->m_noFirstPageHeader=true;
f << "noPage1Footer,";
}
else if (unkns[2]) f << "#noPage1Footer=" << unkns[2] << ",";
f << "headerH=" << unkns[3]/1440. << ",";
f << "footerH=" << unkns[4]/1440. << ",";
if (unkns[6] != 100) f << "zoom=" << unkns[6] << "%,";
if (unkns[7]) f << "#unkn=" << unkns[7] << ","; // alway 0 ?
ascii().addPos(actPos);
ascii().addNote(f.str().c_str());
return true;
}
// encoding
void WPS4Parser::findDefaultEncoding()
{
if (m_state->m_fontType != libwps_tools_win::Font::UNKNOWN)
return;
RVNGInputStreamPtr &input = getInput();
auto begPos=input->tell();
std::string text;
for (int i=0; i<3; ++i)
{
auto const entry= i==0 ? m_textParser->getHeaderEntry() :
i==1 ? m_textParser->getFooterEntry() : m_textParser->getMainTextEntry();
if (!entry.valid())
continue;
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
for (long l=0; l<entry.length(); ++l)
{
if (input->isEnd()) break;
text.push_back(char(libwps::readU8(input)));
}
text.push_back(' ');
}
m_state->m_fontType=libwps_tools_win::Font::getTypeForTextZone
(text, version()<=2 ? libwps_tools_win::Font::DOS_FONT : libwps_tools_win::Font::WINDOWS_FONT);
input->seek(begPos, librevenge::RVNG_SEEK_SET);
}
// PRNT : the printer definition
bool WPS4Parser::readPrnt(WPSEntry const &entry)
{
if (!entry.valid()) return false;
RVNGInputStreamPtr &input = getInput();
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
libwps::DebugStream f;
long length = entry.length();
if (length < 0x174)
{
WPS_DEBUG_MSG(("WPS4Parser::readPrnt: length::=%ld is to short\n", length));
return false;
}
f << std::hex;
for (int st = 0; st < 2; st++)
{
float dim[8];
for (int i = 0; i < 8; i++)
{
if (i == 4 || i == 5)
dim[i] = float(libwps::readU32(input)/1440.);
else
dim[i] = float(libwps::read32(input)/1440.);
}
f << "dim"<< st << "=" << dim[5] << "x" << dim[4] << ",";
f << "margin"<< st << "=[" << dim[0] << "x" << dim[2] << ","
<< dim[3] << "x" << dim[1] << "],";
f << "head/foot??"<< st << "=" << dim[6] << "x" << dim[7] << ",";
}
f << std::dec;
long val;
/* I find f0=1, f1=0|1, f2=0|1, f4=100, f5=0|15, f23=0|372
Note: f23=372 and strLen!=0 only when f1=1 and f2=1 ( probably related)
*/
for (int i = 0; i < 24; i++)
{
val = libwps::read32(input);
if (val) f << "f" << i << "=" << val << ",";
}
val = libwps::read32(input);
if (val) f << "strLen?=" << val << ",";
/* I only find 0 here */
for (int i = 0; i < 52; i++)
{
val = libwps::read32(input);
if (val) f << "g" << i << "=" << val << ",";
}
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
length -= 372;
if (length)
{
/* In my files, I find one time a strLen string: a header/footer? */
ascii().addPos(input->tell());
f.str("");
f << "ZZPRNT(II):";
for (int i = 0; i < length; i++)
f << char(libwps::readU8(input));
ascii().addNote(f.str().c_str());
}
return true;
}
// Link : in Works4 seems to contain data corresponding to the page border, ...
bool WPS4Parser::readDocWindowsInfo(WPSEntry const &entry)
{
if (!entry.valid()) return false;
RVNGInputStreamPtr &input = getInput();
long length = entry.length();
if (length < 0x154)
{
WPS_DEBUG_MSG(("WPS4Parser::readDocWindowsInfo: length::=%ld is to short\n", length));
return false;
}
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
libwps::DebugStream f;
std::string str("");
int pos = 0, debSPos = -1;
// Find in one file str[0]="C:\Databases\Elem 02-03.wdb",str[120]="Query1"
while (pos++ < 0x132) // try to find some strings
{
auto c = char(libwps::read8(input));
if (c == '\0')
{
if (debSPos>=0)
f << "str[" << std::hex << debSPos << "]=\"" << str << "\",";
debSPos = -1;
str = std::string("");
continue;
}
if (debSPos < 0) debSPos = pos-1;
str += c;
}
if (debSPos>=0)
f << "str[" << debSPos << "]=\"" << str << "\",";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
f.str("");
input->seek(entry.begin()+0x132, librevenge::RVNG_SEEK_SET);
f << "ZZDocWInfo(II):" << std::hex;
// f0=f1=-1 in one file, f0=f1=0 in another file
// f0=e6|1b0|2d0 ( but 2d0 in 2/3 of the files), 100<f1<438
for (int i = 0; i < 2; i++)
{
long val = libwps::read32(input);
if (val)
f << "f" << i << "=" << val << ",";
}
int dim[2]; // TB, LR
for (int &i : dim) i = libwps::read16(input);
if (dim[0] || dim[1])
f << "pageBorderDist=" << dim[1]/1440. << "x" << dim[0]/1440. << ",";
long val = libwps::readU8(input);
WPSColor color;
if (val && getColor(int(val), color))
f << "pageBorderColor=" << color << ",";
else if (val)
f << "#pageBorderColor=" << std::hex << val << std::dec << ",";
val = libwps::readU8(input);
if (val)
f << "pageBorderStyle=" << val << ",";
val = long(libwps::readU32(input));
if (val&2)
f << "pageBorderShaded,";
if (val&1)
f << "firstPageBorder,";
val &= 0xFFFFFFFCL;
if (val)
f << "#pageBorder?=" << std::hex << val << std::dec << ",";
// in one file: nothing ( case when f0=f1=-1)
//
// h0-h1: seems to correspond to a text zone
for (int i = 0; i < 4; i++)
{
long val_ = libwps::read32(input);
if (i == 0)
{
f << "textpos?=[" << 0x100+val_<< ",";
continue;
}
if (i == 1)
{
f << 0x100+val_ << "],";
continue;
}
if (val_)
f << "h" << i << "=" << val_ << ",";
}
ascii().addPos(entry.begin()+0x132);
ascii().addNote(f.str().c_str());
if (input->tell() != entry.end())
{
// can this happens ?
ascii().addPos(input->tell());
ascii().addNote("ZZDocWInfo(III)");
}
return true;
}
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */