/* -*- 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 <sstream>
#include <librevenge/librevenge.h>
#include "MWAWTextListener.hxx"
#include "MWAWDebug.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWPageSpan.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPosition.hxx"
#include "MWAWRSRCParser.hxx"
#include "MWAWSection.hxx"
#include "MWAWSubDocument.hxx"
#include "NisusWrtParser.hxx"
#include "NisusWrtStruct.hxx"
#include "NisusWrtText.hxx"
/** Internal: the structures of a NisusWrtText */
namespace NisusWrtTextInternal
{
/** Internal: the fonts and many other data*/
struct Font {
//! the constructor
Font()
: m_font(-1,-1)
, m_pictureId(0)
, m_pictureWidth(0)
, m_markId(-1)
, m_variableId(0)
, m_format(0)
, m_format2(0)
, m_extra("")
{
}
bool isVariable() const
{
return (m_format2&0x20);
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Font const &font);
//! the font
MWAWFont m_font;
//! the picture id ( if this is for a picture )
int m_pictureId;
//! the picture width
int m_pictureWidth;
//! a mark id
int m_markId;
//! the variable id : in fact cst[unkn] + v_id
int m_variableId;
//! the main format ...
int m_format;
//! a series of flags
int m_format2;
//! two picture dim ( orig && file ?)
MWAWBox2i m_pictureDim[2];
//! extra data
std::string m_extra;
};
std::ostream &operator<<(std::ostream &o, Font const &font)
{
if (font.m_pictureId) o << "pictId=" << font.m_pictureId << ",";
if (font.m_pictureWidth) o << "pictW=" << font.m_pictureWidth << ",";
if (font.m_markId >= 0) o << "markId=" << font.m_markId << ",";
if (font.m_variableId > 0) o << "variableId=" << font.m_variableId << ",";
if (font.m_format2&0x4) o << "index,";
if (font.m_format2&0x8) o << "TOC,";
if (font.m_format2&0x10) o << "samePage,";
if (font.m_format2&0x20) o << "variable,";
if (font.m_format2&0x40) o << "hyphenate,";
if (font.m_format2&0x83)
o << "#format2=" << std::hex << (font.m_format2 &0x83) << std::dec << ",";
if (font.m_format & 1) o << "noSpell,";
if (font.m_format & 0x10) o << "sameLine,";
if (font.m_format & 0x40) o << "endOfPage,"; // checkme
if (font.m_format & 0xA6)
o << "#fl=" << std::hex << (font.m_format & 0xA6) << std::dec << ",";
if (font.m_pictureDim[0].size()[0] || font.m_pictureDim[0].size()[1])
o << "pictDim=" << font.m_pictureDim[0] << ",";
if (font.m_pictureDim[0] != font.m_pictureDim[1] &&
(font.m_pictureDim[1].size()[0] || font.m_pictureDim[1].size()[1]))
o << "pictDim[crop]=" << font.m_pictureDim[1] << ",";
if (font.m_extra.length())
o << font.m_extra << ",";
return o;
}
/** Internal: class to store the paragraph properties */
struct Paragraph final : public MWAWParagraph {
//! Constructor
Paragraph()
: MWAWParagraph()
, m_name("")
{
}
//! destructor
~Paragraph() final;
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind)
{
o << static_cast<MWAWParagraph const &>(ind);
if (ind.m_name.length()) o << "name=" << ind.m_name << ",";
return o;
}
//! the paragraph name
std::string m_name;
};
Paragraph::~Paragraph()
{
}
/** Internal structure: use to store a header */
struct HeaderFooter {
//! Constructor
HeaderFooter()
: m_type(MWAWHeaderFooter::HEADER)
, m_occurrence(MWAWHeaderFooter::NEVER)
, m_page(0)
, m_textParagraph(-1)
, m_unknown(0)
, m_parsed(false)
, m_extra("")
{
for (auto &id : m_paragraph) id = -1;
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, HeaderFooter const &hf);
//! the header type
MWAWHeaderFooter::Type m_type;
//! the header occurrence
MWAWHeaderFooter::Occurrence m_occurrence;
//! the page
int m_page;
//! the paragraph position in the header zone (first and last)
long m_paragraph[2];
//! the text position
long m_textParagraph;
//! a unknown value
int m_unknown;
//! a flag to know if the footnote is parsed
mutable bool m_parsed;
//! some extra debuging information
std::string m_extra;
};
std::ostream &operator<<(std::ostream &o, HeaderFooter const &hf)
{
if (hf.m_type==MWAWHeaderFooter::HEADER) o << "header,";
else o << "footer,";
switch (hf.m_occurrence) {
case MWAWHeaderFooter::NEVER:
o << "never,";
break;
case MWAWHeaderFooter::ODD:
o << "odd,";
break;
case MWAWHeaderFooter::EVEN:
o << "even,";
break;
case MWAWHeaderFooter::ALL:
o << "all,";
break;
#if !defined(__clang__)
default:
o << "#occurrence=" << int(hf.m_occurrence) << ",";
break;
#endif
}
o << "pos=" << hf.m_paragraph[0] << "<->" << hf.m_paragraph[1] << ",";
o << "pos[def]=" << hf.m_textParagraph << ",";
if (hf.m_unknown) o << "unkn=" << std::hex << hf.m_unknown << std::dec << ",";
o << hf.m_extra;
return o;
}
/** Internal structure: use to store a footnote */
struct Footnote {
//! Constructor
Footnote()
: m_number(0)
, m_textPosition()
, m_textLabel("")
, m_noteLabel("")
, m_parsed(false)
, m_extra("")
{
for (auto &id : m_paragraph) id = -1;
}
//! returns a label corresponding to a note ( or nothing if we can use numbering note)
std::string getTextLabel(int actId) const
{
if (m_textLabel.length() == 0 || m_textLabel=="1")
return std::string("");
std::stringstream s;
for (char c : m_textLabel) {
if (c=='1')
s << actId;
else
s << c;
}
return s.str();
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Footnote const &ft);
//! the note number
int m_number;
//! the paragraph position in the footnote zone (first and last)
int m_paragraph[2];
//! the text position
NisusWrtStruct::Position m_textPosition;
//! the label in the text
std::string m_textLabel;
//! the label in the note
std::string m_noteLabel;
//! a flag to know if the footnote is parsed
mutable bool m_parsed;
//! some extra debuging information
std::string m_extra;
};
std::ostream &operator<<(std::ostream &o, Footnote const &ft)
{
o << "pos=" << ft.m_textPosition << ",";
if (ft.m_paragraph[1] > ft.m_paragraph[0])
o << "paragraph[inNote]=" << ft.m_paragraph[0] << "<->" << ft.m_paragraph[1] << ",";
if (ft.m_number) o << "number=" << ft.m_number << ",";
if (ft.m_textLabel.length())
o << "textLabel=\"" << ft.m_textLabel << "\",";
if (ft.m_noteLabel.length())
o << "noteLabel=\"" << ft.m_noteLabel << "\",";
if (ft.m_extra.length())
o << ft.m_extra;
return o;
}
//! Internal: the picture data ( PICD )
struct PicturePara {
//! constructor
PicturePara()
: m_id(-1)
, m_paragraph(-1)
, m_position()
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, PicturePara const &pict);
//! the picture id
int m_id;
//! the paragraph position
int m_paragraph;
//! the position
MWAWBox2i m_position;
};
std::ostream &operator<<(std::ostream &o, PicturePara const &pict)
{
if (pict.m_id > 0) o << "pictId=" << pict.m_id << ",";
if (pict.m_paragraph >= 0) o << "paragraph=" << pict.m_paragraph << ",";
if (pict.m_position.size()[0] || pict.m_position.size()[1])
o << "pos=" << pict.m_position << ",";
return o;
}
/** different types
*
* - Format: font properties
* - Ruler: new ruler
*/
enum PLCType { P_Format=0, P_Ruler, P_Footnote, P_HeaderFooter, P_PicturePara, P_Unknown};
/** Internal: class to store the PLC: Pointer List Content ? */
struct DataPLC {
DataPLC()
: m_type(P_Format)
, m_id(-1)
, m_extra("")
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, DataPLC const &plc);
//! PLC type
PLCType m_type;
//! the id
int m_id;
//! an extra data to store message ( if needed )
std::string m_extra;
};
//! operator<< for DataPLC
std::ostream &operator<<(std::ostream &o, DataPLC const &plc)
{
switch (plc.m_type) {
case P_Format:
o << "F";
break;
case P_Ruler:
o << "R";
break;
case P_Footnote:
o << "Fn";
break;
case P_HeaderFooter:
o << "HF";
break;
case P_PicturePara:
o << "Pict";
break;
case P_Unknown:
#if !defined(__clang__)
default:
#endif
o << "#type=" << int(plc.m_type) << ",";
}
if (plc.m_id >= 0) o << plc.m_id << ",";
else o << "_";
if (plc.m_extra.length()) o << plc.m_extra;
return o;
}
//! internal structure used to store zone data
struct Zone {
typedef std::multimap<NisusWrtStruct::Position,DataPLC,NisusWrtStruct::Position::Compare> PLCMap;
//! constructor
Zone()
: m_entry()
, m_paragraphList()
, m_pictureParaList()
, m_plcMap()
{
}
//! the position of text in the rsrc file
MWAWEntry m_entry;
//! the list of paragraph
std::vector<Paragraph> m_paragraphList;
//! the list of paragraph
std::vector<PicturePara> m_pictureParaList;
//! the map pos -> format id
PLCMap m_plcMap;
};
////////////////////////////////////////
//! Internal: the state of a NisusWrtText
struct State {
//! constructor
State()
: m_version(-1)
, m_fontList()
, m_footnoteList()
, m_numPages(-1)
, m_actualPage(0)
, m_hfList()
, m_headersId()
, m_footersId()
{
}
//! the file version
mutable int m_version;
/** the font list */
std::vector<Font> m_fontList;
/** the list of footnote */
std::vector<Footnote> m_footnoteList;
/** the main zones : Main, Footnote, HeaderFooter */
Zone m_zones[3];
int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
/** the list of header footer */
std::vector<HeaderFooter> m_hfList;
/** the list of header id which corresponds to each page */
std::vector<int> m_headersId;
/** the list of footer id which corresponds to each page */
std::vector<int> m_footersId;
};
////////////////////////////////////////
//! Internal: the subdocument of a NisusWrtText
class SubDocument final : public MWAWSubDocument
{
public:
SubDocument(NisusWrtText &pars, MWAWInputStreamPtr const &input, int id, libmwaw::SubDocumentType type)
: MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
, m_textParser(&pars)
, m_id(id)
, m_type(type)
{
}
//! 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 */
NisusWrtText *m_textParser;
//! the subdocument id
int m_id;
//! the subdocument type
libmwaw::SubDocumentType m_type;
private:
SubDocument(SubDocument const &orig) = delete;
SubDocument &operator=(SubDocument const &orig) = delete;
};
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
{
if (!listener.get()) {
MWAW_DEBUG_MSG(("NisusWrtTextInternal::SubDocument::parse: no listener\n"));
return;
}
if (!m_textParser) {
MWAW_DEBUG_MSG(("NisusWrtTextInternal::SubDocument::parse: no parser\n"));
return;
}
long pos = m_input->tell();
if (m_type == libmwaw::DOC_NOTE)
m_textParser->sendFootnote(m_id);
else if (m_type == libmwaw::DOC_HEADER_FOOTER)
m_textParser->sendHeaderFooter(m_id);
else {
MWAW_DEBUG_MSG(("NisusWrtTextInternal::SubDocument::parse: oops do not know how to send this kind of document\n"));
return;
}
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
}
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_id != sDoc->m_id) return true;
if (m_type != sDoc->m_type) return true;
return false;
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
NisusWrtText::NisusWrtText(NisusWrtParser &parser)
: m_parserState(parser.getParserState())
, m_state(new NisusWrtTextInternal::State)
, m_mainParser(&parser)
{
}
NisusWrtText::~NisusWrtText()
{
}
int NisusWrtText::version() const
{
if (m_state->m_version < 0)
m_state->m_version = m_parserState->m_version;
return m_state->m_version;
}
int NisusWrtText::numPages() const
{
if (m_state->m_numPages >= 0)
return m_state->m_numPages;
const_cast<NisusWrtText *>(this)->computePositions();
return m_state->m_numPages;
}
std::shared_ptr<MWAWSubDocument> NisusWrtText::getHeader(int page, int &numSimilar)
{
numSimilar=1;
std::shared_ptr<MWAWSubDocument> res;
auto numHeaders = int(m_state->m_headersId.size());
if (page < 1 || page-1 >= numHeaders) {
if (m_state->m_numPages>page)
numSimilar=m_state->m_numPages-page+1;
return res;
}
int hId = m_state->m_headersId[size_t(page-1)];
if (hId >= 0)
res.reset(new NisusWrtTextInternal::SubDocument(*this, m_mainParser->rsrcInput(), hId, libmwaw::DOC_HEADER_FOOTER));
while (page < numHeaders && m_state->m_headersId[size_t(page)]==hId) {
page++;
numSimilar++;
}
return res;
}
std::shared_ptr<MWAWSubDocument> NisusWrtText::getFooter(int page, int &numSimilar)
{
numSimilar=1;
std::shared_ptr<MWAWSubDocument> res;
auto numFooters = int(m_state->m_footersId.size());
if (page < 1 || page-1 >= numFooters) {
if (m_state->m_numPages>page)
numSimilar=m_state->m_numPages-page+1;
return res;
}
int fId = m_state->m_footersId[size_t(page-1)];
if (fId >= 0)
res.reset(new NisusWrtTextInternal::SubDocument(*this, m_mainParser->rsrcInput(), fId, libmwaw::DOC_HEADER_FOOTER));
while (page < numFooters && m_state->m_footersId[size_t(page)]==fId) {
page++;
numSimilar++;
}
return res;
}
void NisusWrtText::computePositions()
{
// first compute the number of page and the number of paragraph by pages
int nPages = 1;
MWAWInputStreamPtr input = m_mainParser->getInput();
input->seek(0, librevenge::RVNG_SEEK_SET);
int paragraph=0;
std::vector<int> firstParagraphInPage;
firstParagraphInPage.push_back(0);
while (!input->isEnd()) {
auto c = char(input->readULong(1));
if (c==0xd)
paragraph++;
else if (c==0xc) {
nPages++;
firstParagraphInPage.push_back(paragraph);
}
}
m_state->m_actualPage = 1;
m_state->m_numPages = nPages;
// update the main page zone
m_state->m_zones[NisusWrtStruct::Z_Main].m_entry.setBegin(0);
m_state->m_zones[NisusWrtStruct::Z_Main].m_entry.setEnd(input->tell());
m_state->m_zones[NisusWrtStruct::Z_Main].m_entry.setId(NisusWrtStruct::Z_Main);
// compute the header/footer pages
int actPage = 1;
MWAWVec2i headerId(-1,-1), footerId(-1,-1);
m_state->m_headersId.resize(size_t(nPages), -1);
m_state->m_footersId.resize(size_t(nPages), -1);
for (size_t i = 0; i < m_state->m_hfList.size(); i++) {
auto &hf = m_state->m_hfList[i];
int page = 1;
long textPos = hf.m_textParagraph;
if (hf.m_type == MWAWHeaderFooter::FOOTER && textPos) textPos--;
for (size_t j = 0; j < firstParagraphInPage.size(); j++) {
if (textPos < firstParagraphInPage[j])
break;
page = int(j)+1;
}
//std::cerr << "find : page=" << page << " for hf" << int(i) << "\n";
for (int p = actPage; p < page; p++) {
m_state->m_headersId[size_t(p)-1] = (p%2) ? headerId[0] : headerId[1];
m_state->m_footersId[size_t(p)-1] = (p%2) ? footerId[0] : footerId[1];
}
actPage = hf.m_page = page;
MWAWVec2i &wh = hf.m_type == MWAWHeaderFooter::HEADER ? headerId : footerId;
switch (hf.m_occurrence) {
case MWAWHeaderFooter::ODD:
wh[0] = int(i);
break;
case MWAWHeaderFooter::EVEN:
wh[1] = int(i);
break;
case MWAWHeaderFooter::ALL:
wh[0] = wh[1] = int(i);
break;
case MWAWHeaderFooter::NEVER:
wh[0] = wh[1] = -1;
break;
#if !defined(__clang__)
default:
break;
#endif
}
}
for (int p = actPage; p <= nPages; p++) {
m_state->m_headersId[size_t(p)-1] = (p%2) ? headerId[0] : headerId[1];
m_state->m_footersId[size_t(p)-1] = (p%2) ? footerId[0] : footerId[1];
}
}
////////////////////////////////////////////////////////////
// Intermediate level
////////////////////////////////////////////////////////////
// find the different zones
bool NisusWrtText::createZones()
{
if (!m_mainParser->getRSRCParser()) {
MWAW_DEBUG_MSG(("NisusWrtText::createZones: can not find the entry map\n"));
return false;
}
auto &entryMap = m_mainParser->getRSRCParser()->getEntriesMap();
// footnote and headerFooter main zone
auto it = entryMap.lower_bound("HF ");
while (it != entryMap.end()) {
if (it->first != "HF ")
break;
MWAWEntry &entry = it++->second;
readHeaderFooter(entry);
}
it = entryMap.lower_bound("FOOT");
while (it != entryMap.end()) {
if (it->first != "FOOT")
break;
MWAWEntry &entry = it++->second;
readFootnotes(entry);
}
// fonts
it = entryMap.lower_bound("FLST");
while (it != entryMap.end()) {
if (it->first != "FLST")
break;
MWAWEntry &entry = it++->second;
readFontsList(entry);
}
it = entryMap.lower_bound("STYL");
while (it != entryMap.end()) {
if (it->first != "STYL")
break;
MWAWEntry &entry = it++->second;
readFonts(entry);
}
it = entryMap.lower_bound("FTAB");
while (it != entryMap.end()) {
if (it->first != "FTAB")
break;
MWAWEntry &entry = it++->second;
readFonts(entry);
}
// the font change
char const *posFontNames[] = { "FRMT", "FFRM", "HFRM" };
for (int z = 0; z < 3; z++) {
it = entryMap.lower_bound(posFontNames[z]);
while (it != entryMap.end()) {
if (it->first != posFontNames[z])
break;
MWAWEntry &entry = it++->second;
readPosToFont(entry, NisusWrtStruct::ZoneType(z));
}
}
// the paragraph: in mainZone 1004 means style paragraph
char const *paragNames[] = { "RULE", "FRUL", "HRUL" };
for (int z = 0; z < 3; z++) {
it = entryMap.lower_bound(paragNames[z]);
while (it != entryMap.end()) {
if (it->first != paragNames[z])
break;
MWAWEntry &entry = it++->second;
readParagraphs(entry, NisusWrtStruct::ZoneType(z));
}
}
// the picture associated to the paragraph
char const *pictDNames[] = { "PICD", "FPIC", "HPIC" };
for (int z = 0; z < 3; z++) {
it = entryMap.lower_bound(pictDNames[z]);
while (it != entryMap.end()) {
if (it->first != pictDNames[z])
break;
MWAWEntry &entry = it++->second;
readPICD(entry, NisusWrtStruct::ZoneType(z));
}
}
// End of the style zone
// style name ( can also contains some flags... )
it = entryMap.lower_bound("STNM");
while (it != entryMap.end()) {
if (it->first != "STNM")
break;
MWAWEntry &entry = it++->second;
std::vector<std::string> list;
m_mainParser->readStringsList(entry, list, false);
}
// style link to paragraph name
it = entryMap.lower_bound("STRL");
while (it != entryMap.end()) {
if (it->first != "STRL")
break;
MWAWEntry &entry = it++->second;
std::vector<std::string> list;
m_mainParser->readStringsList(entry, list, false);
}
// style next name
it = entryMap.lower_bound("STNX");
while (it != entryMap.end()) {
if (it->first != "STNX")
break;
MWAWEntry &entry = it++->second;
std::vector<std::string> list;
m_mainParser->readStringsList(entry, list, false);
}
// the text zone
char const *textNames[] = { "", "FNTX", "HFTX" };
for (int z = 1; z < 3; z++) {
it = entryMap.lower_bound(textNames[z]);
while (it != entryMap.end()) {
if (it->first != textNames[z])
break;
m_state->m_zones[z].m_entry = it++->second;
m_state->m_zones[z].m_entry.setId(z);
}
}
// now update the different data
computePositions();
return true;
}
////////////////////////////////////////////////////////////
// Fonts
////////////////////////////////////////////////////////////
// read a list of fonts
bool NisusWrtText::readFontsList(MWAWEntry const &entry)
{
if (!entry.valid() && entry.length()!=0) {
MWAW_DEBUG_MSG(("NisusWrtText::readFontsList: the entry is bad\n"));
return false;
}
entry.setParsed(true);
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(FontNames)[" << entry.id() << "]:";
asciiFile.addPos(pos-4);
asciiFile.addNote(f.str().c_str());
int num=0;
while (!input->isEnd()) {
pos = input->tell();
if (pos == entry.end()) break;
if (pos+4 > entry.end()) {
asciiFile.addPos(pos);
asciiFile.addNote("FontNames###");
MWAW_DEBUG_MSG(("NisusWrtText::readFontsList: can not read flst\n"));
return false;
}
auto fId = static_cast<int>(input->readULong(2));
f.str("");
f << "FontNames" << num++ << ":fId=" << std::hex << fId << std::dec << ",";
auto pSz = static_cast<int>(input->readULong(1));
if (pSz+1+pos+2 > entry.end()) {
f << "###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
MWAW_DEBUG_MSG(("NisusWrtText::readFontsList: can not read pSize\n"));
return false;
}
std::string name("");
for (int c=0; c < pSz; c++)
name += char(input->readULong(1));
m_parserState->m_fontConverter->setCorrespondance(fId, name);
f << name;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if ((pSz%2)==0) input->seek(1,librevenge::RVNG_SEEK_CUR);
}
return true;
}
// read the FTAB/STYL resource: a font format ?
bool NisusWrtText::readFonts(MWAWEntry const &entry)
{
bool isStyle = entry.type()=="STYL";
int const fSize = isStyle ? 58 : 98;
std::string name(isStyle ? "Style" : "Fonts");
if ((!entry.valid()&&entry.length()) || (entry.length()%fSize)) {
MWAW_DEBUG_MSG(("NisusWrtText::readFonts: the entry is bad\n"));
return false;
}
entry.setParsed(true);
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto numElt = int(entry.length()/fSize);
libmwaw::DebugStream f;
f << "Entries(" << name << ")[" << entry.id() << "]:N=" << numElt;
asciiFile.addPos(pos-4);
asciiFile.addNote(f.str().c_str());
long val;
for (int i = 0; i < numElt; i++) {
NisusWrtTextInternal::Font font;
pos = input->tell();
f.str("");
if (!isStyle)
font.m_pictureId = static_cast<int>(input->readLong(2));
if (font.m_pictureId) {
// the two value seems to differ slightly for a picture
val = long(input->readULong(2));
if (val != 0xFF01) f << "#pictFlags0=" << std::hex << val << ",";
font.m_pictureWidth = static_cast<int>(input->readLong(2));
}
else {
val = long(input->readULong(2));
if (val != 0xFF00)
font.m_font.setId(int(val));
val = long(input->readULong(2));
if (val != 0xFF00)
font.m_font.setSize(float(val));
}
uint32_t flags=0;
auto flag = static_cast<int>(input->readULong(2));
if (flag&0x1) flags |= MWAWFont::boldBit;
if (flag&0x2) flags |= MWAWFont::italicBit;
if (flag&0x4) font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
if (flag&0x8) flags |= MWAWFont::embossBit;
if (flag&0x10) flags |= MWAWFont::shadowBit;
if (flag&0x20) font.m_font.setDeltaLetterSpacing(-1);
if (flag&0x40) font.m_font.setDeltaLetterSpacing(1);
if (flag &0xFF80)
f << "#flags0=" << std::hex << (flag &0xFF80) << std::dec << ",";
flag = static_cast<int>(input->readULong(2));
if (flag & 1) {
font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
f << "underline[lower],";
}
if (flag & 2) font.m_font.setUnderlineStyle(MWAWFont::Line::Dot);
if (flag & 4) font.m_font.setUnderlineWordFlag(true);
if (flag & 0x8) font.m_font.set(MWAWFont::Script::super());
if (flag & 0x10) font.m_font.set(MWAWFont::Script::sub());
if (flag & 0x20) font.m_font.setStrikeOutStyle(MWAWFont::Line::Simple);
if (flag & 0x40) font.m_font.setOverlineStyle(MWAWFont::Line::Simple);
if (flag & 0x80) flags |= MWAWFont::smallCapsBit;
if (flag & 0x100) flags |= MWAWFont::uppercaseBit;
if (flag & 0x200) flags |= MWAWFont::boxedBit;
if (flag & 0x400) flags |= MWAWFont::hiddenBit;
if (flag & 0x1000) font.m_font.set(MWAWFont::Script(40,librevenge::RVNG_PERCENT,58));
if (flag & 0x2000) font.m_font.set(MWAWFont::Script(-40,librevenge::RVNG_PERCENT,58));
if (flag & 0x4000) flags |= MWAWFont::reverseVideoBit;
if (flag & 0x8800)
f << "#flags1=" << std::hex << (flag & 0x8800) << std::dec << ",";
val = input->readLong(2);
if (val) f << "#f0=" << std::hex << val << ",";
font.m_format = static_cast<int>(input->readULong(1));
if (font.m_format & 8) {
font.m_format &= 0xF7;
flags |= MWAWFont::reverseWritingBit;
}
font.m_format2 = static_cast<int>(input->readULong(1));
font.m_font.setFlags(flags);
int color = 0;
// now data differs
if (isStyle) {
val = static_cast<int>(input->readULong(2)); // find [0-3] here
if (val) f << "unkn0=" << val << ",";
for (int j = 0; j < 6; j++) { // find s0=67, s1=a728
val = static_cast<int>(input->readULong(2));
if (val) f << "f" << j << "=" << std::hex << val << std::dec << ",";
}
color = static_cast<int>(input->readULong(2));
}
else {
color = static_cast<int>(input->readULong(2));
for (int j = 1; j < 6; j++) { // find always 0 here...
val = static_cast<int>(input->readULong(2));
if (val) f << "#f" << j << "=" << val << ",";
}
bool hasMark = false;
val = static_cast<int>(input->readULong(2));
if (val == 1) hasMark = true;
else if (val) f << "#hasMark=" << val << ",";
val = static_cast<int>(input->readULong(2));
if (hasMark) font.m_markId = int(val);
else if (val) f << "#markId=" << val << ",";
// f0=0|1 and if f0=1 then f1 is a small number between 1 and 20
for (int j = 0; j < 18; j++) {
val = static_cast<int>(input->readULong(2));
if (val) f << "g" << j << "=" << val << ",";
}
float expand=float(input->readLong(4))/65536.f;
if (expand < 0 || expand > 0)
font.m_font.setDeltaLetterSpacing(expand);
// 0, -1 or a small number related to the variable id : probably unknowncst+vId
font.m_variableId = static_cast<int>(input->readLong(4));
// the remaining seems to be 0 excepted for picture
for (int j = 0; j < 4; j++) { // find 0,0,[0|d5|f5|f8|ba], big number
val = static_cast<int>(input->readULong(2));
if (val) f << "h" << j << "=" << std::hex << val << std::dec << ",";
}
// two dim ?
for (auto &pictDim : font.m_pictureDim) {
int dim[4];
for (auto &d : dim) d = static_cast<int>(input->readLong(2));
pictDim=MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2]));
}
}
static const uint32_t colors[] =
{ 0, 0xFF0000, 0x00FF00, 0x0000FF, 0x00FFFF, 0xFF00FF, 0xFFFF00, 0xFFFFFF };
if (color >= 0 && color < 8)
font.m_font.setColor(MWAWColor(colors[color]));
else if (color != 0xFF00)
f << "#color=" << color << ",";
font.m_extra = f.str();
if (!isStyle)
m_state->m_fontList.push_back(font);
f.str("");
f << name << i << ":" << font.m_font.getDebugString(m_parserState->m_fontConverter)
<< font;
if (input->tell() != pos+fSize)
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+fSize, librevenge::RVNG_SEEK_SET);
}
return true;
}
// read the FRMT resource: filepos -> fonts
bool NisusWrtText::readPosToFont(MWAWEntry const &entry, NisusWrtStruct::ZoneType zoneId)
{
if (!entry.valid() || (entry.length()%10)) {
MWAW_DEBUG_MSG(("NisusWrtText::readPosToFont: the entry is bad\n"));
return false;
}
if (zoneId >= 3) {
MWAW_DEBUG_MSG(("NisusWrtText::readPosToFont: find unexpected zoneId: %d\n", zoneId));
return false;
}
NisusWrtTextInternal::Zone &zone = m_state->m_zones[zoneId];
entry.setParsed(true);
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto numElt = int(entry.length()/10);
libmwaw::DebugStream f;
f << "Entries(PosToFont)[" << zoneId << "]:N=" << numElt;
asciiFile.addPos(pos-4);
asciiFile.addNote(f.str().c_str());
NisusWrtStruct::Position position;
NisusWrtTextInternal::DataPLC plc;
plc.m_type = NisusWrtTextInternal::P_Format;
for (int i = 0; i < numElt; i++) {
pos = input->tell();
f.str("");
f << "PosToFont" << i << "[" << zoneId << "]:";
position.m_paragraph = static_cast<int>(input->readULong(4)); // checkme or ??? m_paragraph
position.m_word = static_cast<int>(input->readULong(2));
position.m_char = static_cast<int>(input->readULong(2));
f << "pos=" << position << ",";
auto id = static_cast<int>(input->readLong(2));
f << "F" << id << ",";
plc.m_id = id;
zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(position, plc));
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+10, librevenge::RVNG_SEEK_SET);
}
return true;
}
////////////////////////////////////////////////////////////
// the paragraphs
////////////////////////////////////////////////////////////
// send the paragraph to the listener
void NisusWrtText::setProperty(NisusWrtTextInternal::Paragraph const ¶, int width)
{
if (!m_parserState->m_textListener) return;
double origRMargin = para.m_margins[2].get();
double rMargin=double(width)/72.-origRMargin;
if (rMargin < 0.0) rMargin = 0;
const_cast<NisusWrtTextInternal::Paragraph &>(para).m_margins[2] = rMargin;
m_parserState->m_textListener->setParagraph(para);
const_cast<NisusWrtTextInternal::Paragraph &>(para).m_margins[2] = origRMargin;
}
// read the RULE resource: a list of rulers
bool NisusWrtText::readParagraphs(MWAWEntry const &entry, NisusWrtStruct::ZoneType zoneId)
{
if (!entry.valid() && entry.length() != 0) {
MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: the entry is bad\n"));
return false;
}
if (zoneId >= 3) {
MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: find unexpected zoneId: %d\n", zoneId));
return false;
}
auto &zone = m_state->m_zones[zoneId];
entry.setParsed(true);
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto numElt = int(entry.length()/98);
libmwaw::DebugStream f, f2;
f << "Entries(RULE)[" << entry.type() << entry.id() << "]";
if (entry.id()==1004) f << "[Styl]";
else if (entry.id() != 1003) {
MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: find unexpected entryId: %d\n", entry.id()));
f << "###";
}
f << ":N=" << numElt;
asciiFile.addPos(pos-4);
asciiFile.addNote(f.str().c_str());
NisusWrtTextInternal::DataPLC plc;
plc.m_type = NisusWrtTextInternal::P_Ruler;
long val;
while (input->tell() != entry.end()) {
int num = (entry.id() == 1003) ? static_cast<int>(zone.m_paragraphList.size()) : -1;
pos = input->tell();
f.str("");
if (pos+8 > entry.end() || input->isEnd()) {
f << "RULE" << num << "[" << zoneId << "]:###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: can not read end of zone\n"));
return false;
}
auto nPara = long(input->readULong(4));
if (nPara == 0x7FFFFFFF) {
input->seek(-4, librevenge::RVNG_SEEK_CUR);
break;
}
NisusWrtStruct::Position textPosition;
textPosition.m_paragraph = static_cast<int>(nPara); // checkme or ???? + para
auto sz = long(input->readULong(4));
if (sz < 0x42 || pos+sz > entry.end()) {
f << "RULE" << num << "[" << zoneId << "]:###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: can not read the size zone\n"));
return false;
}
NisusWrtTextInternal::Paragraph para;
para.setInterline(double(input->readLong(4))/65536., librevenge::RVNG_POINT, MWAWParagraph::AtLeast);
para.m_spacings[1] = double(input->readLong(4))/65536./72.;
auto wh = int(input->readLong(2));
switch (wh) {
case 0:
break; // left
case 1:
para.m_justify = MWAWParagraph::JustificationCenter;
break;
case 2:
para.m_justify = MWAWParagraph::JustificationRight;
break;
case 3:
para.m_justify = MWAWParagraph::JustificationFull;
break;
default:
f << "#align=" << wh << ",";
break;
}
val = input->readLong(2);
if (val) f << "#f0=" << val << ",";
para.m_marginsUnit = librevenge::RVNG_INCH;
para.m_margins[0] = double(input->readLong(4))/65536./72.;
para.m_margins[1] = double(input->readLong(4))/65536./72.;
para.m_margins[2] = double(input->readLong(4))/65536./72.;
para.m_margins[0] =para.m_margins[0].get()-para.m_margins[1].get();
wh = int(input->readULong(1));
switch (wh) {
case 0: // at least
break;
case 1:
para.m_spacingsInterlineType = MWAWParagraph::Fixed;
break;
case 2:
para.m_spacingsInterlineUnit = librevenge::RVNG_PERCENT;
para.m_spacingsInterlineType = MWAWParagraph::Fixed;
// before spacing is also in %, try to correct it
para.m_spacings[1] = para.m_spacings[1].get()*12.0;
break;
default: // unknown, so...
f << "#interline=" << (val&0xFC) << ",";
para.setInterline(1.0, librevenge::RVNG_PERCENT);
break;
}
val = input->readLong(1);
if (val) f << "#f1=" << val << ",";
for (int i = 0; i < 14; i++) {
val = input->readLong(2);
if (val) f << "g" << i << "=" << val << ",";
}
input->seek(pos+0x3E, librevenge::RVNG_SEEK_SET);
long numTabs = input->readLong(2);
bool ok = true;
if (0x40+8*numTabs+2 > sz) {
f << "###";
MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: can not read the string\n"));
ok = false;
numTabs = 0;
}
for (int i = 0; i < numTabs; i++) {
long tabPos = input->tell();
MWAWTabStop tab;
f2.str("");
tab.m_position = double(input->readLong(4))/72./65536.; // from left pos
val = long(input->readULong(1));
switch (val) {
case 1:
break;
case 2:
tab.m_alignment = MWAWTabStop::CENTER;
break;
case 3:
tab.m_alignment = MWAWTabStop::RIGHT;
break;
case 4:
tab.m_alignment = MWAWTabStop::DECIMAL;
break;
case 6: // a little old, look simillar to full justification
f2 << "justify,";
break;
default:
f2 << "#type=" << val << ",";
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);
}
val = long(input->readLong(2)); // unused ?
if (val) f2 << "#unkn0=" << val << ",";
para.m_tabs->push_back(tab);
if (f2.str().length())
f << "tab" << i << "=[" << f2.str() << "],";
input->seek(tabPos+8, librevenge::RVNG_SEEK_SET);
}
// ruler name
long pSz = ok ? long(input->readULong(1)) : 0;
if (pSz) {
if (input->tell()+pSz != pos+sz && input->tell()+pSz+1 != pos+sz) {
f << "name###";
MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: can not read the ruler name\n"));
asciiFile.addDelimiter(input->tell()-1,'#');
}
else {
std::string str("");
for (int i = 0; i < pSz; i++)
str += char(input->readULong(1));
para.m_name = str;
}
}
plc.m_id = num;
para.m_extra=f.str();
if (entry.id() == 1003) {
zone.m_paragraphList.push_back(para);
zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(textPosition, plc));
}
f.str("");
f << "RULE" << num << "[" << zoneId << "]:";
f << "paragraph=" << nPara << "," << para;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+sz, librevenge::RVNG_SEEK_SET);
}
pos = input->tell();
f.str("");
f << "RULE[" << zoneId << "](II):";
if (pos+66 != entry.end() || input->readULong(4) != 0x7FFFFFFF) {
f << "###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: find odd end\n"));
return true;
}
for (int i = 0; i < 31; i++) { // only find 0 expected f12=0|100
val = long(input->readLong(2));
if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// the zones header
////////////////////////////////////////////////////////////
// read the header/footer main entry
bool NisusWrtText::readHeaderFooter(MWAWEntry const &entry)
{
if (!entry.valid() || (entry.length()%32)) {
MWAW_DEBUG_MSG(("NisusWrtText::readHeaderFooter: the entry is bad\n"));
return false;
}
auto &zone = m_state->m_zones[NisusWrtStruct::Z_HeaderFooter];
entry.setParsed(true);
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto numElt = int(entry.length()/32);
libmwaw::DebugStream f;
f << "Entries(HeaderFooter)[" << entry.id() << "]:N=" << numElt;
asciiFile.addPos(pos-4);
asciiFile.addNote(f.str().c_str());
NisusWrtTextInternal::DataPLC plc;
plc.m_type = NisusWrtTextInternal::P_HeaderFooter;
long val;
long lastPara = 0;
for (int i = 0; i < numElt; i++) {
pos = input->tell();
f.str("");
NisusWrtTextInternal::HeaderFooter hf;
hf.m_textParagraph = input->readLong(4);
hf.m_paragraph[0] = lastPara;
hf.m_paragraph[1] = lastPara = input->readLong(4);
auto what = static_cast<int>(input->readULong(2));
switch ((what>>2)&0x3) {
case 1:
hf.m_type = MWAWHeaderFooter::HEADER;
break;
case 2:
hf.m_type = MWAWHeaderFooter::FOOTER;
break;
default:
f << "#what=" << ((what>>2)&0x3);
break;
}
switch (what&0x3) {
case 1:
hf.m_occurrence = MWAWHeaderFooter::ODD;
break;
case 2:
hf.m_occurrence = MWAWHeaderFooter::EVEN;
break;
case 3:
hf.m_occurrence = MWAWHeaderFooter::ALL;
break;
default:
f << "[#page],";
break;
}
if (what&0xFFF0) f << "#flags=" << std::hex << (what&0xFFF0) << ",";
// find 0|0x10|0x18|0x20|0x36|0x3a|0x40|0x4c|0x66 here
hf.m_unknown = static_cast<int>(input->readLong(2));
for (int j = 0; j < 10; j++) { // always 0 ?
val = long(input->readLong(2));
if (val) f << "g" << j << "=" << val << ",";
}
hf.m_extra = f.str();
f.str("");
f << "HeaderFooter" << i << ":" << hf;
m_state->m_hfList.push_back(hf);
plc.m_id = i+1;
NisusWrtStruct::Position hfPosition;
hfPosition.m_paragraph=int(lastPara);
zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(hfPosition, plc));
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+32, librevenge::RVNG_SEEK_SET);
}
return true;
}
// read the footnote main entry
bool NisusWrtText::readFootnotes(MWAWEntry const &entry)
{
if (!entry.valid() || (entry.length()%36)) {
MWAW_DEBUG_MSG(("NisusWrtText::readFootnotes: the entry is bad\n"));
return false;
}
auto &mainZone = m_state->m_zones[NisusWrtStruct::Z_Main];
auto &zone = m_state->m_zones[NisusWrtStruct::Z_Footnote];
entry.setParsed(true);
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto numElt = int(entry.length()/36);
libmwaw::DebugStream f;
f << "Entries(Footnotes)[" << entry.id() << "]:N=" << numElt;
asciiFile.addPos(pos-4);
asciiFile.addNote(f.str().c_str());
NisusWrtTextInternal::DataPLC plc;
plc.m_type = NisusWrtTextInternal::P_Footnote;
int lastParagraph = 0;
long val;
for (int i = 0; i < numElt; i++) {
pos = input->tell();
f.str("");
NisusWrtTextInternal::Footnote footnote;
footnote.m_textPosition.m_paragraph = static_cast<int>(input->readULong(4)); // checkme or ??? m_paragraph
footnote.m_textPosition.m_word = static_cast<int>(input->readULong(2));
footnote.m_textPosition.m_char = static_cast<int>(input->readULong(2));
footnote.m_paragraph[0] = lastParagraph;
lastParagraph = static_cast<int>(input->readULong(4)); // checkme or ??? m_paragraph
footnote.m_paragraph[1] = lastParagraph;
// find f0=0|55|6f, f1=15|16|26|27|39|3a
for (int j = 0; j < 2; j++) {
val = input->readLong(2);
if (val) f << "f" << j << "=" << std::hex << val << std::dec << ",";
}
footnote.m_number = static_cast<int>(input->readLong(2));
for (int j = 0; j < 3; j++) { // always 0 ?
val = input->readLong(2);
if (val) f << "g" << j << "=" << val << ",";
}
for (int wh = 0; wh < 2; wh++) {
input->seek(pos+24+wh*6, librevenge::RVNG_SEEK_SET);
std::string label("");
for (int c = 0; c < 6; c++) {
auto ch = char(input->readULong(1));
if (ch == 0)
break;
label += ch;
}
if (wh==0) footnote.m_noteLabel = label;
else footnote.m_textLabel = label;
}
footnote.m_extra = f.str();
f.str("");
f << "Footnotes" << i << ":" << footnote;
m_state->m_footnoteList.push_back(footnote);
plc.m_id = i;
mainZone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(footnote.m_textPosition, plc));
NisusWrtStruct::Position notePosition;
notePosition.m_paragraph= footnote.m_paragraph[0];
zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(notePosition, plc));
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+36, librevenge::RVNG_SEEK_SET);
}
return true;
}
// read the PICD zone ( a list of picture ? )
bool NisusWrtText::readPICD(MWAWEntry const &entry, NisusWrtStruct::ZoneType zoneId)
{
if ((!entry.valid()&&entry.length()) || (entry.length()%14)) {
MWAW_DEBUG_MSG(("NisusWrtText::readPICD: the entry is bad\n"));
return false;
}
if (zoneId >= 3) {
MWAW_DEBUG_MSG(("NisusWrtText::readPICD: find unexpected zoneId: %d\n", zoneId));
return false;
}
auto &zone = m_state->m_zones[zoneId];
entry.setParsed(true);
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto numElt = int(entry.length()/14);
libmwaw::DebugStream f;
f << "Entries(PICD)[" << zoneId << "]:N=" << numElt;
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
NisusWrtTextInternal::DataPLC plc;
plc.m_type = NisusWrtTextInternal::P_PicturePara;
for (int i = 0; i < numElt; i++) {
pos = input->tell();
f.str("");
NisusWrtTextInternal::PicturePara pict;
pict.m_paragraph = static_cast<int>(input->readLong(4));
int dim[4];
for (int &j : dim)
j = static_cast<int>(input->readLong(2));
pict.m_position = MWAWBox2i(MWAWVec2i(dim[1],dim[0]), MWAWVec2i(dim[3],dim[2]));
pict.m_id = static_cast<int>(input->readULong(2));
zone.m_pictureParaList.push_back(pict);
NisusWrtStruct::Position pictPosition;
pictPosition.m_paragraph= pict.m_paragraph;
plc.m_id = i;
zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(pictPosition, plc));
f << "PICD" << i << ":" << pict;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+14, librevenge::RVNG_SEEK_SET);
}
return true;
}
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
long NisusWrtText::findFilePos(NisusWrtStruct::ZoneType zoneId, NisusWrtStruct::Position const &pos)
{
if (zoneId >= 3) {
MWAW_DEBUG_MSG(("NisusWrtText::findFilePos: find unexpected zoneId: %d\n", zoneId));
return -1;
}
auto &zone = m_state->m_zones[zoneId];
MWAWEntry entry = zone.m_entry;
if (!entry.valid()) {
MWAW_DEBUG_MSG(("NisusWrtText::findFilePos: the entry is bad\n"));
return -1;
}
bool isMain = zoneId == NisusWrtStruct::Z_Main;
MWAWInputStreamPtr input = isMain ?
m_mainParser->getInput() : m_mainParser->rsrcInput();
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
NisusWrtStruct::Position actPos;
for (int i = 0; i < entry.length(); i++) {
if (input->isEnd())
break;
if (pos == actPos)
return input->tell();
auto c = static_cast<unsigned char>(input->readULong(1));
// update the position
switch (c) {
case 0xd:
actPos.m_paragraph++;
actPos.m_word = actPos.m_char = 0;
break;
case '\t':
case ' ':
actPos.m_word++;
actPos.m_char = 0;
break;
default:
actPos.m_char++;
break;
}
}
if (pos == actPos)
return input->tell();
MWAW_DEBUG_MSG(("NisusWrtText::findFilePos: can not find the position\n"));
return -1;
}
////////////////////////////////////////////////////////////
bool NisusWrtText::sendHeaderFooter(int hfId)
{
if (!m_parserState->m_textListener) {
MWAW_DEBUG_MSG(("NisusWrtText::sendHeaderFooter: can not find the listener\n"));
return false;
}
if (hfId >= int(m_state->m_hfList.size())) {
MWAW_DEBUG_MSG(("NisusWrtText::sendHeaderFooter: can not find the headerFooter list\n"));
return false;
}
if (hfId < 0)
return true;
auto const &hf = m_state->m_hfList[size_t(hfId)];
hf.m_parsed = true;
MWAWEntry entry;
entry.setId(NisusWrtStruct::Z_HeaderFooter);
NisusWrtStruct::Position pos;
pos.m_paragraph = static_cast<int>(hf.m_paragraph[0]);
entry.setBegin(findFilePos(NisusWrtStruct::Z_HeaderFooter, pos));
pos.m_paragraph = static_cast<int>(hf.m_paragraph[1]);
entry.setEnd(findFilePos(NisusWrtStruct::Z_HeaderFooter, pos));
if (entry.begin() < 0 || entry.length() < 0) {
MWAW_DEBUG_MSG(("NisusWrtText::sendHeaderFooter: can not compute the headerFooter entry\n"));
return false;
}
pos.m_paragraph = static_cast<int>(hf.m_paragraph[0]);
sendText(entry, pos);
return true;
}
////////////////////////////////////////////////////////////
bool NisusWrtText::sendFootnote(int footnoteId)
{
if (!m_parserState->m_textListener) {
MWAW_DEBUG_MSG(("NisusWrtText::sendFootnote: can not find the listener\n"));
return false;
}
if (footnoteId >= int(m_state->m_footnoteList.size())) {
MWAW_DEBUG_MSG(("NisusWrtText::sendFootnote: can not find the footnote list\n"));
return false;
}
if (footnoteId < 0)
return true;
auto const &fnote = m_state->m_footnoteList[size_t(footnoteId)];
fnote.m_parsed = true;
MWAWEntry entry;
entry.setId(NisusWrtStruct::Z_Footnote);
NisusWrtStruct::Position pos;
pos.m_paragraph = fnote.m_paragraph[0];
entry.setBegin(findFilePos(NisusWrtStruct::Z_Footnote, pos));
pos.m_paragraph = fnote.m_paragraph[1];
entry.setEnd(findFilePos(NisusWrtStruct::Z_Footnote, pos));
if (entry.begin() < 0 || entry.length() < 0) {
MWAW_DEBUG_MSG(("NisusWrtText::sendFootnote: can not compute the footnote entry\n"));
return false;
}
pos.m_paragraph = fnote.m_paragraph[0];
sendText(entry, pos);
return true;
}
////////////////////////////////////////////////////////////
bool NisusWrtText::sendText(MWAWEntry const &entry, NisusWrtStruct::Position firstPos)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: can not find the listener\n"));
return false;
}
if (!entry.valid()) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: the entry is bad\n"));
return false;
}
auto zoneId = static_cast<NisusWrtStruct::ZoneType>(entry.id());
if (zoneId >= 3) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: find unexpected zoneId: %d\n", zoneId));
return false;
}
auto &zone = m_state->m_zones[zoneId];
bool isMain = zoneId == NisusWrtStruct::Z_Main;
auto width = int(72.0*m_mainParser->getPageWidth());
if (isMain || zoneId == NisusWrtStruct::Z_Footnote) {
float colSep = 0.5f;
int nCol = 1;
m_mainParser->getColumnInfo(nCol, colSep);
if (nCol > 1)
width /= nCol;
if (isMain && nCol > 1) {
if (listener->isSectionOpened())
listener->closeSection();
MWAWSection sec;
sec.setColumns(nCol, double(width), librevenge::RVNG_POINT);
listener->openSection(sec);
}
}
MWAWInputStreamPtr input
= isMain ? m_mainParser->getInput() : m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile =
isMain ? m_mainParser->ascii() : m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(TEXT)[" << zoneId << "]:";
std::string str("");
NisusWrtStruct::Position actPos(firstPos);
auto it = zone.m_plcMap.begin();
while (it != zone.m_plcMap.end() && it->first.cmp(actPos) < 0)
++it;
NisusWrtTextInternal::Font actFont;
actFont.m_font = MWAWFont(3,12);
listener->setFont(actFont.m_font);
NisusWrtStruct::FootnoteInfo ftInfo;
m_mainParser->getFootnoteInfo(ftInfo);
bool lastCharFootnote = false;
int noteId = 0, numVar=0, actVar=-1;
std::vector<int> varValues;
std::map<int,int> fontIdToVarIdMap;
for (int i = 0; i <= entry.length(); i++) {
if (i==entry.length() && !lastCharFootnote)
break;
while (it != zone.m_plcMap.end() && it->first.cmp(actPos) <= 0) {
auto const &plcPos = it->first;
auto const &plc = it++->second;
f << str;
str="";
if (plcPos.cmp(actPos) < 0) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops find unexpected position\n"));
f << "###[" << plc << "]";
continue;
}
f << "[" << plc << "]";
switch (plc.m_type) {
case NisusWrtTextInternal::P_Format: {
if (plc.m_id < 0 || plc.m_id >= int(m_state->m_fontList.size())) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops can not find the actual font\n"));
f << "###";
break;
}
auto const &font = m_state->m_fontList[size_t(plc.m_id)];
actFont = font;
if (font.m_pictureId <= 0)
listener->setFont(font.m_font);
if (!font.isVariable())
break;
if (fontIdToVarIdMap.find(plc.m_id) != fontIdToVarIdMap.end())
actVar = fontIdToVarIdMap.find(plc.m_id)->second;
else {
actVar = numVar++;
fontIdToVarIdMap[plc.m_id]=actVar;
}
break;
}
case NisusWrtTextInternal::P_Ruler: {
if (plc.m_id < 0 || plc.m_id >= int(zone.m_paragraphList.size())) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops can not find the actual ruler\n"));
f << "###";
break;
}
auto const ¶ = zone.m_paragraphList[size_t(plc.m_id)];
setProperty(para, width);
break;
}
case NisusWrtTextInternal::P_Footnote: {
if (!isMain) break;
if (!lastCharFootnote) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops do not find the footnote symbol\n"));
break;
}
if (plc.m_id < 0 || plc.m_id >= int(m_state->m_footnoteList.size())) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: can not find the footnote\n"));
MWAWSubDocumentPtr subdoc(new NisusWrtTextInternal::SubDocument(*this, input, -1, libmwaw::DOC_NOTE));
listener->insertNote(MWAWNote(MWAWNote::FootNote), subdoc);
break;
}
MWAWSubDocumentPtr subdoc(new NisusWrtTextInternal::SubDocument(*this, input, plc.m_id, libmwaw::DOC_NOTE));
auto const &fnote = m_state->m_footnoteList[size_t(plc.m_id)];
noteId++;
if (fnote.m_number && noteId != fnote.m_number)
noteId = fnote.m_number;
MWAWNote note(ftInfo.endNotes() ? MWAWNote::EndNote : MWAWNote::FootNote);
note.m_number=noteId;
note.m_label=fnote.getTextLabel(noteId).c_str();
listener->insertNote(note, subdoc);
break;
}
case NisusWrtTextInternal::P_PicturePara: {
if (plc.m_id < 0 || plc.m_id >= int(zone.m_pictureParaList.size())) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: can not find the paragraph picture\n"));
break;
}
auto &pict = zone.m_pictureParaList[size_t(plc.m_id)];
MWAWPosition pictPos(MWAWVec2f(pict.m_position.min()), MWAWVec2f(pict.m_position.size()), librevenge::RVNG_POINT);
pictPos.setRelativePosition(MWAWPosition::Paragraph);
pictPos.m_wrapping = MWAWPosition::WBackground;
m_mainParser->sendPicture(pict.m_id, pictPos);
break;
}
case NisusWrtTextInternal::P_HeaderFooter:
break;
case NisusWrtTextInternal::P_Unknown:
#if !defined(__clang__)
default:
#endif
MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops can not find unknown plc type\n"));
f << "###";
break;
}
}
if (input->isEnd())
break;
if (i==entry.length())
break;
auto c = static_cast<unsigned char>(input->readULong(1));
str+=char(c);
if (c==0xd) {
f << str;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
str = "";
pos = input->tell();
f.str("");
f << "TEXT" << zoneId << ":";
}
// update the position
switch (c) {
case 0xd:
actPos.m_paragraph++;
actPos.m_word = actPos.m_char = 0;
break;
case '\t':
case ' ':
actPos.m_word++;
actPos.m_char = 0;
break;
default:
actPos.m_char++;
break;
}
// send char
lastCharFootnote = false;
switch (c) {
case 0x1: {
if (actFont.m_pictureId <= 0) {
MWAW_DEBUG_MSG(("NisusWrtText::sendText: can not find pictureId for char 1\n"));
f << "#";
break;
}
MWAWPosition pictPos(MWAWVec2f(actFont.m_pictureDim[0].min()), MWAWVec2f(actFont.m_pictureDim[0].size()), librevenge::RVNG_POINT);
pictPos.setRelativePosition(MWAWPosition::CharBaseLine);
pictPos.setClippingPosition
(MWAWVec2f(actFont.m_pictureDim[1].min()-actFont.m_pictureDim[0].min()),
MWAWVec2f(actFont.m_pictureDim[0].max()-actFont.m_pictureDim[1].max()));
m_mainParser->sendPicture(actFont.m_pictureId, pictPos);
break;
}
case 0x3: // checkme: find in some file ( but seems to do nothing )
break;
case 0x9:
listener->insertTab();
break;
case 0xb:
listener->insertEOL(true);
break;
case 0xc:
if (!isMain) break;
m_mainParser->newPage(++m_state->m_actualPage);
if (ftInfo.resetNumberOnNewPage()) noteId = 0;
break;
case 0xd:
listener->insertEOL();
break;
case 0xf: {
std::string format(m_mainParser->getDateFormat(zoneId, actVar));
MWAWField field(MWAWField::Date);
if (format.length())
field.m_DTFormat = format;
else
f << "#";
listener->insertField(field);
break;
}
case 0x10: {
MWAWField field(MWAWField::Time);
field.m_DTFormat = "%H:%M";
listener->insertField(field);
break;
}
case 0x11:
listener->insertField(MWAWField(MWAWField::Title));
break;
case 0x14: // mark separator ( ok to ignore )
break;
case 0x1c:
if (isMain)
lastCharFootnote = true;
break;
case 0x1d: {
MWAWField::Type fType;
std::string content;
if (!m_mainParser->getReferenceData(zoneId, actVar, fType, content, varValues)) {
listener->insertChar(' ');
f << "#[ref]";
break;
}
if (fType != MWAWField::None)
listener->insertField(MWAWField(fType));
else if (content.length())
listener->insertUnicodeString(librevenge::RVNGString(content.c_str()));
else
f << "#[ref]";
break;
}
// checkme: find also 0x8, 0x13, 0x15, 0x1e, 0x1f in glossary
default:
i+=listener->insertCharacter(static_cast<unsigned char>(c), input, entry.end());
break;
}
}
f << str;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
return true;
}
//! send data to the listener
bool NisusWrtText::sendMainText()
{
if (!m_parserState->m_textListener) return true;
if (!m_state->m_zones[0].m_entry.valid()) {
MWAW_DEBUG_MSG(("NisusWrtText::sendMainText: can not find the main text\n"));
return false;
}
m_state->m_zones[0].m_entry.setParsed(true);
sendText(m_state->m_zones[0].m_entry);
return true;
}
void NisusWrtText::flushExtra()
{
if (!m_parserState->m_textListener) return;
for (size_t f = 0; f < m_state->m_footnoteList.size(); f++) {
if (m_state->m_footnoteList[f].m_parsed)
continue;
sendFootnote(int(f));
}
m_parserState->m_textListener->insertChar(' ');
for (size_t hf = 0; hf < m_state->m_hfList.size(); hf++) {
if (m_state->m_hfList[hf].m_parsed)
continue;
sendHeaderFooter(int(hf));
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: