/* -*- 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 <sstream>
#include <librevenge/librevenge.h>
#include "MWAWGraphicListener.hxx"
#include "MWAWHeader.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWPictMac.hxx"
#include "MWAWPrinter.hxx"
#include "MWAWSubDocument.hxx"
#include "MsWksGraph.hxx"
#include "MsWksDocument.hxx"
#include "MsWks3Text.hxx"
#include "MsWks4Zone.hxx"
#include "MsWksDRParser.hxx"
/** Internal: the structures of a MsWksDRParser */
namespace MsWksDRParserInternal
{
////////////////////////////////////////
//! Internal: the state of a MsWksDRParser
struct State {
//! constructor
State()
: m_mainZoneId(0)
, m_actPage(0)
, m_numPages(0)
{
}
/** the main zone */
int m_mainZoneId;
int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
};
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
MsWksDRParser::MsWksDRParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
: MWAWGraphicParser(input, rsrcParser, header)
, m_state()
, m_listZones()
, m_document()
{
MWAWInputStreamPtr mainInput=input;
if (input->isStructured()) {
MWAWInputStreamPtr mainOle = input->getSubStreamByName("MN0");
if (mainOle)
mainInput=mainOle;
}
m_document.reset(new MsWksDocument(mainInput, *this));
init();
}
MsWksDRParser::~MsWksDRParser()
{
}
void MsWksDRParser::init()
{
resetGraphicListener();
setAsciiName("main-1");
m_state.reset(new MsWksDRParserInternal::State);
// reduce the margin (in case, the page is not defined)
getPageSpan().setMargins(0.1);
m_document->m_newPage=static_cast<MsWksDocument::NewPage>(&MsWksDRParser::newPage);
}
////////////////////////////////////////////////////////////
// new page
////////////////////////////////////////////////////////////
void MsWksDRParser::newPage(int number, bool softBreak)
{
if (!getGraphicListener() || number < m_state->m_actPage || number > m_state->m_numPages)
return;
long pos = m_document->getInput()->tell();
int const vers=version();
while (m_state->m_actPage <= number) {
if (++m_state->m_actPage!=1) {
if (softBreak)
getGraphicListener()->insertBreak(MWAWGraphicListener::SoftPageBreak);
else
getGraphicListener()->insertBreak(MWAWGraphicListener::PageBreak);
}
// first, send the background
if (vers==4) {
MsWksGraph::SendData sendData;
sendData.m_type = MsWksGraph::SendData::RBIL;
sendData.m_anchor = MWAWPosition::Page;
sendData.m_id = 0;
sendData.m_page = -1;
m_document->getGraphParser()->sendObjects(sendData);
}
MsWksGraph::SendData sendData;
sendData.m_type = MsWksGraph::SendData::RBDR;
sendData.m_anchor = MWAWPosition::Page;
sendData.m_page = m_state->m_actPage;
m_document->getGraphParser()->sendObjects(sendData);
}
m_document->getInput()->seek(pos, librevenge::RVNG_SEEK_SET);
}
////////////////////////////////////////////////////////////
// the parser
////////////////////////////////////////////////////////////
void MsWksDRParser::parse(librevenge::RVNGDrawingInterface *docInterface)
{
if (!checkHeader(nullptr) || !m_document || !m_document->getInput()) throw(libmwaw::ParseException());
bool ok = true;
try {
// create the asciiFile
m_document->initAsciiFile(asciiName());
checkHeader(nullptr);
ok = createZones();
if (ok) {
createDocument(docInterface);
for (int i=0; i< m_state->m_numPages; ++i)
newPage(i);
#if 0 && defined(DEBUG)
if (version()<=3)
m_document->getTextParser3()->flushExtra();
m_document->getGraphParser()->flushExtra();
#endif
}
m_document->ascii().reset();
}
catch (...) {
MWAW_DEBUG_MSG(("MsWksDRParser::parse: exception catched when parsing\n"));
ok = false;
}
resetGraphicListener();
if (!ok) throw(libmwaw::ParseException());
}
////////////////////////////////////////////////////////////
// create the document
////////////////////////////////////////////////////////////
void MsWksDRParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface)
{
if (!documentInterface) return;
if (getGraphicListener()) {
MWAW_DEBUG_MSG(("MsWksDRParser::createDocument: listener already exist\n"));
return;
}
std::vector<MWAWPageSpan> pageList;
m_state->m_actPage = 0;
m_document->getPageSpanList(pageList, m_state->m_numPages);
MWAWGraphicListenerPtr listen(new MWAWGraphicListener(*getParserState(), pageList, documentInterface));
setGraphicListener(listen);
listen->startDocument();
// time to send page information the graph parser and the text parser
m_document->getGraphParser()->setPageLeftTop
(MWAWVec2f(72.f*float(getPageSpan().getMarginLeft()),
72.f*float(getPageSpan().getMarginTop())+m_document->getHeaderFooterHeight(true)));
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool MsWksDRParser::createZones()
{
if (getInput()->isStructured())
m_document->createOLEZones(getInput());
MWAWInputStreamPtr input = m_document->getInput();
long pos = input->tell();
if (!m_document->readDocumentInfo(0x9a))
input->seek(pos, librevenge::RVNG_SEEK_SET);
if (m_document->hasHeader() && !m_document->readGroupHeaderFooter(true,99))
input->seek(pos, librevenge::RVNG_SEEK_SET);
pos = input->tell();
if (m_document->hasFooter() && !m_document->readGroupHeaderFooter(false,99))
input->seek(pos, librevenge::RVNG_SEEK_SET);
if (!readDrawHeader()) return false;
libmwaw::DebugFile &ascFile = m_document->ascii();
auto &typeZoneMap=m_document->getTypeZoneMap();
MWAWEntry group;
// now the main group of draw shape
m_state->m_mainZoneId= version()==4 ? 0 : m_document->getNewZoneId();
typeZoneMap.insert(std::multimap<int,MsWksDocument::Zone>::value_type
(MsWksDocument::Z_MAIN,MsWksDocument::Zone(MsWksDocument::Z_MAIN, m_state->m_mainZoneId)));
if (version()==4) {
pos=input->tell();
int id=m_document->getNewZoneId();
typeZoneMap.insert(std::multimap<int,MsWksDocument::Zone>::value_type
(MsWksDocument::Z_NONE,MsWksDocument::Zone(MsWksDocument::Z_NONE, id)));
group.setId(m_state->m_mainZoneId);
group.setName("RBIL");
if (!m_document->m_graphParser->readRB(input,group,1)) {
MWAW_DEBUG_MSG(("MsWksDRParser::createZones: can not read RBIL group\n"));
ascFile.addPos(pos);
ascFile.addNote("Entries(RBIL):###");
return false;
}
}
pos=input->tell();
group.setId(m_state->m_mainZoneId);
group.setName("RBDR");
if (!m_document->m_graphParser->readRB(input,group,1)) {
MWAW_DEBUG_MSG(("MsWksDRParser::createZones: can not read RBDR group\n"));
ascFile.addPos(pos);
ascFile.addNote("Entries(RBDR):###");
return false;
}
// normally, the file is now parsed, let check for potential remaining zones
if (!input->isEnd()) {
MWAW_DEBUG_MSG(("MsWksDRParser::createZones: find some extra data\n"));
while (!input->isEnd()) {
pos = input->tell();
MsWksDocument::Zone unknown;
if (!m_document->readZone(unknown) || input->tell()<=pos) {
ascFile.addPos(pos);
ascFile.addNote("Entries(End)");
ascFile.addPos(pos+100);
ascFile.addNote("_");
break;
}
}
}
std::vector<int> linesH, pagesH;
m_document->getGraphParser()->computePositions(m_state->m_mainZoneId, linesH, pagesH);
return true;
}
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool MsWksDRParser::readDrawHeader()
{
MWAWInputStreamPtr input=m_document->getInput();
int const vers=version();
long pos = input->tell();
auto N = static_cast<int>(input->readULong(2));
int headerSize = vers == 3 ? 4 : 88;
int dataSize = vers == 3 ? 4 : 51;
libmwaw::DebugStream f;
f << "FileHeader(A)";
if (!input->checkPosition(pos+headerSize+dataSize*N)) {
f << "###";
MWAW_DEBUG_MSG(("MsWksDRParser::readDrawHeader: Unknown header find N=%d\n", N));
m_document->ascii().addPos(pos);
m_document->ascii().addNote(f.str().c_str());
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
f << "N = " << N << ", v0 = " << input->readLong(2);
// version 4
// begin always by
// v0=1, v1=[2|6|9], v2=180, v3=1, v6=0x300, v7=0x400, v8=1, v11=0x300, v12=0x400, v14=255
// followed by
// v16=4003, v20=8, v31=100
// or v15=6, v16=-1, v17=-1, v19=0x300, v20=8, v21=1, v24=44, v25=6, v31=100
if (vers == 4) {
for (int i = 1; i < 35; i++) {
auto val = static_cast<int>(input->readLong(2));
if (val) f << ", v" << i << "=" << val;
}
// [0,0,1,1,0,0,0,0,1,1,0,1,1,1,1,0,]
// or [0,0,3,1,0,0,0,0,1,1,0,1,1,1,1,0,]
f << ",fl=[";
for (int i = 0; i < 16; i++) {
f << input->readLong(1) << ",";
}
f << "]";
}
m_document->ascii().addPos(pos);
m_document->ascii().addNote(f.str().c_str());
input->seek(pos+headerSize,librevenge::RVNG_SEEK_SET);
for (int i = 0; i < N; i++) {
pos = input->tell();
f.str("");
f << "FileHeader(A)[" << i << "]:";
auto v = static_cast<int>(input->readULong(2)); // normally 0xe or 0x800e
auto id = static_cast<int>(input->readLong(2));
f << std::hex << v << std::dec;
if (id != i+1) {
MWAW_DEBUG_MSG(("MsWksDRParser::readDrawHeader: bad data %i\n", id));
f << "###";
m_document->ascii().addPos(pos);
m_document->ascii().addNote(f.str().c_str());
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
if (vers==4) {
// always v0=255, v2=4003, v6=8, v16=-1, w2=3, w4=4
for (int j = 0; j < 20; j++) {
auto val = static_cast<int>(input->readLong(2));
if (val) f << ", v" << j << "=" << val;
}
for (int j = 0; j < 7; j++) {
auto val = static_cast<int>(input->readLong(1));
if (val) f << ", w" << j << "=" << val;
}
}
m_document->ascii().addPos(pos);
m_document->ascii().addNote(f.str().c_str());
input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET);
}
return true;
}
////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool MsWksDRParser::checkHeader(MWAWHeader *header, bool strict)
{
*m_state = MsWksDRParserInternal::State();
if (!m_document->checkHeader3(header, strict)) return false;
if (m_document->getKind() != MWAWDocument::MWAW_K_DRAW)
return false;
if (version() < 2 || version() > 4)
return false;
return true;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: