/* -*- 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 <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>
#include <librevenge/librevenge.h>
#include "MWAWTextListener.hxx"
#include "MWAWDebug.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWInputStream.hxx"
#include "MWAWPageSpan.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSection.hxx"
#include "MWAWSubDocument.hxx"
#include "BeagleWksParser.hxx"
#include "BeagleWksStructManager.hxx"
#include "BeagleWksText.hxx"
/** Internal: the structures of a BeagleWksText */
namespace BeagleWksTextInternal
{
////////////////////////////////////////
//! Internal: a class used to store the font data of a BeagleWksText
struct Font {
//! constructor
Font()
: m_id(0)
, m_size(12)
, m_flags(0)
, m_color(0)
, m_extra()
{
}
/** returns a MWAWFont.
\note the font id remains filled with the local id */
MWAWFont getFont() const
{
MWAWFont res(m_id,float(m_size));
uint32_t flags=0;
if (m_flags&1) flags |= MWAWFont::boldBit;
if (m_flags&2) flags |= MWAWFont::italicBit;
if (m_flags&4) res.setUnderlineStyle(MWAWFont::Line::Simple);
if (m_flags&8) flags |= MWAWFont::embossBit;
if (m_flags&0x10) flags |= MWAWFont::shadowBit;
if (m_flags&0x100) res.set(MWAWFont::Script::super());
if (m_flags&0x200) res.set(MWAWFont::Script::sub());
if (m_flags&0x400) flags |= MWAWFont::uppercaseBit;
if (m_flags&0x800) flags |= MWAWFont::lowercaseBit;
res.setFlags(flags);
switch (m_color) {
case 63:
res.setColor(MWAWColor::white());
break;
case 100:
res.setColor(MWAWColor(0xFF,0xFF,0));
break;
case 168:
res.setColor(MWAWColor(0xFF,0,0xFF));
break;
case 236:
res.setColor(MWAWColor(0xFF,0,0));
break;
case 304:
res.setColor(MWAWColor(0,0xFF,0xFF));
break;
case 372:
res.setColor(MWAWColor(0,0xFF,0));
break;
case 440:
res.setColor(MWAWColor(0,0,0xFF));
break;
default:
break;
}
return res;
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Font const &fnt)
{
if (fnt.m_id) o << "id=" << fnt.m_id << ",";
if (fnt.m_size!=12) o << "sz=" << fnt.m_size << ",";
if (fnt.m_flags&1) o << "b,";
if (fnt.m_flags&2) o << "it,";
if (fnt.m_flags&4) o << "underline,";
if (fnt.m_flags&8) o << "outline,";
if (fnt.m_flags&0x10) o << "shadow,";
if (fnt.m_flags&0x100) o << "sup,";
if (fnt.m_flags&0x200) o << "sub,";
if (fnt.m_flags&0x400) o << "uppercase,";
if (fnt.m_flags&0x800) o << "lowercase,";
if (fnt.m_flags&0xF0E0)
o << "fl=" << std::hex << (fnt.m_flags&0xF0E0) << std::dec << ",";
switch (fnt.m_color) {
case 0: // black
break;
case 63:
o << "white,";
break;
case 100:
o << "yellow,";
break;
case 168:
o << "magenta,";
break;
case 236:
o << "red,";
break;
case 304:
o << "cyan,";
break;
case 372:
o << "green,";
break;
case 440:
o << "blue,";
break;
default:
o << "#color=" << fnt.m_color << ",";
break;
}
o << fnt.m_extra;
return o;
}
//! the font id
int m_id;
//! the font size
int m_size;
//! the font flags
int m_flags;
//! the font color
int m_color;
//! extra data
std::string m_extra;
};
////////////////////////////////////////
//! Internal: a class used to store the section data of a BeagleWksText
struct Section final : public MWAWSection {
//! constructor
Section()
: MWAWSection()
, m_ruler()
, m_hasFirstPage(false)
, m_hasHeader(false)
, m_hasFooter(false)
, m_pageNumber(1)
, m_usePageNumber(false)
, m_extra("")
{
for (auto &i : m_limitPos) i=0;
for (auto &i : m_parsed) i=false;
m_heights[0]=m_heights[1]=0;
m_balanceText=true;
}
//! destructor
~Section() final;
//! return the i^th entry
MWAWEntry getEntry(int i) const
{
MWAWEntry res;
if (i<0||i>=4) {
MWAW_DEBUG_MSG(("BeagleWksTextInternal::getEntry: called with bad id=%d\n",i));
return res;
}
if (m_limitPos[i]<=0)
return res;
res.setBegin(m_limitPos[i]);
res.setEnd(m_limitPos[i+1]-2);
return res;
}
//! return the header entry
MWAWEntry getHeaderEntry(bool fPage) const
{
return getEntry(fPage?0:2);
}
//! return true if we have a header
MWAWEntry getFooterEntry(bool fPage) const
{
return getEntry(fPage?1:3);
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Section const &sec)
{
o << static_cast<MWAWSection const &>(sec);
for (int i=0; i<4; ++i) {
if (sec.m_limitPos[i+1]<=sec.m_limitPos[i]+2)
continue;
static char const *wh[] = {"header[fP]", "footer[fP]", "header", "footer"};
o << wh[i] << "=" << std::hex << sec.m_limitPos[i]
<< "->" << sec.m_limitPos[i+1] << std::hex << ",";
}
if (sec.m_hasFirstPage) o << "firstPage[special],";
if (!sec.m_hasHeader) o << "hide[header],";
else if (sec.m_heights[0]) o << "h[header]=" << sec.m_heights[0] << ",";
if (!sec.m_hasFooter) o << "hide[footer],";
else if (sec.m_heights[1]) o << "h[footer]=" << sec.m_heights[1] << ",";
if (sec.m_pageNumber != 1) o << "pagenumber=" << sec.m_pageNumber << ",";
if (sec.m_usePageNumber) o << "pagenumber[use],";
o << sec.m_extra;
return o;
}
//! the default section ruler
MWAWParagraph m_ruler;
//! a flag to know if the first page is special
bool m_hasFirstPage;
//! a flag to know if we need to print the header
bool m_hasHeader;
//! a flag to know if we need to print the footer
bool m_hasFooter;
//! the data limits ( first page header, first page footer, header, footer, end)
long m_limitPos[5];
//! true if the data are send to the listener
mutable bool m_parsed[4];
//! the header/footer height
int m_heights[2];
//! the page number
int m_pageNumber;
//! true if we need to use the page number
bool m_usePageNumber;
//! extra data
std::string m_extra;
};
Section::~Section()
{
}
////////////////////////////////////////
//! Internal: the state of a BeagleWksText
struct State {
//! constructor
State()
: m_textEntry()
, m_sectionList()
, m_numPagesBySectionList()
, m_version(-1)
, m_numPages(-1)
, m_actualPage(1)
{
}
//! the main text entry
MWAWEntry m_textEntry;
//! the section list
std::vector<Section> m_sectionList;
//! the number of page by section
std::vector<int> m_numPagesBySectionList;
//! the file version
mutable int m_version;
int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
};
////////////////////////////////////////
//! Internal: the subdocument of a BeagleWksText
class SubDocument final : public MWAWSubDocument
{
public:
SubDocument(BeagleWksText &pars, MWAWInputStreamPtr &input, int hFId, int sId)
: MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
, m_textParser(&pars)
, m_hfId(hFId)
, m_sectId(sId)
{
}
//! destructor
~SubDocument() final {}
//! operator!=
bool operator!=(MWAWSubDocument const &doc) const final;
//! the parser function
void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
protected:
/** the text parser */
BeagleWksText *m_textParser;
//! the header/footer id
int m_hfId;
//! the section id
int m_sectId;
private:
SubDocument(SubDocument const &orig) = delete;
SubDocument &operator=(SubDocument const &orig) = delete;
};
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
{
if (MWAWSubDocument::operator!=(doc)) return true;
auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
if (!sDoc) return true;
if (m_textParser != sDoc->m_textParser) return true;
if (m_hfId != sDoc->m_hfId) return true;
if (m_sectId != sDoc->m_sectId) return true;
return false;
}
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
{
if (!listener.get()) {
MWAW_DEBUG_MSG(("BeagleWksTextInternal::SubDocument::parse: no listener\n"));
return;
}
if (!m_textParser) {
MWAW_DEBUG_MSG(("BeagleWksTextInternal::SubDocument::parse: no text parser\n"));
return;
}
long pos = m_input->tell();
m_textParser->sendHF(m_hfId, m_sectId);
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
BeagleWksText::BeagleWksText(BeagleWksParser &parser)
: m_parserState(parser.getParserState())
, m_state(new BeagleWksTextInternal::State)
, m_structureManager(parser.m_structureManager)
, m_mainParser(&parser)
{
}
BeagleWksText::~BeagleWksText()
{
}
int BeagleWksText::version() const
{
if (m_state->m_version < 0)
m_state->m_version = m_parserState->m_version;
return m_state->m_version;
}
int BeagleWksText::numPages() const
{
if (m_state->m_numPages <= 0)
const_cast<BeagleWksText *>(this)->countPages();
return m_state->m_numPages;
}
MWAWFont BeagleWksText::getFont(BeagleWksTextInternal::Font const &ft) const
{
MWAWFont font=ft.getFont();
font.setId(m_structureManager->getFontId(font.id()));
return font;
}
std::shared_ptr<MWAWSubDocument> BeagleWksText::getHeader(int page, int &numSimilar)
{
numSimilar=1;
std::shared_ptr<MWAWSubDocument> res;
int actPage=0, newSectionPage=0;
size_t s=0;
for (; s < m_state->m_numPagesBySectionList.size(); s++) {
newSectionPage+=m_state->m_numPagesBySectionList[s];
if (newSectionPage>page)
break;
actPage=newSectionPage;
}
if (s >= m_state->m_sectionList.size()) {
if (m_state->m_numPages>page)
numSimilar=m_state->m_numPages-page+1;
return res;
}
auto const &sec=m_state->m_sectionList[s];
bool useFPage=page==actPage && sec.m_hasFirstPage;
if (!useFPage)
numSimilar=newSectionPage-page;
if (sec.getHeaderEntry(useFPage).valid())
res.reset(new BeagleWksTextInternal::SubDocument
(*this, m_parserState->m_input, useFPage?0:2, int(s)));
return res;
}
std::shared_ptr<MWAWSubDocument> BeagleWksText::getFooter(int page, int &numSimilar)
{
numSimilar=1;
std::shared_ptr<MWAWSubDocument> res;
int actPage=0, newSectionPage=0;
size_t s=0;
for (; s < m_state->m_numPagesBySectionList.size(); s++) {
newSectionPage+=m_state->m_numPagesBySectionList[s];
if (newSectionPage>page)
break;
actPage=newSectionPage;
}
if (s >= m_state->m_sectionList.size()) {
if (m_state->m_numPages>page)
numSimilar=m_state->m_numPages-page+1;
return res;
}
auto const &sec=m_state->m_sectionList[s];
bool useFPage=page==actPage && sec.m_hasFirstPage;
if (!useFPage)
numSimilar=newSectionPage-page;
if (sec.getFooterEntry(useFPage).valid())
res.reset(new BeagleWksTextInternal::SubDocument
(*this, m_parserState->m_input, useFPage?1:3, int(s)));
return res;
}
////////////////////////////////////////////////////////////
// Intermediate level
////////////////////////////////////////////////////////////
//
// find the different zones
//
bool BeagleWksText::createZones(MWAWEntry &entry)
{
if (!entry.valid() || entry.length()<22) {
MWAW_DEBUG_MSG(("BeagleWksText::createZones: the entry seems bad\n"));
return false;
}
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=entry.begin();
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
f << "Entries(THeader):";
long val=input->readLong(4); // always 0x238
if (val!=0x238)
f << "f0=" << val << ",";
val=input->readLong(2);
if (val!=1)
f << "f1=" << val << ",";
val=long(input->readULong(4));
if (val >= entry.length())
val = 0;
auto nSections=int(entry.length()-val);
if (val<22|| nSections<6 || (nSections%6)) {
f << "###";
MWAW_DEBUG_MSG(("BeagleWksText::createZones: the data size seems bad\n"));
return false;
}
long endPos = pos+val;
nSections/=6;
for (int i=0; i<2; i++) { // f2=0, f3=6
val=input->readLong(2);
if (val)
f << "f" << i+2 << "=" << val << ",";
}
val=input->readLong(2);
f << "nSect=" << val << ",";
if (val!=nSections) {
f << "###";
MWAW_DEBUG_MSG(("BeagleWksText::createZones: the number of sections/pages seems bad\n"));
}
// checkme: after junk ?
ascFile.addDelimiter(input->tell(),'|');
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
std::vector<MWAWEntry> listEntries;
listEntries.reserve(size_t(nSections));
f.str("");
f << "Entries(Text):";
for (int i=0; i <nSections; ++i) {
pos=input->tell();
MWAWEntry pEntry;
pEntry.setBegin(entry.begin()+long(input->readULong(4)));
pEntry.setLength(long(input->readULong(2)));
f << std::hex << pEntry.begin() << "<->" << pEntry.end() << std::dec << ",";
if (!pEntry.valid() || pEntry.begin()+16 < entry.begin()
|| pEntry.end()>endPos) {
pEntry=MWAWEntry();
f << "###";
MWAW_DEBUG_MSG(("BeagleWksText::createZones: the page entry %d seems bad\n", i));
}
listEntries.push_back(pEntry);
input->seek(pos+6, librevenge::RVNG_SEEK_SET);
}
ascFile.addPos(endPos);
ascFile.addNote(f.str().c_str());
size_t p=0;
m_state->m_textEntry.setBegin(listEntries[0].begin());
for (p=0; p+1 < listEntries.size(); ++p) {
if (!listEntries[p].valid())
continue;
if (p) {
// use the section signature to diffentiate text/section (changeme)
input->seek(listEntries[p].begin(), librevenge::RVNG_SEEK_SET);
if (input->readLong(2)==0xdc)
break;
}
m_state->m_textEntry.setEnd(listEntries[p].end());
}
for (; p < listEntries.size(); ++p) {
BeagleWksTextInternal::Section sec;
if (listEntries[p].valid() && !readSection(listEntries[p], sec))
sec = BeagleWksTextInternal::Section();
m_state->m_sectionList.push_back(sec);
}
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return m_state->m_textEntry.valid();
}
void BeagleWksText::countPages()
{
if (!m_state->m_textEntry.valid()) {
MWAW_DEBUG_MSG(("BeagleWksText::countPages: can not find the main entry\n"));
return;
}
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=m_state->m_textEntry.begin(), endPos=m_state->m_textEntry.end();
input->seek(pos, librevenge::RVNG_SEEK_SET);
int nSectPages=0, nPages=1;
while (!input->isEnd()) {
pos=input->tell();
if (pos>=endPos) break;
auto c = static_cast<unsigned char>(input->readULong(1));
if (c) continue;
c=static_cast<unsigned char>(input->readULong(1));
bool done=false;
input->seek(pos, librevenge::RVNG_SEEK_SET);
switch (c) {
case 0: {
BeagleWksTextInternal::Font font;
done=readFont(font,endPos);
break;
}
case 1: {
MWAWParagraph para;
done=readParagraph(para,endPos);
break;
}
case 2:
if (pos+6 > endPos)
break;
input->seek(4, librevenge::RVNG_SEEK_CUR);
done = input->readLong(2)==0x200;
break;
case 3: { // type 3:page 4:section
if (pos+6 > endPos)
break;
input->seek(2, librevenge::RVNG_SEEK_CUR);
auto type=static_cast<int>(input->readLong(2));
if (input->readLong(2)!=0x300)
break;
if (type==3) {
nSectPages++;
nPages++;
}
else if (type==4) {
m_state->m_numPagesBySectionList.push_back(nSectPages);
nSectPages=0;
}
done=true;
break;
}
case 4: // picture
if (pos+8 > endPos)
break;
input->seek(6, librevenge::RVNG_SEEK_CUR);
done = input->readLong(2)==0x400;
break;
case 5: // a field
if (pos+36 > endPos)
break;
input->seek(34, librevenge::RVNG_SEEK_CUR);
done=input->readLong(2)==0x500;
break;
default:
break;
}
if (!done)
break;
}
}
//
// send the text
//
bool BeagleWksText::sendMainText()
{
return sendText(m_state->m_textEntry);
}
bool BeagleWksText::sendHF(int hfId, int sectId)
{
if (hfId<0||hfId>=4) {
MWAW_DEBUG_MSG(("BeagleWksText::sendHF: hfId=%d is bad\n", hfId));
return false;
}
if (sectId<0||sectId>=static_cast<int>(m_state->m_sectionList.size())) {
MWAW_DEBUG_MSG(("BeagleWksText::sendHF: can not find section %d\n", sectId));
return false;
}
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=input->tell();
auto const &sec=m_state->m_sectionList[size_t(sectId)];
sec.m_parsed[hfId]=true;
bool ok=sendText(sec.getEntry(hfId));
input->seek(pos,librevenge::RVNG_SEEK_SET);
return ok;
}
void BeagleWksText::flushExtra()
{
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
for (auto const &sec : m_state->m_sectionList) {
for (int j=0; j < 4; ++j) {
if (sec.m_parsed[j])
continue;
MWAWEntry hfEntry=sec.getEntry(j);
if (!hfEntry.valid()) {
if (hfEntry.begin()>0) {
ascFile.addPos(hfEntry.begin());
ascFile.addNote("_");
}
continue;
}
sendText(hfEntry);
}
}
}
bool BeagleWksText::sendText(MWAWEntry const &entry)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) {
MWAW_DEBUG_MSG(("BeagleWksText::sendText: can not find the listener\n"));
return false;
}
if (!entry.valid()) {
MWAW_DEBUG_MSG(("BeagleWksText::sendText: can not find the entry\n"));
return false;
}
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=entry.begin(), debPos=pos, endPos=entry.end();
bool isMain=entry.begin()==m_state->m_textEntry.begin();
size_t actSection=0, numSection=isMain ? m_state->m_sectionList.size() : 0;
if (actSection<numSection) {
if (listener->isSectionOpened())
listener->closeSection();
listener->openSection(m_state->m_sectionList[actSection++]);
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
f << "Text:";
BeagleWksTextInternal::Font font;
listener->setFont(getFont(font));
int actPage = 1, sectPage=1;
while (!input->isEnd()) {
pos=input->tell();
bool last=pos==endPos;
unsigned char c = last ? static_cast<unsigned char>(0) :
static_cast<unsigned char>(input->readULong(1));
if ((c==0 || c==0xd) && pos!=debPos) {
ascFile.addPos(debPos);
ascFile.addNote(f.str().c_str());
debPos=(c==0xd) ? pos+1:pos;
f.str("");
f << "Text:";
}
if (last) break;
if (c) {
f << c;
switch (c) {
case 0x1: // end zone marker, probably save to ignore
break;
case 0x9:
listener->insertTab();
break;
case 0xd:
listener->insertEOL();
break;
default:
listener->insertCharacter(static_cast<unsigned char>(c));
break;
}
continue;
}
c=static_cast<unsigned char>(input->readULong(1));
bool done=false;
input->seek(pos, librevenge::RVNG_SEEK_SET);
switch (c) {
case 0:
if (!readFont(font,endPos))
break;
done=true;
listener->setFont(getFont(font));
break;
case 1: {
MWAWParagraph para;
if (!readParagraph(para,endPos))
break;
done=true;
listener->setParagraph(para);
break;
}
case 2: {
if (pos+6 > endPos)
break;
input->seek(2, librevenge::RVNG_SEEK_CUR);
auto type=static_cast<int>(input->readLong(2));
if (input->readLong(2)!=0x200)
break;
f.str("");
f << "Entries(Field):";
switch (type) {
case 0:
case 1: {
std::stringstream s;
if (type==0) {
f << "pagenumber[section]";
s << sectPage;
}
else {
f << "section";
s << actSection;
}
listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str()));
break;
}
case 2:
listener->insertField(MWAWField(MWAWField::PageNumber));
f << "pagenumber";
break;
case 3:
listener->insertField(MWAWField(MWAWField::Date));
f << "date";
break;
case 4: {
MWAWField field(MWAWField::Time);
field.m_DTFormat="%H:%M";
listener->insertField(field);
f << "time";
break;
}
default:
MWAW_DEBUG_MSG(("BeagleWksText::sendText: find unknown field type=%d\n", type));
f << "#type=" << type << ",";
break;
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
done=true;
break;
}
case 3: {
if (pos+6 > endPos)
break;
input->seek(2, librevenge::RVNG_SEEK_CUR);
auto type=static_cast<int>(input->readLong(2));
if (input->readLong(2)!=0x300)
break;
f.str("");
f << "Entries(Break):";
switch (type) {
case 3:
f << "pagebreak";
sectPage++;
if (!isMain) break;
m_mainParser->newPage(++actPage);
break;
case 4:
f << "sectionbreak";
sectPage=1;
if (!isMain) break;
if (actSection<numSection) {
if (listener->isSectionOpened())
listener->closeSection();
listener->openSection(m_state->m_sectionList[actSection++]);
}
else {
MWAW_DEBUG_MSG(("BeagleWksText::sendText: can not find the new section\n"));
}
break;
default:
MWAW_DEBUG_MSG(("BeagleWksText::sendText: find unknown break type=%d\n", type));
f << "#type=" << type << ",";
break;
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
done=true;
break;
}
case 4: { // picture
if (pos+8 > endPos)
break;
input->seek(2, librevenge::RVNG_SEEK_CUR);
auto val=static_cast<int>(input->readLong(2));
auto id=static_cast<int>(input->readULong(2));
if (input->readLong(2)!=0x400)
break;
f.str("");
f << "Entries(Picture):id?=" << id << ",";
if (val) f << "f0=" << val << ",";
m_mainParser->sendFrame(id);
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
done=true;
break;
}
case 5: { // a field
if (pos+36 > endPos)
break;
input->seek(2, librevenge::RVNG_SEEK_CUR);
f.str("");
f << "Entries(Database):";
auto fl=static_cast<int>(input->readULong(1)); // find 40
if (fl) f << "fl=" << std::hex << fl << std::dec << ",";
auto fSz=static_cast<int>(input->readULong(1));
if (fSz>30) {
MWAW_DEBUG_MSG(("BeagleWksText::sendText: field name size seems bad\n"));
fSz=0;
f << "###";
}
std::string name("");
listener->insertUnicode(0xab);
for (int i=0; i < fSz; ++i) {
auto ch=static_cast<unsigned char>(input->readULong(1));
listener->insertCharacter(ch);
name+=char(ch);
}
listener->insertUnicode(0xbb);
f << name;
input->seek(pos+34, librevenge::RVNG_SEEK_SET);
if (input->readLong(2)!=0x500)
break;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
done=true;
break;
}
default:
break;
}
if (done) {
debPos=input->tell();
f.str("");
f << "Text:";
continue;
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
if (input->tell()!=endPos) {
ascFile.addPos(input->tell());
ascFile.addNote("Text:###");
MWAW_DEBUG_MSG(("BeagleWksText::sendText: find extra data\n"));
input->seek(endPos, librevenge::RVNG_SEEK_SET);
}
ascFile.addPos(endPos);
ascFile.addNote("_");
return true;
}
//////////////////////////////////////////////
// Fonts
//////////////////////////////////////////////
bool BeagleWksText::readFont(BeagleWksTextInternal::Font &font, long endPos)
{
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=input->tell();
if (pos+12 > endPos || input->readLong(2)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
font.m_size ^= static_cast<int>(input->readLong(2));
font.m_flags ^= static_cast<int>(input->readULong(2));
font.m_color ^= static_cast<int>(input->readLong(2));
auto val=static_cast<int>(input->readULong(1));
if (val) // find b1 and 20
f << "#f0=" << std::hex << val << std::dec << ",";
font.m_id ^= static_cast<int>(input->readULong(1));
font.m_extra=f.str();
f.str("");
f << "Entries(FontDef):" << font;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
// now the reverse header
if (input->readLong(2)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
input->seek(pos+12, librevenge::RVNG_SEEK_SET);
return true;
}
//////////////////////////////////////////////
// Paragraph
//////////////////////////////////////////////
bool BeagleWksText::readParagraph(MWAWParagraph ¶, long endPos, bool inSection)
{
para=MWAWParagraph();
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=input->tell();
if (pos+23 > endPos) return false;
int fSz=0;
if (!inSection) {
bool ok= input->readLong(2)==1;
fSz=ok ? static_cast<int>(input->readULong(1)) : 0;
if (!ok || fSz < 19 || pos+4+fSz > endPos) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
}
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
para.setInterline(1.+double(input->readULong(1))/10., librevenge::RVNG_PERCENT);
// para spacing, before/after
para.m_spacings[1] = para.m_spacings[2] =
(double(input->readULong(1))/10.)*6./72.;
auto fl=static_cast<int>(input->readULong(1));
switch (fl&0xf) {
case 1: // left
break;
case 2:
para.m_justify=MWAWParagraph::JustificationRight;
break;
case 4:
para.m_justify=MWAWParagraph::JustificationCenter;
break;
case 8:
para.m_justify=MWAWParagraph::JustificationFull;
break;
default:
f << "#align=" << (fl&0xf) << ",";
break;
}
fl &=0xFFF0; // find 60 or 70
if (fl) f << "flags=" << std::hex << fl << std::dec << ",";
para.m_marginsUnit = librevenge::RVNG_POINT;
for (int i=0; i<3; ++i) // left, right, indent
para.m_margins[i==2 ? 0 : i+1]=double(input->readLong(4))/65536.;
auto nTabs=static_cast<int>(input->readLong(2));
if ((inSection && (nTabs < 0 || nTabs>20)) ||
(!inSection && 19+nTabs*6!=fSz)) {
MWAW_DEBUG_MSG(("BeagleWksText::readParagraph: the number of tabs seems bad\n"));
f << "###numTabs=" << nTabs << ",";
nTabs=0;
}
for (int i=0; i<nTabs; ++i) {
MWAWTabStop tab;
tab.m_position=double(input->readLong(4))/65536./72;
auto val=static_cast<int>(input->readLong(1));
switch (val) {
case 1: // left
break;
case 2:
tab.m_alignment=MWAWTabStop::RIGHT;
break;
case 3:
tab.m_alignment=MWAWTabStop::CENTER;
break;
case 4:
tab.m_alignment=MWAWTabStop::DECIMAL;
break;
case 5:
tab.m_alignment=MWAWTabStop::BAR;
break;
default:
MWAW_DEBUG_MSG(("BeagleWksText::readParagraph: find unknown tab align=%d\n", val));
f << "tabs" << i << "[#align=" << tab.m_alignment << "],";
break;
}
auto leader=static_cast<unsigned char>(input->readULong(1));
if (leader) {
int unicode= m_parserState->m_fontConverter->unicode(3, leader);
if (unicode==-1)
tab.m_leaderCharacter =static_cast<unsigned short>(leader);
else
tab.m_leaderCharacter =static_cast<unsigned short>(unicode);
}
para.m_tabs->push_back(tab);
}
para.m_extra=f.str();
f.str("");
f << "Entries(Ruler):" << para;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
if (inSection)
return true;
// now the reverse header
if (static_cast<int>(input->readULong(1))!=fSz || input->readLong(2)!=0x100) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
input->seek(pos+4+fSz, librevenge::RVNG_SEEK_SET);
return true;
}
//////////////////////////////////////////////
// Section
//////////////////////////////////////////////
bool BeagleWksText::readSection(MWAWEntry const &entry, BeagleWksTextInternal::Section &sec)
{
sec=BeagleWksTextInternal::Section();
if (entry.length()<0xdc) {
MWAW_DEBUG_MSG(("BeagleWksText::readSection: the entry seems bad\n"));
return false;
}
MWAWInputStreamPtr &input= m_parserState->m_input;
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
long pos=entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
if (input->readULong(2)!=0xdc) {
MWAW_DEBUG_MSG(("BeagleWksText::readSection: the section header seems bad\n"));
ascFile.addPos(pos);
ascFile.addNote("Entries(Section):###");
return false;
}
sec.m_limitPos[0]=pos+0xdc;
for (int i=1; i < 5; ++i) {
sec.m_limitPos[i]=pos+long(input->readULong(2));
if (sec.m_limitPos[i]>entry.end()) {
MWAW_DEBUG_MSG(("BeagleWksText::readSection: some limits seem too big\n"));
f << "###limit-" << i << "=" << std::hex << sec.m_limitPos[i-1] << std::dec << ",";
sec.m_limitPos[i]=0;
}
if (sec.m_limitPos[i]<=sec.m_limitPos[i-1]) {
MWAW_DEBUG_MSG(("BeagleWksText::readSection: some limits seem incoherent\n"));
f << "###limit-" << i << "=" << std::hex << sec.m_limitPos[i-1] << "x"
<< sec.m_limitPos[i] << std::dec << ",";
}
}
auto nCols=static_cast<int>(input->readULong(1));
if (nCols<0 || nCols>16) {
MWAW_DEBUG_MSG(("BeagleWksText::readSection: the number of columns seems bad\n"));
f << "###nCols=" << nCols << ",";
nCols=1;
}
auto val=long(input->readULong(1)); // 0|1|6|1e
if (val) f << "f0=" << std::hex << val << std::dec << ",";
double colSep=double(input->readLong(4))/65536;
if (colSep<48 || colSep>48)
f << "colSep=" << colSep << ",";
if (nCols>1)
sec.setColumns(nCols, m_mainParser->getPageWidth()/double(nCols), librevenge::RVNG_INCH, colSep/72.);
for (int st=0; st<2; ++st) {
f << ((st==0) ? "header=[" : "footer=[");
sec.m_heights[st]=static_cast<int>(input->readLong(2));
val = input->readLong(2);
if (val) f << "fl=" << val << ",";
val = input->readLong(2); // right/left page ?
if (val!=sec.m_heights[st]) f << "dim2=" << val << ",";
f << "],";
}
sec.m_pageNumber=static_cast<int>(input->readLong(2));
unsigned long flags= input->readULong(4);
sec.m_hasFirstPage = (flags & 0x10000);
if (flags & 0x20000) f << "newPage,";
sec.m_hasHeader = (flags & 0x40000);
sec.m_hasFooter = (flags & 0x80000);
sec.m_usePageNumber = (flags & 0x100000);
if (flags & 0x400000)
sec.m_columnSeparator=MWAWBorder();
flags &= 0xFFA0FFFF;
if (val) f << "flags=" << std::hex << flags << std::dec << ",";
val=input->readLong(2);
if (val!=1) f << "page=" << val << ",";
val=input->readLong(2);
if (val) f << "yPos=" << val << ",";
sec.m_extra=f.str();
f.str("");
f << "Entries(Section):" << sec;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
pos=input->tell();
ascFile.addPos(pos);
ascFile.addNote("Section-II:");
input->seek(entry.begin()+81,librevenge::RVNG_SEEK_SET);
if (!readParagraph(sec.m_ruler, pos+0xda, true)) {
sec.m_ruler=MWAWParagraph();
MWAW_DEBUG_MSG(("BeagleWksText::readSection: can not read the section ruler\n"));
ascFile.addPos(pos+81);
ascFile.addNote("Section(Ruler):###");
}
input->seek(entry.begin()+0xda,librevenge::RVNG_SEEK_SET);
pos=input->tell();
f.str("");
f << "Section-III:";
val=long(input->readULong(2)); // find 3007, 4fef, 7006, fff9 ?
if (val) f << "f0=" << std::hex << val << std::dec << ",";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(entry.end(),librevenge::RVNG_SEEK_SET);
return true;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: