/* -*- 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 "MWAWPageSpan.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPosition.hxx"
#include "MWAWRSRCParser.hxx"
#include "MWAWSection.hxx"
#include "MWAWSubDocument.hxx"
#include "LightWayTxtParser.hxx"
#include "LightWayTxtText.hxx"
/** Internal: the structures of a LightWayTxtText */
namespace LightWayTxtTextInternal
{
/** the different plc type */
enum PLCType { P_Font, P_Font2, P_Ruler, P_Ruby, P_StyleU, P_StyleV, P_Unknown};
/** Internal : the different plc types: mainly for debugging */
struct PLC {
/// the constructor
PLC()
: m_type(P_Unknown)
, m_id(-1)
, m_extra("")
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, PLC const &plc);
/** the PLC types */
PLCType m_type;
/** the id */
int m_id;
/** extra data */
std::string m_extra;
};
std::ostream &operator<<(std::ostream &o, PLC const &plc)
{
switch (plc.m_type) {
case P_Font:
o << "F";
break;
case P_Ruler:
o << "P";
break;
case P_StyleU:
o << "U";
break;
case P_StyleV:
o << "V";
break;
case P_Font2:
o << "Fa";
break;
case P_Ruby:
o << "Rb";
break;
case P_Unknown:
#if !defined(__clang__)
default:
#endif
o << "#Unkn";
break;
}
if (plc.m_id >= 0) o << plc.m_id;
else o << "_";
if (plc.m_extra.length()) o << ":" << plc.m_extra;
else o << ",";
return o;
}
////////////////////////////////////////
//! Internal: struct used to store the font of a LightWayTxtText
struct Font {
//! constructor
Font()
: m_font()
, m_height(0)
, m_pictId(0)
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Font const &font);
//! merge extra data to get final font
void merge(Font const &fExtra)
{
m_font.setFlags(m_font.flags()|fExtra.m_font.flags());
if (fExtra.m_font.getUnderline().isSet())
m_font.setUnderline(fExtra.m_font.getUnderline());
if (fExtra.m_font.getStrikeOut().isSet())
m_font.setStrikeOut(fExtra.m_font.getStrikeOut());
if (fExtra.m_font.getOverline().isSet())
m_font.setOverline(fExtra.m_font.getOverline());
m_font.set(fExtra.m_font.script());
MWAWColor backColor;
fExtra.m_font.getBackgroundColor(backColor);
m_font.setBackgroundColor(backColor);
m_font.m_extra += fExtra.m_font.m_extra;
m_pictId = fExtra.m_pictId;
}
//! the font
MWAWFont m_font;
//! the line height
int m_height;
//! the pict id (if set)
int m_pictId;
};
std::ostream &operator<<(std::ostream &o, Font const &font)
{
if (font.m_height > 0)
o << "h=" << font.m_height << ",";
if (font.m_pictId > 0)
o << "pictId=" << font.m_pictId << ",";
return o;
}
/** Internal: class to store the paragraph properties */
struct Paragraph final : public MWAWParagraph {
//! Constructor
Paragraph()
: MWAWParagraph()
, m_deltaSpacing(0)
{
}
//! destructor
~Paragraph() final;
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind)
{
o << static_cast<MWAWParagraph const &>(ind);
if (ind.m_deltaSpacing<0 || ind.m_deltaSpacing>0) o << "deltaSpacing=" << ind.m_deltaSpacing << ",";
return o;
}
//! the paragraph delta spacing
float m_deltaSpacing;
};
Paragraph::~Paragraph()
{
}
////////////////////////////////////////
//! Internal: the header/footer zone of a LightWayTxtText
struct HFZone {
//! constructor
HFZone()
: m_numChar(0)
, m_pos()
, m_height(0)
, m_font()
, m_justify(MWAWParagraph::JustificationLeft)
, m_extra("")
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, HFZone const &hf);
//! the number of char
int m_numChar;
//! the position of the text in the file
MWAWEntry m_pos;
//! the height
int m_height;
//! the font
MWAWFont m_font;
/** the paragraph justification */
MWAWParagraph::Justification m_justify;
//! extra data
std::string m_extra;
};
std::ostream &operator<<(std::ostream &o, HFZone const &hf)
{
if (hf.m_numChar > 0)
o << "nC=" << hf.m_numChar << ",";
if (hf.m_height > 0)
o << "h=" << hf.m_height << ",";
switch (hf.m_justify) {
case MWAWParagraph::JustificationLeft:
break;
case MWAWParagraph::JustificationCenter:
o << "just=centered, ";
break;
case MWAWParagraph::JustificationRight:
o << "just=right, ";
break;
case MWAWParagraph::JustificationFull:
o << "just=full, ";
break;
case MWAWParagraph::JustificationFullAllLines:
o << "just=fullAllLines, ";
break;
#if !defined(__clang__)
default:
o << "#just=" << hf.m_justify << ", ";
#endif
}
o << hf.m_extra;
return o;
}
////////////////////////////////////////
//! Internal: the state of a LightWayTxtText
struct State {
//! constructor
State()
: m_version(-1)
, m_numPages(-1)
, m_actualPage(0)
, m_fontsList()
, m_auxiFontsList()
, m_paragraphsList()
, m_plcMap()
, m_header()
, m_footer()
{
}
//! the file version
mutable int m_version;
int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
//! the list of fonts
std::vector<Font> m_fontsList;
//! the auxiliar list of fonts
std::vector<Font> m_auxiFontsList;
//! the list of paragraph
std::vector<Paragraph> m_paragraphsList;
std::multimap<long, PLC> m_plcMap /** the plc map */;
HFZone m_header /** header */, m_footer /** footer */;
};
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
LightWayTxtText::LightWayTxtText(LightWayTxtParser &parser)
: m_parserState(parser.getParserState())
, m_state(new LightWayTxtTextInternal::State)
, m_mainParser(&parser)
{
}
LightWayTxtText::~LightWayTxtText()
{
}
int LightWayTxtText::version() const
{
if (m_state->m_version < 0)
m_state->m_version = m_parserState->m_version;
return m_state->m_version;
}
int LightWayTxtText::numPages() const
{
if (m_state->m_numPages >= 0)
return m_state->m_numPages;
const_cast<LightWayTxtText *>(this)->computePositions();
return m_state->m_numPages;
}
void LightWayTxtText::computePositions()
{
int nPages = 1;
m_state->m_actualPage = 1;
m_state->m_numPages = nPages;
}
bool LightWayTxtText::hasHeaderFooter(bool header) const
{
if (header)
return m_state->m_header.m_pos.valid();
return m_state->m_footer.m_pos.valid();
}
bool LightWayTxtText::getColor(int id, MWAWColor &col) const
{
if (id < 0 || id >= 11) {
MWAW_DEBUG_MSG(("LightWayTxtText::createZones: can not find col for id=%d\n", id));
return false;
}
static uint32_t const color[] = {
0, 0xFF0000, 0x00FF00, 0xFF, 0xFFFF, 0xFF00FF, 0xFFFF00,
0xFF8040, 0x800000, 0x808080, 0xFFFFFF
};
col = color[id];
return true;
}
////////////////////////////////////////////////////////////
// Intermediate level
////////////////////////////////////////////////////////////
// find the different zones
bool LightWayTxtText::createZones()
{
MWAWRSRCParserPtr rsrcParser = m_mainParser->getRSRCParser();
if (!rsrcParser) {
MWAW_DEBUG_MSG(("LightWayTxtText::createZones: can not find the entry map\n"));
return false;
}
auto &entryMap = rsrcParser->getEntriesMap();
// the different zones
auto it = entryMap.lower_bound("styl");
while (it != entryMap.end()) {
if (it->first != "styl")
break;
MWAWEntry const &entry = it++->second;
readFonts(entry);
}
it = entryMap.lower_bound("styw");
while (it != entryMap.end()) {
if (it->first != "styw")
break;
MWAWEntry const &entry = it++->second;
readFont2(entry);
}
it = entryMap.lower_bound("styx");
while (it != entryMap.end()) {
if (it->first != "styx")
break;
MWAWEntry const &entry = it++->second;
readRulers(entry);
}
it = entryMap.lower_bound("styu"); // pos, flags, 0?
while (it != entryMap.end()) {
if (it->first != "styu")
break;
MWAWEntry const &entry = it++->second;
readStyleU(entry);
}
it = entryMap.lower_bound("styv"); // never seen
while (it != entryMap.end()) {
if (it->first != "styv")
break;
MWAWEntry const &entry = it++->second;
readUnknownStyle(entry);
}
it = entryMap.lower_bound("styy"); // pos, 2 flags
while (it != entryMap.end()) {
if (it->first != "styy")
break;
MWAWEntry const &entry = it++->second;
readRuby(entry);
}
// now update the different data
computePositions();
return true;
}
bool LightWayTxtText::sendMainText()
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) {
MWAW_DEBUG_MSG(("LightWayTxtText::sendMainText: can not find a listener\n"));
return false;
}
bool useDataFork=m_mainParser->textInDataFork();
libmwaw::DebugFile &ascFile =
useDataFork ? m_parserState->m_asciiFile : m_mainParser->rsrcAscii();
libmwaw::DebugStream f, f2;
MWAWInputStreamPtr input=
useDataFork ? m_parserState->m_input : m_mainParser->rsrcInput();
long beginPos=0, endPos=-1;
if (!useDataFork) {
MWAWEntry entry = m_mainParser->getRSRCParser()->getEntry("TEXT", 128);
if (!entry.valid()) {
MWAW_DEBUG_MSG(("LightWayTxtText::sendMainText: can not find text entry\n"));
return false;
}
beginPos=entry.begin();
endPos=entry.end();
}
long pos = beginPos;
input->seek(pos, librevenge::RVNG_SEEK_SET);
LightWayTxtTextInternal::Font font, auxiFont;
int numCols, sepWidth;
if (m_mainParser->getColumnInfo(numCols, sepWidth) && numCols > 1) {
MWAWSection sec;
sec.setColumns(numCols, m_mainParser->getPageWidth()/double(numCols), librevenge::RVNG_INCH);
listener->openSection(sec);
}
float deltaSpacing=0;
while (1) {
long actPos = input->tell();
bool done = input->isEnd() || (endPos>=0&&actPos>=endPos);
char c = done ? char(0) : char(input->readULong(1));
if (c==0xd || done) {
f2.str("");
f2 << "Entries(TextContent):" << f.str();
ascFile.addPos(pos);
ascFile.addNote(f2.str().c_str());
f.str("");
pos = actPos+1;
}
if (done) break;
long actC=actPos-beginPos;
auto plcIt = m_state->m_plcMap.find(actC);
bool fontChanged = false;
while (plcIt != m_state->m_plcMap.end() && plcIt->first<=actC) {
auto const &plc = plcIt++->second;
f << "[" << plc << "]";
switch (plc.m_type) {
case LightWayTxtTextInternal::P_Font:
if (plc.m_id < 0 || plc.m_id >= int(m_state->m_fontsList.size())) {
MWAW_DEBUG_MSG(("LightWayTxtText::sendMainText: can not find font %d\n", plc.m_id));
break;
}
font = m_state->m_fontsList[size_t(plc.m_id)];
fontChanged = true;
break;
case LightWayTxtTextInternal::P_Font2:
if (plc.m_id < 0 || plc.m_id >= int(m_state->m_auxiFontsList.size())) {
MWAW_DEBUG_MSG(("LightWayTxtText::sendMainText: can not find auxi font %d\n", plc.m_id));
break;
}
auxiFont = m_state->m_auxiFontsList[size_t(plc.m_id)];
fontChanged = true;
break;
case LightWayTxtTextInternal::P_Ruler: {
float newDeltaSpacing = 0;
if (plc.m_id < 0 || plc.m_id >= int(m_state->m_paragraphsList.size())) {
MWAW_DEBUG_MSG(("LightWayTxtText::sendMainText: can not find paragraph %d\n", plc.m_id));
}
else {
auto const ¶=m_state->m_paragraphsList[size_t(plc.m_id)];
newDeltaSpacing = para.m_deltaSpacing;
setProperty(para);
}
if (deltaSpacing < newDeltaSpacing || deltaSpacing > newDeltaSpacing) {
deltaSpacing=newDeltaSpacing;
fontChanged = true;
}
break;
}
case LightWayTxtTextInternal::P_Ruby:
case LightWayTxtTextInternal::P_StyleU:
case LightWayTxtTextInternal::P_StyleV:
case LightWayTxtTextInternal::P_Unknown:
#if !defined(__clang__)
default:
#endif
break;
}
}
f << c;
if (fontChanged) {
LightWayTxtTextInternal::Font final(font);
final.merge(auxiFont);
if (deltaSpacing<0||deltaSpacing>0)
final.m_font.setDeltaLetterSpacing(deltaSpacing+final.m_font.deltaLetterSpacing());
listener->setFont(final.m_font);
if (final.m_pictId>0) {
m_mainParser->sendGraphic(final.m_pictId);
if (c==char(0xca))
continue;
}
}
switch (c) {
case 0x9:
listener->insertTab();
break;
case 0xa:
listener->insertEOL(true);
break;
case 0xc: // new section (done)
break;
case 0xd:
listener->insertEOL();
break;
default:
listener->insertCharacter(static_cast<unsigned char>(c), input);
break;
}
}
return true;
}
//////////////////////////////////////////////
// Fonts
//////////////////////////////////////////////
bool LightWayTxtText::readFonts(MWAWEntry const &entry)
{
if (!entry.valid() || entry.length() < 2) {
MWAW_DEBUG_MSG(("LightWayTxtText::readFonts: the entry is bad\n"));
return false;
}
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(Fonts)[" << entry.id() << "]:";
entry.setParsed(true);
auto N=static_cast<int>(input->readULong(2));
f << "N=" << N << ",";
if (long(N*20+2) != entry.length()) {
MWAW_DEBUG_MSG(("LightWayTxtText::readFonts: the number of entry seems bad\n"));
f << "###";
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
return false;
}
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
LightWayTxtTextInternal::PLC plc;
plc.m_type = LightWayTxtTextInternal::P_Font;
for (int i = 0; i < N; i++) {
pos = input->tell();
LightWayTxtTextInternal::Font font;
f.str("");
long cPos = input->readLong(4);
font.m_height = static_cast<int>(input->readLong(2));
auto sz = float(input->readLong(2));
font.m_font.setId(static_cast<int>(input->readULong(2)));
uint32_t flags=0;
auto flag=static_cast<int>(input->readULong(1));
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&0x80) f << "#fl80,";
auto val = static_cast<int>(input->readULong(1)); // always 0?
if (val) f << "#f0=" << val << ",";
font.m_font.setFlags(flags);
font.m_font.setSize(float(input->readLong(2)));
if (sz < font.m_font.size() || sz > font.m_font.size())
f << "#sz=" << sz << ",";
unsigned char col[3];
for (auto &c : col) c = static_cast<unsigned char>(input->readULong(2)>>8);
if (col[0] || col[1] || col[2])
font.m_font.setColor(MWAWColor(col[0],col[1],col[2]));
font.m_font.m_extra=f.str();
f.str("");
f << "Fonts-" << i << ":cPos=" << std::hex << cPos << std::dec << ","
<< font.m_font.getDebugString(m_parserState->m_fontConverter) << font;
m_state->m_fontsList.push_back(font);
plc.m_id = i;
m_state->m_plcMap.insert(std::map<long, LightWayTxtTextInternal::PLC>::value_type(cPos, plc));
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+20, librevenge::RVNG_SEEK_SET);
}
return true;
}
bool LightWayTxtText::readFont2(MWAWEntry const &entry)
{
if (!entry.valid() || (entry.length()%10) != 2) {
MWAW_DEBUG_MSG(("LightWayTxtText::readFont2: the entry seems bad\n"));
return false;
}
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(Font2)[" << entry.id() << "]:";
entry.setParsed(true);
auto N=static_cast<int>(input->readULong(2));
f << "N=" << N << ",";
if (entry.length() != N*10+2) {
f << "###";
MWAW_DEBUG_MSG(("LightWayTxtText::readFont2: N seems bad\n"));
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
return false;
}
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
LightWayTxtTextInternal::PLC plc;
plc.m_type = LightWayTxtTextInternal::P_Font2;
for (int i = 0; i < N; i++) {
pos = input->tell();
f.str("");
LightWayTxtTextInternal::Font font;
long cPos = input->readLong(4);
uint32_t flag = static_cast<uint32_t>(input->readULong(2)), flags=0;
switch ((flag>>3)&7) {
case 0:
break;
case 1:
font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
break;
case 2:
font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
font.m_font.setUnderlineType(MWAWFont::Line::Double);
break;
case 3:
font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
font.m_font.setUnderlineWidth(2.0);
break;
case 4:
font.m_font.setUnderlineStyle(MWAWFont::Line::Dot);
break;
case 5:
font.m_font.setUnderlineStyle(MWAWFont::Line::Dot);
font.m_font.setUnderlineWidth(2.0);
break;
default:
f << "#underline=" << ((flag>>3)&7) << ",";
break;
}
switch ((flag>>6)&7) {
case 0:
break;
case 1:
font.m_font.setStrikeOutStyle(MWAWFont::Line::Simple);
break;
case 2:
font.m_font.setStrikeOutStyle(MWAWFont::Line::Simple);
font.m_font.setStrikeOutType(MWAWFont::Line::Double);
break;
case 3:
font.m_font.setStrikeOutStyle(MWAWFont::Line::Simple);
font.m_font.setStrikeOutWidth(2.0);
break;
case 4:
font.m_font.setStrikeOutStyle(MWAWFont::Line::Dot);
break;
case 5:
font.m_font.setStrikeOutStyle(MWAWFont::Line::Dot);
font.m_font.setStrikeOutWidth(2.0);
break;
default:
f << "#strike=" << ((flag>>6)&7) << ",";
break;
}
switch ((flag>>9)&7) {
case 0:
break;
case 1:
font.m_font.setOverlineStyle(MWAWFont::Line::Simple);
break;
case 2:
font.m_font.setOverlineStyle(MWAWFont::Line::Simple);
font.m_font.setOverlineType(MWAWFont::Line::Double);
break;
case 3:
font.m_font.setOverlineStyle(MWAWFont::Line::Simple);
font.m_font.setOverlineWidth(2.0);
break;
case 4:
font.m_font.setOverlineStyle(MWAWFont::Line::Dot);
break;
case 5:
font.m_font.setOverlineStyle(MWAWFont::Line::Dot);
font.m_font.setOverlineWidth(2.0);
break;
default:
f << "#over=" << ((flag>>9)&7) << ",";
break;
}
if (flag >> 12) {
MWAWColor col(0);
if (getColor(int(flag >> 12), col)) {
font.m_font.setOverlineColor(col);
font.m_font.setStrikeOutColor(col);
font.m_font.setUnderlineColor(col);
}
else
f << "#colorId[line]=" << (flag >> 12) << ",";
}
flag &= 0x0004;
if (flag) f << "flags=#" << std::hex << flag << std::dec << ",";
/* fl0=0|2|b|40|42|..|a0 */
auto val = long(input->readULong(1));
MWAWColor backColor(MWAWColor::black());
if ((val & 0xf0) && !getColor(int(val>>4), backColor))
f << "#backColorId=" << (val>>4) << ",";
int percent = (val & 0xf);
if (percent >= 0 && percent <= 10) {
float percentVal = 0.1f*float(percent);
backColor =MWAWColor::barycenter(percentVal,backColor,1.f-percentVal,MWAWColor::white());
font.m_font.setBackgroundColor(backColor);
}
else if (percent == 11) // fixme: backColor is the border color
flags |= MWAWFont::boxedBit;
else {
MWAW_DEBUG_MSG(("LightWayTxtText::readFont2: find unknown percent id=%d\n", percent));
}
// 0: 0%, .. 10:100%, 11:border
if (val & 0xf)
f << "backPatternId=" << (val&0xf) << ",";
flag = static_cast<uint32_t>(input->readULong(1));
switch (flag&7) {
case 0:
break;
case 1:
font.m_font.set(MWAWFont::Script(33));
break;
case 2:
font.m_font.set(MWAWFont::Script(-33));
break;
case 5:
font.m_font.set(MWAWFont::Script::super());
break;
case 6:
font.m_font.set(MWAWFont::Script::sub());
break;
default:
f << "#pos=" << (flag&7) << ",";
}
if (flag&8) f << "write[hor],";
if (flag & 0xF0)
f << "#pos2=" << std::hex << (flag&0xF0) << std::dec << ",";
font.m_pictId = static_cast<int>(input->readULong(2));
font.m_font.setFlags(flags);
font.m_font.m_extra = f.str();
plc.m_id = i;
plc.m_extra = f.str();
m_state->m_auxiFontsList.push_back(font);
m_state->m_plcMap.insert(std::map<long, LightWayTxtTextInternal::PLC>::value_type(cPos, plc));
f.str("");
f << "Font2-" << i << ":cPos=" << std::hex << cPos << std::dec << ",Fa" << i << ","
<< font.m_font.getDebugString(m_parserState->m_fontConverter) << font;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+10, librevenge::RVNG_SEEK_SET);
}
return true;
}
//////////////////////////////////////////////
// Ruler
//////////////////////////////////////////////
void LightWayTxtText::setProperty(MWAWParagraph const &ruler)
{
if (!m_parserState->m_textListener) return;
m_parserState->m_textListener->setParagraph(ruler);
}
bool LightWayTxtText::readRulers(MWAWEntry const &entry)
{
if (!entry.valid() || (entry.length()%84) != 2) {
MWAW_DEBUG_MSG(("LightWayTxtText::readRulers: the entry seems bad\n"));
return false;
}
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(Ruler)[" << entry.id() << "]:";
entry.setParsed(true);
auto N=static_cast<int>(input->readULong(2));
f << "N=" << N << ",";
if (entry.length() != N*84+2) {
f << "###";
MWAW_DEBUG_MSG(("LightWayTxtText::readRulers: N seems bad\n"));
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
return false;
}
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
LightWayTxtTextInternal::PLC plc;
plc.m_type = LightWayTxtTextInternal::P_Ruler;
for (int i = 0; i < N; i++) {
LightWayTxtTextInternal::Paragraph para;
pos = input->tell();
f.str("");
long cPos = input->readLong(4);
para.m_marginsUnit = librevenge::RVNG_POINT;
para.m_margins[0] = static_cast<int>(input->readLong(2));
para.m_margins[1] = static_cast<int>(input->readLong(2));
para.m_margins[2] = static_cast<int>(input->readLong(2));
para.m_margins[0] = para.m_margins[0].get()-para.m_margins[1].get();
long val = static_cast<int>(input->readLong(2));
if (val)
para.m_spacings[1]=para.m_spacings[2]=double(val)/72.;
para.m_deltaSpacing = float(input->readLong(2));
auto flag = long(input->readULong(1));
switch (flag) {
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 << "#justify=" << flag << ",";
}
auto numTabs = static_cast<int>(input->readULong(1));
if (numTabs > 16) {
MWAW_DEBUG_MSG(("LightWayTxtText::readRulers: the numbers of tabs seems bad\n"));
f << "###nTabs=" << numTabs << ",";
numTabs=0;
}
auto tabsType = static_cast<uint32_t>(input->readULong(4));
for (int j = 0; j < numTabs; j++) {
MWAWTabStop tab;
tab.m_position = double(input->readLong(2))/72.;
switch (tabsType&3) {
case 1:
tab.m_alignment = MWAWTabStop::CENTER;
break;
case 2:
tab.m_alignment = MWAWTabStop::RIGHT;
break;
case 3:
tab.m_alignment = MWAWTabStop::DECIMAL;
break;
case 0: // left
default:
break;
}
tabsType = tabsType>>2;
para.m_tabs->push_back(tab);
}
input->seek(pos+52, librevenge::RVNG_SEEK_SET);
auto tabsLeader = static_cast<uint32_t>(input->readULong(4));
for (auto &tab : *para.m_tabs) {
uint16_t leader=0;
switch (tabsLeader&3) {
case 1:
leader='.';
break;
case 2:
leader='-';
break;
case 3:
leader='_';
break;
case 0: // none
default:
break;
}
tabsLeader = tabsLeader>>2;
if (!leader) continue;
tab.m_leaderCharacter = leader;
}
for (int j = 0; j < 3; j++) { // g0=0|0b13
val = static_cast<int>(input->readLong(2));
if (val)
f << "g" << j << "=" << std::hex << val << std::dec << ",";
}
for (int j = 0; j < 2; j++) { // g5=5|14
val = long(input->readULong(1));
if (val) f << "g" << j+3 << "=" << val << ",";
}
for (int j = 0; j < 10; j++) { // always 0
val = static_cast<int>(input->readLong(2));
if (val)
f << "h" << j << "=" << std::hex << val << std::dec << ",";
}
para.m_extra = f.str();
m_state->m_paragraphsList.push_back(para);
plc.m_id = i;
m_state->m_plcMap.insert(std::map<long, LightWayTxtTextInternal::PLC>::value_type(cPos, plc));
f.str("");
f << "Ruler-" << i << ":cPos=" << std::hex << cPos << std::dec << "," << para;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+84, librevenge::RVNG_SEEK_SET);
}
return true;
}
//////////////////////////////////////////////
// HF
//////////////////////////////////////////////
bool LightWayTxtText::sendHeaderFooter(bool header)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) {
MWAW_DEBUG_MSG(("LightWayTxtText::sendHeaderFooter: can not find the listener\n"));
return false;
}
LightWayTxtTextInternal::HFZone const &zone =
header ? m_state->m_header : m_state->m_footer;
if (!zone.m_pos.valid()) {
MWAW_DEBUG_MSG(("LightWayTxtText::sendHeaderFooter: can not find the text\n"));
return false;
}
MWAWParagraph para;
para.m_justify=zone.m_justify;
listener->setParagraph(para);
listener->setFont(zone.m_font);
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
input->seek(zone.m_pos.begin(), librevenge::RVNG_SEEK_SET);
auto numChar = static_cast<int>(zone.m_pos.length());
std::string str("");
for (int c = 0; c < numChar; c++) {
if (input->isEnd()) {
MWAW_DEBUG_MSG(("LightWayTxtText::sendHeaderFooter: can not read end of text\n"));
break;
}
str += char(input->readULong(1));
}
auto it = str.begin();
while (it!=str.end()) {
char c=*(it++);
if (c=='<' && it!= str.end()) {
char const *strPtr = &(*it);
bool done = true;
if (strncmp(strPtr, "PAGE>", 5)==0)
listener->insertField(MWAWField(MWAWField::PageNumber));
else if (strncmp(strPtr, "DATE>", 5)==0)
listener->insertField(MWAWField(MWAWField::Date));
else if (strncmp(strPtr, "TIME>", 5)==0)
listener->insertField(MWAWField(MWAWField::Time));
else if (strncmp(strPtr, "PMAX>", 5)==0)
listener->insertField(MWAWField(MWAWField::PageCount));
else if (strncmp(strPtr, "NAME>", 5)==0)
listener->insertField(MWAWField(MWAWField::Title));
else
done=false;
if (done) {
it += 5;
continue;
}
}
if (c == 0xd) {
listener->insertEOL();
continue;
}
listener->insertCharacter(static_cast<unsigned char>(c));
}
return true;
}
bool LightWayTxtText::readDocumentHF(MWAWEntry const &entry)
{
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
long pos = input->tell();
libmwaw::DebugStream f, f2;
f << "Document(HF):";
if (entry.length()<0x50) {
f << "###";
MWAW_DEBUG_MSG(("LightWayTxtText::readDocumentHF: HF seems too short\n"));
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
return false;
}
for (int s=0; s < 2; s++) {
LightWayTxtTextInternal::HFZone zone;
zone.m_height = static_cast<int>(input->readLong(2));
zone.m_numChar = static_cast<int>(input->readLong(2));
// ruler align
auto flag = static_cast<int>(input->readULong(1)); // [012]
f2.str("");
switch (flag) {
case 0:
break; // left
case 1:
zone.m_justify = MWAWParagraph::JustificationCenter;
break;
case 2:
zone.m_justify = MWAWParagraph::JustificationRight;
break;
case 3:
zone.m_justify = MWAWParagraph::JustificationFull;
break;
default:
f2 << "#justify=" << flag << ",";
}
uint32_t flags=0;
flag=static_cast<int>(input->readULong(1));
if (flag&0x1) flags |= MWAWFont::boldBit;
if (flag&0x2) flags |= MWAWFont::italicBit;
if (flag&0x4) zone.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
if (flag&0x8) flags |= MWAWFont::embossBit;
if (flag&0x10) flags |= MWAWFont::shadowBit;
if (flag&0x20) zone.m_font.setDeltaLetterSpacing(-1);
if (flag&0x40) zone.m_font.setDeltaLetterSpacing(1);
if (flag&0x80) f2 << "#fl80,";
zone.m_font.setFlags(flags);
zone.m_font.setId(static_cast<int>(input->readULong(2)));
zone.m_font.setSize(float(input->readULong(2)));
unsigned char col[3];
for (auto &c : col) c = static_cast<unsigned char>(input->readULong(2)>>8);
if (col[0] || col[1] || col[2])
zone.m_font.setColor(MWAWColor(col[0],col[1],col[2]));
long val = input->readLong(2); // can be 0x200
if (val) f2 << "f0=" << std::hex << val << std::dec << ",";
zone.m_extra = f2.str();
if (s==0)
m_state->m_header = zone;
else
m_state->m_footer = zone;
f << (s==0 ? "header" : "footer") << "=[" << zone << ","
<< zone.m_font.getDebugString(m_parserState->m_fontConverter) << "],";
val = input->readLong(2);
if (val) {
if (s==0) f << "fPage=" << val+1 << ",";
else
f << "#f1=" << std::hex << val << std::dec << ",";
}
}
long debText = input->tell();
auto numRemains=int(entry.end()-debText);
bool ok = numRemains == m_state->m_header.m_numChar+m_state->m_footer.m_numChar;
if (ok) {
if (m_state->m_header.m_numChar) {
m_state->m_header.m_pos.setBegin(debText);
m_state->m_header.m_pos.setLength(m_state->m_header.m_numChar);
debText+=m_state->m_header.m_numChar;
}
if (m_state->m_footer.m_numChar) {
m_state->m_footer.m_pos.setBegin(debText);
m_state->m_footer.m_pos.setLength(m_state->m_footer.m_numChar);
}
}
else {
MWAW_DEBUG_MSG(("LightWayTxtText::readDocumentHF: HF can not determine header/footer text\n"));
f << "###";
}
std::string name(""); // something like <NAME>(<PAGE>)
for (int i = 0; i < numRemains; i++)
name += char(input->readULong(1));
f << name << ",";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
return true;
}
//////////////////////////////////////////////
// Unknown
//////////////////////////////////////////////
bool LightWayTxtText::readStyleU(MWAWEntry const &entry)
{
if (!entry.valid() || (entry.length()%8) != 4) {
MWAW_DEBUG_MSG(("LightWayTxtText::readStyleU: the entry seems bad\n"));
return false;
}
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(StyleU)[" << entry.id() << "]:";
entry.setParsed(true);
auto N=static_cast<int>(input->readULong(4));
f << "N=" << N << ",";
if (entry.length() != N*8+4) {
f << "###";
MWAW_DEBUG_MSG(("LightWayTxtText::readStyleU: N seems bad\n"));
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
return false;
}
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
LightWayTxtTextInternal::PLC plc;
plc.m_type = LightWayTxtTextInternal::P_StyleU;
for (int i = 0; i < N; i++) {
pos = input->tell();
f.str("");
long cPos = input->readLong(4);
auto flag = long(input->readULong(2)); // 2022
if (flag)
f << "flag=" << std::hex << flag << std::dec << ",";
long val = input->readLong(2); // always 0
if (val) f << "f0=" << val << ",";
plc.m_id = i;
plc.m_extra = f.str();
m_state->m_plcMap.insert(std::map<long, LightWayTxtTextInternal::PLC>::value_type(cPos, plc));
f.str("");
f << "StyleU-" << i << ":cPos=" << std::hex << cPos << std::dec << "," << plc;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+8, librevenge::RVNG_SEEK_SET);
}
return true;
}
bool LightWayTxtText::readRuby(MWAWEntry const &entry)
{
if (!entry.valid() || (entry.length()%6) != 4) {
MWAW_DEBUG_MSG(("LightWayTxtText::readRuby: the entry seems bad\n"));
return false;
}
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(Ruby)[" << entry.id() << "]:";
entry.setParsed(true);
auto N=static_cast<int>(input->readULong(4));
f << "N=" << N << ",";
if (entry.length() != N*6+4) {
f << "###";
MWAW_DEBUG_MSG(("LightWayTxtText::readRuby: N seems bad\n"));
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
return false;
}
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
LightWayTxtTextInternal::PLC plc;
plc.m_type = LightWayTxtTextInternal::P_Ruby;
for (int i = 0; i < N; i++) {
pos = input->tell();
f.str("");
long cPos = input->readLong(4);
f << "n[text]=" << static_cast<int>(input->readULong(1)) << ",";
f << "n[ruby]=" << static_cast<int>(input->readULong(1)) << ",";
plc.m_id = i;
plc.m_extra = f.str();
m_state->m_plcMap.insert(std::map<long, LightWayTxtTextInternal::PLC>::value_type(cPos, plc));
f.str("");
f << "Ruby-" << i << ":cPos=" << std::hex << cPos << std::dec << "," << plc;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+6, librevenge::RVNG_SEEK_SET);
}
return true;
}
bool LightWayTxtText::readUnknownStyle(MWAWEntry const &entry)
{
if (!entry.valid() || entry.length() < 4) {
MWAW_DEBUG_MSG(("LightWayTxtText::readUnknownStyle: the entry is bad\n"));
return false;
}
MWAWInputStreamPtr input = m_mainParser->rsrcInput();
libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
long pos = entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(" << entry.type() << ")[" << entry.id() << "]:";
entry.setParsed(true);
int numSz=2;
auto N=static_cast<int>(input->readULong(2));
if (!N) {
N=static_cast<int>(input->readULong(2));
numSz+=2;
}
f << "N=" << N << ",";
int fSz = N ? int((entry.length()-numSz)/N) : 0;
if (long(N*fSz+numSz) != entry.length()) {
MWAW_DEBUG_MSG(("LightWayTxtText::readUnknownStyle: the number of entry seems bad\n"));
f << "###";
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
return false;
}
ascFile.addPos(pos-4);
ascFile.addNote(f.str().c_str());
for (int i = 0; i < N; i++) {
pos = input->tell();
f.str("");
f << entry.type() << "-" << i << ":";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+fSz, librevenge::RVNG_SEEK_SET);
}
return true;
}
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
void LightWayTxtText::flushExtra()
{
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: