/* -*- 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 <set>
#include <sstream>
#include <librevenge/librevenge.h>
#include "MWAWCell.hxx"
#include "MWAWTextListener.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWSection.hxx"
#include "MWAWTable.hxx"
#include "FullWrtParser.hxx"
#include "FullWrtStruct.hxx"
#include "FullWrtText.hxx"
#define DEBUGII 0
/** Internal: the structures of a FullWrtText */
namespace FullWrtTextInternal
{
/** Internal: class to store a para modifier with appear in docInfo */
struct ParaModifier {
//! constructor
ParaModifier()
: m_beforeSpacing(0)
, m_afterSpacing(0)
, m_extra("")
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, ParaModifier const &p)
{
if (p.m_beforeSpacing<0)
o << "befSpacing=" << -p.m_beforeSpacing << "pt,";
else if (p.m_beforeSpacing>0)
o << "befSpacing=" << p.m_beforeSpacing << "%,";
if (p.m_afterSpacing<0)
o << "aftSpacing=" << -p.m_afterSpacing << "pt,";
else if (p.m_afterSpacing>0)
o << "aftSpacing=" << p.m_afterSpacing << "%,";
o << p.m_extra;
return o;
}
//! the before spacing ( negative in point, positive in percent )
float m_beforeSpacing;
//! the after spacing ( negative in point, positive in percent )
float m_afterSpacing;
//! some extra data
std::string m_extra;
};
/** Internal: class to store a font/para modifier with appear in text data */
struct DataModifier {
//! constructor
DataModifier()
: m_color(MWAWColor::black())
, m_extra("")
{
for (auto &data : m_data) data=0xFFFF;
}
//! returns the superscript value ( negative in pt, position in li)
float getSuper() const
{
return float(int32_t((m_data[0]<<16)|m_data[1]))/65536.f;
}
//! returns the sub value ( negative in pt, position in li)
float getSub() const
{
return float(int32_t((m_data[2]<<16)|m_data[3]))/65536.f;
}
//! returns the border id
int getBorderId() const
{
return m_data[2]==0xFFFF?0:m_data[2];
}
//! returns the document extra id
int getDocParaId() const
{
return m_data[3];
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, DataModifier const &m)
{
if (!m.m_color.isBlack())
o << "col=" << m.m_color << ",";
if (m.m_data[1]!=0xFFFF)
o << "sup=" << m.getSuper() << ",";
if (m.m_data[3]!=0xFFFF)
o << "subs=" << m.getSub() << ",";
for (int i=0; i<2; i++) {
if (m.m_data[i] != 0xFFFF)
o << "f" << i << "=" << std::hex << m.m_data[i] << std::dec << ",";
}
if (m.m_data[2] && m.m_data[2]<20)
o << "bordId=" << m.m_data[2] << ",";
if (m.m_data[3] && m.m_data[3]<20)
o << "paraId=" << m.m_data[3] << ",";
o << m.m_extra;
return o;
}
//! the color
MWAWColor m_color;
//! the data
int m_data[4];
//! extra data
std::string m_extra;
};
struct Zone;
/** Internal: class to store an item state */
struct Item {
/** the different type of id */
enum Type { Father=0, Child, Next, Prev, Main };
/** constructor */
Item()
: m_level(0)
, m_index(1)
, m_collapsed(false)
, m_hidden(false)
, m_childList()
, m_hiddenZone()
, m_extra("")
{
for (auto &structId : m_structId) structId=0;
}
//! return a value which can be used to represent the label(changme)
std::string label() const
{
if (m_level <= 0) return "";
std::stringstream s;
s << m_index << ". ";
return s.str();
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Item const &it)
{
if (it.m_hidden) o << "hidden,";
if (it.m_collapsed) o << "collapsed,";
if (it.m_level) o << "level=" << it.m_level << ",";
if (it.m_index!=1) o << "index=" << it.m_index << ",";
char const *wh[5]= {"father", "child", "next", "prev", "main"};
o << "zId=[";
for (int i = 0; i < 5; i++) {
if (it.m_structId[i]) o << wh[i] << "=" << it.m_structId[i] << ",";
}
o << "],";
o << it.m_extra;
return o;
}
//! the level
int m_level;
//! the actual index
int m_index;
//! true if the item is hidden
bool m_collapsed;
//! true if the item is hidden
bool m_hidden;
//! the list of childlist
std::vector<int> m_childList;
//! the hidden item zone
std::shared_ptr<Zone> m_hiddenZone;
//! the item id in text struct zone ( father, child, next, prev, main )
int m_structId[5];
//! extra data
std::string m_extra;
};
/** Internal: class to store a font and it state */
struct Font {
/** constructor */
Font()
: m_font()
, m_modifier()
, m_defModifier(true)
, m_item()
{
for (auto &state : m_state) state=false;
}
/** update the font using the modifier */
void update();
/** the font */
MWAWFont m_font;
/** the rendering state */
bool m_state[128];
/** the modifier data */
DataModifier m_modifier;
/** a flag to know if the data modifier is default */
bool m_defModifier;
/** the index */
Item m_item;
};
void Font::update()
{
if (m_state[9])
m_font.setColor(m_modifier.m_color);
else
m_font.setColor(MWAWColor(0,0,0));
if (m_state[0xa]) {
if (m_defModifier)
m_font.set(MWAWFont::Script::super100());
else {
float sup = m_modifier.getSuper();
if (sup < 0)
m_font.set(MWAWFont::Script(float(-sup),librevenge::RVNG_POINT));
else
m_font.set(MWAWFont::Script(float(sup*100.f),librevenge::RVNG_PERCENT));
}
}
else if (m_state[0xb]) {
if (m_defModifier)
m_font.set(MWAWFont::Script::sub100());
else {
float sub = m_modifier.getSub();
if (sub < 0)
m_font.set(MWAWFont::Script(float(sub),librevenge::RVNG_POINT));
else
m_font.set(MWAWFont::Script(float(-sub*100.f),librevenge::RVNG_PERCENT));
}
}
else
m_font.set(MWAWFont::Script());
}
/** Internal: class to store the LineHeader */
struct LineHeader {
/** Constructor */
LineHeader()
: m_numChar(0)
, m_font()
, m_fontSet(false)
, m_height(-1.0)
, m_prevHeight(-1.0)
, m_textIndent(0)
, m_extra("")
{
}
//! try to find the line height using m_height or m_prevHeight
float height() const
{
if (m_height > 0) return m_height;
return m_prevHeight;
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, LineHeader const &line)
{
o << "numC=" << line.m_numChar << ",";
if (line.m_fontSet)
o << "font=[fId=" << line.m_font.id()
<< ",fSize=" << line.m_font.size() << "],";
if (line.m_height > 0) o << "height=" << line.m_height << ",";
if (line.m_textIndent.isSet()) o << "textIndent=" << *line.m_textIndent << ",";
o << line.m_extra;
return o;
}
/** the number of char */
int m_numChar;
/** the font */
MWAWFont m_font;
/** a flag to know if the font is set */
bool m_fontSet;
/** the line height in point ( if known) */
float m_height;
/** the previous line height in point ( if known) */
float m_prevHeight;
/** the text indent in inches ( if known) */
MWAWVariable<double> m_textIndent;
/** extra data */
std::string m_extra;
};
/** Internal: class to store a ColumnInfo */
struct ColumnInfo {
ColumnInfo()
: m_column(0)
, m_box()
, m_beginPos(1)
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, ColumnInfo const &c)
{
if (c.m_column > 0) o << "col=" << c.m_column+1 << ",";
o << c.m_box << ",";
if (c.m_beginPos > 1) o << "textPos=" << c.m_beginPos << ",";
return o;
}
//! the column number
int m_column;
//! the bdbox
MWAWBox2i m_box;
//! the first data
int m_beginPos;
};
struct PageInfo {
PageInfo()
: m_page(-1)
, m_columns()
{
}
//! returns true if the page has same color position
bool isSimilar(PageInfo const &p) const
{
size_t numColumns = m_columns.size();
if (numColumns != p.m_columns.size())
return false;
for (size_t c = 0; c < numColumns; c++) {
if (m_columns[c].m_box[0].x() != p.m_columns[c].m_box[0].x())
return false;
if (m_columns[c].m_box[1].x() != p.m_columns[c].m_box[1].x())
return false;
}
return true;
}
//! return a section
MWAWSection getSection() const
{
MWAWSection sec;
size_t numC = m_columns.size();
if (numC <= 1)
return sec;
sec.m_columns.resize(numC);
for (size_t c=0; c < numC; c++) {
MWAWSection::Column &col=sec.m_columns[c];
int prevPos= c ? (m_columns[c].m_box[0].x()+m_columns[c-1].m_box[1].x())/2 :
m_columns[c].m_box[0].x();
int nextPos= c+1!=numC ? (m_columns[c+1].m_box[0].x()+m_columns[c].m_box[1].x())/2 :
m_columns[c].m_box[1].x();
col.m_width = double(nextPos-prevPos);
col.m_widthUnit = librevenge::RVNG_POINT;
col.m_margins[libmwaw::Left]=double(m_columns[c].m_box[0].x()-prevPos)/72.;
col.m_margins[libmwaw::Right]=double(nextPos-m_columns[c].m_box[1].x())/72.;
}
return sec;
}
//! the pages
int m_page;
//! the columns
std::vector<ColumnInfo> m_columns;
};
/** Internal: class to store a text zone */
struct Zone {
//! the zone type
enum ZoneType { Normal, Main, CollapsedItem };
//! constructor
Zone()
: m_zone()
, m_box()
, m_begin(-1)
, m_end(-1)
, m_zoneType(Normal)
, m_pagesInfo()
, m_extra("")
{
for (auto &fl : m_flags) fl=0;
for (auto &p : m_pages) p=0;
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Zone const &z)
{
switch (z.m_zoneType) {
case Zone::Normal:
break;
case Zone::Main:
o << "Main,";
break;
case Zone::CollapsedItem:
o << "Collapsed[item],";
break;
#if !defined(__clang__)
default:
o << "#type=" << z.m_zoneType << ",";
break;
#endif
}
if (z.m_zone) o << *z.m_zone << ",";
if (z.m_pages[0]) o << "firstP=" << z.m_pages[0] << ",";
if (z.m_pages[1] && z.m_pages[1] != z.m_pages[0])
o << "lastP=" << z.m_pages[1] << ",";
o << "Box=" << z.m_box << ",";
for (int i = 0; i < 2; i++) {
if (z.m_flags[i])
o << "fl" << i << "=" << z.m_flags[i] << ",";
}
if (z.m_end!=z.m_begin)
o << "sz=" << std::hex << z.m_end-z.m_begin << std::dec << ",";
if (z.m_extra.length())
o << "extra=[" << z.m_extra << "],";
return o;
}
//! return the col/page break
std::vector<int> getBreaksPosition() const
{
int prevPos = 0;
std::vector<int> res;
for (auto const &page : m_pagesInfo) {
for (auto const &column : page.m_columns) {
int pos = column.m_beginPos;
if (pos < prevPos) {
MWAW_DEBUG_MSG(("FullWrtTextInternal::Zone::getBreaksPosition pos go back\n"));
break;
}
res.push_back(pos);
prevPos = pos;
}
}
return res;
}
//! the main zone
FullWrtStruct::EntryPtr m_zone;
//! the bdbox
MWAWBox2f m_box;
//! the beginning of the text data
long m_begin;
//! the end of the text data
long m_end;
//! the zone type
ZoneType m_zoneType;
//! the zone flags, header|footer, normal|extra
int m_flags[2];
//! the pages
int m_pages[2];
//! the pages info
std::vector<PageInfo> m_pagesInfo;
//! the extra data ( for debugging )
std::string m_extra;
};
/** Internal: class to store the paragraph properties */
struct Paragraph final : public MWAWParagraph {
//! Constructor
Paragraph()
: MWAWParagraph()
, m_align(0)
, m_interSpacing(1.)
, m_interSpacingUnit(librevenge::RVNG_PERCENT)
, m_dim(0,0)
, m_border()
, m_isTable(false)
, m_tableBorderId(0)
, m_tableFlags()
, m_actCol(-1)
, m_isSent(false)
{
for (auto &space : m_befAftSpacings) space=0;
}
//! destructor
~Paragraph() final;
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind)
{
if (ind.m_isTable) o << "table,";
if (ind.m_tableBorderId) o << "borderId[table]=" << ind.m_tableBorderId << ",";
if (ind.m_align) o << "align=" << ind.m_align << ",";
if (ind.m_dim[0]>0 || ind.m_dim[1]>0) o << "dim=" << ind.m_dim << ",";
o << static_cast<MWAWParagraph const &>(ind);
return o;
}
//! returns true if this is a table
bool isTable() const
{
return m_isTable;
}
//! set the align type
void setAlign(int align)
{
m_align = align;
m_isSent = false;
}
//! set the interline spacing
void setInterlineSpacing(double spacing, librevenge::RVNGUnit unit)
{
m_interSpacing = spacing;
m_interSpacingUnit = unit;
m_isSent = false;
}
//! set the before/after spacing ( negative in point, positive in percent )
void setSpacings(double spacing, bool before)
{
m_befAftSpacings[before ? 0 : 1] = spacing;
m_isSent = false;
}
//! set the border type
void setBorder(FullWrtStruct::Border const &border)
{
m_border = border;
m_isSent = false;
}
//! update the paragraph data from a ruler
void updateFromRuler(Paragraph const &ruler)
{
MWAWParagraph::operator=(ruler);
m_isTable = ruler.m_isTable;
m_tableBorderId = ruler.m_tableBorderId;
m_tableFlags = ruler.m_tableFlags;
m_dim = ruler.m_dim;
m_isSent = false;
}
//! returns the table dimension in points
bool getTableDimensions(std::vector<float> &dim) const
{
size_t numTabs = m_tabs->size();
if ((numTabs%2) != 1 || numTabs != m_tableFlags.size()) {
MWAW_DEBUG_MSG(("FullWrtTextInternal::Paragraph:getTableDimensions: unexpected number of tabs\n"));
return false;
}
if (m_dim[0] <= 0) {
MWAW_DEBUG_MSG(("FullWrtTextInternal::Paragraph:getTableDimensions: can not determine line width\n"));
return false;
}
std::vector<double> limits;
limits.push_back(*m_margins[1]);
for (size_t i=1; i < numTabs; i+=2) {
if (m_tableFlags[i] != 4) {
MWAW_DEBUG_MSG(("FullWrtTextInternal::Paragraph:getTableDimensions: find unexpected tables flags\n"));
return false;
}
limits.push_back((*m_tabs)[i].m_position);
}
limits.push_back(double(m_dim[0])-*m_margins[2]);
dim.resize(limits.size()-1);
for (size_t i=0; i < dim.size(); i++)
dim[i] = 72.f*float(limits[i+1]-limits[i]);
return true;
}
//! update the paragraph data to be sent to a listener
MWAWParagraph updateToSent() const
{
m_isSent = true;
MWAWParagraph res = *this;
if (m_interSpacing>0)
res.setInterline(m_interSpacing, m_interSpacingUnit);
for (int i = 0; i < 2; i++) {
if (m_befAftSpacings[i] <= 0)
res.m_spacings[i+1]=(-m_befAftSpacings[i])/72.0;
else // ok suppose line of height 9
res.m_spacings[i+1]=(m_befAftSpacings[i])*9.0/72.0;
}
if (!m_border.m_backColor.isWhite())
res.m_backgroundColor = m_border.m_backColor;
if (m_isTable && m_actCol >= 0) {
if (2*m_actCol < int(m_tableFlags.size())) {
switch (m_tableFlags[size_t(2*m_actCol)]) {
case 0:
res.m_justify = MWAWParagraph::JustificationLeft;
break;
case 1:
res.m_justify = MWAWParagraph::JustificationCenter;
break;
case 2:
case 5:
res.m_justify = MWAWParagraph::JustificationFull;
break;
case 3:
res.m_justify = MWAWParagraph::JustificationRight;
break;
case 6:
res.m_justify = MWAWParagraph::JustificationFullAllLines;
break;
default:
break;
}
}
res.m_tabs->resize(0);
m_actCol=-1;
return res;
}
switch (m_align) {
case 0:
res.m_justify = MWAWParagraph::JustificationLeft;
break;
case 1:
res.m_justify = MWAWParagraph::JustificationCenter;
break;
case 2:
res.m_justify = MWAWParagraph::JustificationFull;
break;
case 3:
res.m_justify = MWAWParagraph::JustificationRight;
break;
default:
break;
}
res.m_borders = m_border.getParagraphBorders();
return res;
}
//! the align value
int m_align;
//! the spacing
double m_interSpacing;
//! the spacing unit
librevenge::RVNGUnit m_interSpacingUnit;
//! the before/after spacing ( negative in point, positive in percent)
double m_befAftSpacings[2];
//! the zone dimension
MWAWVec2f m_dim;
//! the actual border
FullWrtStruct::Border m_border;
//! a flag to know if this is a table
bool m_isTable;
//! the table border id
int m_tableBorderId;
//! the list of table limit
std::vector<int> m_tableFlags;
//! the index of the actual column to send
mutable int m_actCol;
//! a flag to know if the parser is send or not
mutable bool m_isSent;
};
Paragraph::~Paragraph()
{
}
////////////////////////////////////////
//! Internal: the state of a FullWrtText
struct State {
//! constructor
State()
: m_version(-1)
, m_entryMap()
, m_paragraphMap()
, m_itemMap()
, m_dataModMap()
, m_paragraphModList()
, m_mainZones()
, m_numPages(1)
, m_actualPage(0)
{
}
//! the file version
mutable int m_version;
//! zoneId -> entry
std::multimap<int, std::shared_ptr<Zone> > m_entryMap;
//! rulerId -> ruler
std::map<int, Paragraph> m_paragraphMap;
//! itemId -> item
std::map<int, Item> m_itemMap;
//! modId -> font/paragraph modifier
std::map<int, DataModifier> m_dataModMap;
//! a list of paragraph modifier
std::vector<ParaModifier> m_paragraphModList;
//! the main zone index
std::vector<int> m_mainZones;
int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
};
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
FullWrtText::FullWrtText(FullWrtParser &parser)
: m_parserState(parser.getParserState())
, m_state(new FullWrtTextInternal::State)
, m_mainParser(&parser)
{
}
FullWrtText::~FullWrtText()
{
}
int FullWrtText::version() const
{
if (m_state->m_version < 0)
m_state->m_version = m_parserState->m_version;
return m_state->m_version;
}
int FullWrtText::numPages() const
{
return m_state->m_numPages;
}
////////////////////////////////////////////////////////////
// Intermediate level
////////////////////////////////////////////////////////////
void FullWrtText::send(std::shared_ptr<FullWrtTextInternal::Zone> zone, int numChar,
FullWrtTextInternal::Font &font, FullWrtTextInternal::Paragraph &ruler,
std::string &str)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) return;
MWAWInputStreamPtr input = zone->m_zone->m_input;
long pos = input->tell();
long endPos = pos+numChar;
bool nextIsChar = false;
bool fCharSent = false, lastEOL=false, fontSet = false, checkModifier = false;
libmwaw::DebugStream f;
for (int i = 0; i < numChar; i++) {
long actPos = input->tell();
if (actPos >= endPos)
break;
auto val = static_cast<int>(input->readULong(1));
bool done = false;
int id=-1;
if (nextIsChar)
nextIsChar = false;
else {
done = true;
uint32_t fFlags = font.m_font.flags();
bool on = false;
if (val >= 0x80)
on=font.m_state[val-0x80]=!font.m_state[val-0x80];
std::string onString(on?"":"/");
switch (val) {
case 0:
val=' ';
done = false;
break; // space
case 0x80:
f << "[" << onString << "80]";
break; // often found by pair consecutively : selection ?
case 0x81:
f << "[" << onString << "81]";
break; // often found by pair around a " " a ","...
case 0x82:
f << "[" << onString << "82]";
break; // find one time around a first capital letter I
case 0x83:
f << "[" << onString << "b]";
fFlags ^= MWAWFont::boldBit;
font.m_font.setFlags(fFlags);
fontSet=false;
break;
case 0x84:
f << "[" << onString << "i]";
fFlags ^= MWAWFont::italicBit;
font.m_font.setFlags(fFlags);
fontSet=false;
break;
case 0x86:
f << "[" << onString << "outline]";
fFlags ^= MWAWFont::outlineBit;
font.m_font.setFlags(fFlags);
fontSet=false;
break;
case 0x87:
f << "[" << onString << "shadow]";
fFlags ^= MWAWFont::shadowBit;
font.m_font.setFlags(fFlags);
fontSet=false;
break;
case 0x88:
f << "[" << onString << "smallcap]";
fFlags ^= MWAWFont::smallCapsBit;
font.m_font.setFlags(fFlags);
fontSet=false;
break;
case 0x89:
f << "[" << onString << "color]";
checkModifier=true;
break;
case 0x8a:
f << "[" << onString << "super]";
checkModifier=true;
break;
case 0x8b: // subs
f << "[" << onString << "sub]";
checkModifier=true;
break;
case 0x8c:
f << "[" << onString << "strike]";
if (on)
font.m_font.setStrikeOutStyle(MWAWFont::Line::Simple);
else
font.m_font.setStrikeOutStyle(MWAWFont::Line::None);
fontSet=false;
break;
case 0x90: // condensed
case 0x91: // expand
f << "[" << onString << (val==0x90?"condensed":"expand") << "]";
font.m_font.setDeltaLetterSpacing
(float((font.m_state[0x10]?-1:0)+(font.m_state[0x11]?1:0)));
fontSet=false;
break;
case 0x85: // normal underline
case 0x8e: // word underline
case 0x8f: // double
case 0x92: { // dot
switch (val) {
case 0x85:
f << "[" << onString << "underline]";
break;
case 0x8e:
f << "[" << onString << "underline:word]";
break;
case 0x8f:
f << "[" << onString << "underline:double]";
break;
default:
case 0x92:
f << "[" << onString << "underline:dot]";
break;
}
if (font.m_state[0x12])
font.m_font.setUnderlineStyle(MWAWFont::Line::Dot);
else if (font.m_state[0x5]||font.m_state[0xe]|| font.m_state[0xf])
font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
else
font.m_font.setUnderlineStyle(MWAWFont::Line::None);
if (font.m_state[0xf])
font.m_font.setUnderlineType(MWAWFont::Line::Double);
if (font.m_state[0xe])
font.m_font.setUnderlineWordFlag(true);
fontSet=false;
break;
}
case 0x93:
f << "[" << onString << "overline]";
if (on)
font.m_font.setOverlineStyle(MWAWFont::Line::Simple);
else
font.m_font.setOverlineStyle(MWAWFont::Line::None);
fontSet=false;
break;
case 0x94:
f << "[" << onString << "allCaps]";
fFlags ^= MWAWFont::uppercaseBit;
font.m_font.setFlags(fFlags);
fontSet=false;
break;
case 0x95:
f << "[" << onString << "lowercase]";
fFlags ^= MWAWFont::lowercaseBit;
font.m_font.setFlags(fFlags);
fontSet=false;
break;
case 0xa7: // fixme appear also as tabs separator
f << "[tab]";
listener->insertTab();
break;
case 0xac:
// we must wait sending font/ruler then we will send the data
done = false;
break;
case 0xae: // insecable-
f << "-";
val=0x2011;
done = false;
break;
case 0xb2:
if (actPos+1 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not find font size!!!!\n"));
f << "[##fSz]";
break;
}
font.m_font.setSize(float(input->readULong(1)));
f << "[fSz=" << font.m_font.size() << "]";
fontSet=false;
break;
case 0xb3:
nextIsChar = true;
break;
case 0xc1:
if (actPos+2 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not find font id!!!!\n"));
f << "[##fId]";
break;
}
font.m_font.setId(static_cast<int>(input->readULong(2)));
f << "[fId=" << font.m_font.size() << "]";
fontSet = false;
break;
case 0xc7: // item id
if (actPos+2 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not find item id!!!!\n"));
f << "[##item]";
break;
}
if (font.m_item.m_collapsed && font.m_item.m_childList.size()) {
if (!lastEOL)
listener->insertEOL(false);
auto hFont = font;
auto hRuler = ruler;
for (auto item : font.m_item.m_childList)
sendHiddenItem(item, hFont, hRuler);
fontSet = false;
ruler.m_isSent=false;
lastEOL=false;
}
id = static_cast<int>(input->readULong(2));
if (m_state->m_itemMap.find(id) == m_state->m_itemMap.end()) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not find item id!!!!\n"));
font.m_item = FullWrtTextInternal::Item();
f << "[#itemId=" << id << "]";
}
else {
font.m_item =m_state->m_itemMap.find(id)->second;
f << "[item:" << font.m_item << ",id=" << id << "]";
}
break;
case 0xcb: { // space/and or color
if (actPos+2 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not read data modifier!!!!\n"));
f << "[##dMod]";
break;
}
id = static_cast<int>(input->readULong(2));
if (m_state->m_dataModMap.find(id) == m_state->m_dataModMap.end()) {
font.m_modifier=FullWrtTextInternal::DataModifier();
font.m_defModifier=true;
f << "[#modifier=" << id << "]";
}
else {
font.m_modifier=m_state->m_dataModMap.find(id)->second;
f << "[modifier=" << font.m_modifier << "]";
font.m_defModifier=false;
}
// can be a paragraph modifier
if (fCharSent) break;
int pId = font.m_modifier.getDocParaId();
if (pId >= 0 && pId < int(m_state->m_paragraphModList.size())) {
auto const ¶Mod = m_state->m_paragraphModList[size_t(pId)];
ruler.setSpacings(double(paraMod.m_beforeSpacing), true);
ruler.setSpacings(double(paraMod.m_afterSpacing), false);
ruler.setBorder(FullWrtStruct::Border());
checkModifier = true;
}
int bId = font.m_modifier.getBorderId();
FullWrtStruct::Border border;
if (m_mainParser->getBorder(bId, border)) {
ruler.setBorder(border);
checkModifier = true;
}
break;
}
case 0xd2:
case 0xd3:
case 0xd5:
case 0xdc:
case 0xe1:
case 0xe2:
case 0xe4:
if (actPos+2 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not an id for %x!!!!\n", static_cast<unsigned int>(val)));
f << "[#" << std::hex << val << std::dec << "]";
break;
}
id = static_cast<int>(input->readULong(2));
done = false; // we must wait sending font/ruler then we will send the data
switch (val) {
case 0xd2:
f << "[noteId=" << id << "]";
break;
case 0xd3:
f << "[footnoteId=" << id << "]";
break;
case 0xd5:
f << "[endnoteId=" << id << "]";
break;
case 0xdc:
f << "[graphId=" << id << "]";
break;
case 0xe1: // reference field
done=true;
f << "[refDefId=" << id << "]";
break;
case 0xe2:
f << "[refId=" << id << "]";
break;
case 0xe4:
f << "[variableId=" << id << "]";
break;
default:
break;
}
break;
case 0xa0: // potential hypen break
f << "[hyp]";
if (actPos+1==endPos) {
val = '-';
done = false;
}
break;
case 0x98: // soft break
f << "\\n";
done = false;
break;
case 0x96: // field end
f << "[FEnd]";
break;
case 0x9b: // align
case 0x9c:
ruler.setAlign(ruler.m_align^(val-0x9a));
f << "[" << onString << "align" << (val-0x9b) << "]";
break;
case 0x9d: // justication
case 0x9e:
case 0x9f: {
double justify=1.0+double(val-0x9d)/2.0;
ruler.setInterlineSpacing(justify, librevenge::RVNG_PERCENT);
f << "[just=" << 100*justify << "%]";
break;
}
case 0xa4:
f << "emptyItem?,";
break;
case 0xa8:
f << "\\n";
done = false;
break;
case 0xab: // begin of line
f << "\\{";
break;
case 0xc6: { // ruler id
if (actPos+2 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can find rulerId!!!!\n"));
f << "[##rulerId]";
break;
}
id = int(input->readULong(2));
f << "[P" << id << "]";
auto it=m_state->m_paragraphMap.find(id);
if (it==m_state->m_paragraphMap.end()) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not find paragraph with id=%d\n", id));
break;
}
ruler.updateFromRuler(it->second);
// do not add a break line for a simple ruler
if (i<=1)
lastEOL=true;
break;
}
case 0xe8: // justification
if (actPos+4 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not find justify!!!!\n"));
f << "[#justWithType]";
}
else {
auto just = static_cast<int>(input->readLong(2));
if (just < 0)
ruler.setInterlineSpacing(-just, librevenge::RVNG_POINT);
f << "[just=" << just << ",";
f << "typ?=" << static_cast<int>(input->readLong(2)) << "]";
}
break; // ok
case 0x8d: // unknown
case 0x97:
case 0x9a:
case 0xa1:
case 0xa9:
case 0xaa:
f << "[" << onString << std::hex << val << std::dec << "]";
break;
case 0xca: // unknown
case 0xcd:
case 0xd0: // header
case 0xd1: // footer ?
case 0xd4: // contents
case 0xd6: // biblio
case 0xd7: // entry
case 0xd9:
case 0xda:
case 0xe5: // contents/index data
if (actPos+2 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can find id for %x data!!!!\n", static_cast<unsigned int>(val)));
f << "[##" << std::hex << val << std::dec << "]";
break;
}
id = static_cast<int>(input->readULong(2));
if (val==0xd0) f << "[headerId=" << id << "]";
else if (val==0xd1) f << "[footerId=" << id << "]";
else f << "[" << std::hex << val << std::dec << "Id=" << val << "]";
break;
case 0xe9:
if (actPos+4 > endPos) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not find id/val for fe9!!!!\n"));
f << "[#e9]";
break;
}
id = int(input->readULong(2));
f << "[e9=" << id << ":" << input->readULong(2) << "]";
break;
default:
done = false;
break;
}
}
if (checkModifier && (!done || input->tell() == endPos)) {
font.update();
fontSet = false;
}
if (done)
continue;
if (!fCharSent && !ruler.m_isSent)
listener->setParagraph(ruler.updateToSent());
if (!fontSet) {
listener->setFont(font.m_font);
fontSet = true;
}
fCharSent = true;
lastEOL = false;
// now, we can try to send the data
if (id >= 0 || val==0xa8 || val==0x98 || val==0xac) {
done = true;
switch (val) {
case 0x98:
listener->insertEOL(true);
break;
case 0xa8:
lastEOL = true;
listener->insertEOL();
break;
case 0xac: {
std::string label=font.m_item.label();
if (label.length())
listener->insertUnicodeString(librevenge::RVNGString(label.c_str()));
break;
}
case 0xd2:
m_mainParser->sendText(id, libmwaw::DOC_COMMENT_ANNOTATION);
break;
case 0xd3:
m_mainParser->sendText(id, libmwaw::DOC_NOTE, MWAWNote::FootNote);
break;
case 0xd5:
m_mainParser->sendText(id, libmwaw::DOC_NOTE, MWAWNote::EndNote);
break;
case 0xdc:
m_mainParser->sendGraphic(id);
break;
case 0xe2:
m_mainParser->sendReference(id);
break;
case 0xe4:
m_mainParser->sendVariable(id);
break;
default:
done = false;
break;
}
}
if (done)
continue;
if (val >= 256)
listener->insertUnicode(static_cast<uint32_t>(val));
else {
i += listener->insertCharacter
(static_cast<unsigned char>(val), input, input->tell()+(numChar-1-i));
if (val <= 0x1f)
f << "#[" << std::hex << val << "]";
else
f << char(val);
}
}
if (!fCharSent && !ruler.m_isSent)
listener->setParagraph(ruler.updateToSent());
if (!lastEOL)
listener->insertEOL(false);
str=f.str();
}
bool FullWrtText::sendTable(std::shared_ptr<FullWrtTextInternal::Zone> zone, FullWrtTextInternal::LineHeader const &lHeader,
FullWrtTextInternal::Font &font, FullWrtTextInternal::Paragraph &ruler, std::string &str)
{
std::vector<float> dim;
if (!ruler.getTableDimensions(dim))
return false;
float height=lHeader.height();
if (height <= 0) {
MWAW_DEBUG_MSG(("FullWrtText::sendTable: can not find table height\n"));
return false;
}
size_t numCols = dim.size();
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) return false;
MWAWInputStreamPtr input = zone->m_zone->m_input;
long pos = input->tell();
long endPos=pos+lHeader.m_numChar;
std::vector<long> cellPos;
cellPos.push_back(pos);
for (int i = 0; i < lHeader.m_numChar; i++) {
long actPos = input->tell();
if (input->isEnd())
break;
auto c = static_cast<int>(input->readULong(1));
if (c==0xa7) {
cellPos.push_back(actPos);
cellPos.push_back(actPos+1);
}
// special case: an item implies a column shift
if (c==0xac) {
cellPos.push_back(actPos+1);
cellPos.push_back(actPos+1);
}
}
cellPos.push_back(endPos);
size_t numFind=cellPos.size()/2;
if (numCols < numFind) {
if (numCols+1==numFind) {
// happens one type at the end of file: bug or normal end table?
MWAW_DEBUG_MSG(("FullWrtText::sendTable: find a spurious column break in last line position\n"));
cellPos.resize(2*numCols);
}
else {
MWAW_DEBUG_MSG(("FullWrtText::sendTable: find too many columns\n"));
return false;
}
}
// ok, we have the limit of the text, let works...
libmwaw::DebugStream f;
MWAWTable table(MWAWTable::TableDimBit);
table.setColsSize(dim);
listener->openTable(table);
listener->openTableRow(-height, librevenge::RVNG_POINT);
MWAWBorder outBorder, vBorder;
FullWrtStruct::Border border;
if (m_mainParser->getBorder(ruler.m_tableBorderId, border)) {
outBorder=FullWrtStruct::Border::getBorder(border.m_type[0]);
vBorder=FullWrtStruct::Border::getBorder(border.m_type[2]);
outBorder.m_color=vBorder.m_color=border.m_color[0];
}
else {
MWAW_DEBUG_MSG(("FullWrtText::sendTable: can not find border=%d\n",ruler.m_tableBorderId));
outBorder.m_width=vBorder.m_width=0;
}
for (size_t col = 0; col < numCols; col++) {
MWAWCell cell;
MWAWVec2i cellPosition(MWAWVec2i(0,static_cast<int>(col)));
cell.setPosition(cellPosition);
if (ruler.m_tableBorderId) {
cell.setBorders(0xf, outBorder);
if (col > 0)
cell.setBorders(libmwaw::LeftBit, vBorder);
if (col+1 < numCols)
cell.setBorders(libmwaw::RightBit, vBorder);
}
listener->openTableCell(cell);
if (col < numFind) {
if (cellPos[2*col+1]>cellPos[2*col]) {
std::string string;
input->seek(cellPos[2*col], librevenge::RVNG_SEEK_SET);
ruler.m_actCol=int(col);
ruler.m_isSent=false;
send(zone, int(cellPos[2*col+1]-cellPos[2*col]), font, ruler, string);
f << string;
}
}
if (col+1 != numCols)
f << "[col]";
listener->closeTableCell();
}
listener->closeTableRow();
listener->closeTable();
input->seek(endPos, librevenge::RVNG_SEEK_SET);
str=f.str();
return true;
}
bool FullWrtText::readLineHeader(std::shared_ptr<FullWrtTextInternal::Zone> zone, FullWrtTextInternal::LineHeader &lHeader)
{
lHeader = FullWrtTextInternal::LineHeader();
MWAWInputStreamPtr input = zone->m_zone->m_input;
libmwaw::DebugStream f;
long pos = input->tell();
auto type = static_cast<int>(input->readULong(2));
int lengthSz = 1;
if (type & 0x8000)
lengthSz = 2;
lHeader.m_numChar = static_cast<int>(input->readULong(lengthSz));
if ((lengthSz==1 && (lHeader.m_numChar & 0x80)) ||
pos+2+lHeader.m_numChar > zone->m_end) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
int val;
if (type & 0x4000) {
f << "f0=[";
lHeader.m_height = float(input->readLong(4))/256.f;
val = static_cast<int>(input->readLong(2)); // 0 or -1
if (val == -1) f << "*,";
else if (val) f << "unkn2=" << val << ",";
val = static_cast<int>(input->readLong(2)); // small number between 20 and -20?
if (val) f << "N1=" << float(val)/256.0f << ",";
val = static_cast<int>(input->readLong(2));
if (val)
lHeader.m_textIndent = double(val)/72.;
f << "w=" << static_cast<int>(input->readLong(2)) << ",";
f << "],";
}
if (type & 0x2000) {
// small number between 1 and 4
f << "f1=" << static_cast<int>(input->readLong(1)) << ",";
}
if (type & 0x1000) {
// small number between 1 and 2
f << "f2=" << static_cast<int>(input->readLong(1)) << ",";
}
if (type & 0x800) {
// small number between 1 and 2
f << "f3=" << static_cast<int>(input->readLong(1)) << ",";
}
if (type & 0x400) {
// small number 1
f << "f4=" << static_cast<int>(input->readLong(1)) << ",";
}
if (type & 0x200) {
// small int between 0 and 0x4f : ident ?
f << "f5=" << static_cast<int>(input->readLong(2)) << ",";
}
if (type & 0x100) {
// small int between 0 and 0xb0 : ident ?
f << "f6=" << static_cast<int>(input->readLong(2)) << ",";
}
if (type & 0x80) {
// small int between 0 and 0x5b : ident ?
f << "f7=" << static_cast<int>(input->readLong(2)) << ",";
}
if (type & 0x40) {
// small int between 0 and 0xcf : link to rulerid in data struct?
val = static_cast<int>(input->readLong(2));
if (val)
f << "P" << val << ",";
}
if (type & 0x20) {
/* first small number:
0|10|80|81, 0|1|2|4|10,
last small number 0|1|...|7|e|10|11|20|21|23|40|41
&1 line continue ?
&2 new page ?
*/
f << "f9=[";
for (int i = 0; i < 4; i++) {
val = static_cast<int>(input->readULong(1));
if (val) f << std::hex << val << std::dec << ",";
else f << "_,";
}
f << "],";
}
if (type & 0x10) {
auto fId = static_cast<int>(input->readLong(2));
auto fSz = float(input->readULong(2));
lHeader.m_fontSet = true;
lHeader.m_font.setId(fId);
lHeader.m_font.setSize(fSz);
f << "id=" << fId << ",";
f << "sz=" << fSz << ",";
// negative: point, positive li
f << "justify=" << float(input->readLong(4))/65336.f << ",";
}
if (type & 0x8) { // font flag ?
val = static_cast<int>(input->readULong(2));
f << "fa=" << std::hex << val << std::dec << ",";
}
if (type & 0x4) {
MWAW_DEBUG_MSG(("FullWrtText::readLineHeader: find unknown size flags!!!!\n"));
f << "[#fl&4]";
// we do not know the size of this field, let try with 2
input->seek(2, librevenge::RVNG_SEEK_CUR);
}
if (type & 0x2) { // 0 or 2
val = static_cast<int>(input->readULong(2));
f << "fb=" << val << ",";
}
if (type & 0x1) { // small number between 1 and 1b
val = static_cast<int>(input->readLong(2));
f << "nRows?=" << val << ",";
}
lHeader.m_extra = f.str();
return true;
}
bool FullWrtText::send(std::shared_ptr<FullWrtTextInternal::Zone> zone, MWAWColor fontColor)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) {
MWAW_DEBUG_MSG(("FullWrtText::send can not find the listener\n"));
return false;
}
MWAWInputStreamPtr input = zone->m_zone->m_input;
libmwaw::DebugFile &ascii = zone->m_zone->getAsciiFile();
libmwaw::DebugStream f;
zone->m_zone->setParsed(true);
long pos = zone->m_begin;
input->seek(pos, librevenge::RVNG_SEEK_SET);
int num=1;
FullWrtTextInternal::Font font;
font.m_font=MWAWFont(3,12);
font.m_font.setColor(fontColor);
FullWrtTextInternal::Paragraph ruler;
if (zone->m_zone->m_type==0x11 || zone->m_zone->m_type==0x12)
ruler.setAlign(1);
auto listBreaks = zone->getBreaksPosition();
auto numBreaks = int(listBreaks.size());
auto nPages = int(zone->m_pagesInfo.size());
int actBreak = numBreaks, actBreakPos = -1;
if (numBreaks) {
actBreak = 0;
actBreakPos = listBreaks[size_t(actBreak)];
}
int actPage = 0, actCol = 0, numCol=1;
listener->setParagraph(ruler);
float prevHeight = -1;
while (1) {
pos = input->tell();
bool sendData = false;
f.str("");
f << "TextData-a[" << num << "]:";
while (num==actBreakPos) {
if (num != 1) sendData = true;
if (actCol < numCol-1 && numCol > 1) {
listener->insertBreak(MWAWTextListener::ColumnBreak);
actCol++;
}
else if (actPage >= nPages) {
MWAW_DEBUG_MSG(("FullWrtText::send can not find the page information\n"));
}
else {
auto const &page = zone->m_pagesInfo[size_t(actPage)];
if (sendData) {
if (zone->m_zoneType == FullWrtTextInternal::Zone::Main)
m_mainParser->newPage(++m_state->m_actualPage);
else if (numCol > 1)
listener->insertBreak(MWAWTextListener::ColumnBreak);
}
actCol = 0;
if (!actPage || !page.isSimilar(zone->m_pagesInfo[size_t(actPage-1)])) {
auto section = page.getSection();
libmwaw::SubDocumentType subdocType;
int numC = section.numColumns();
if (listener->isSubDocumentOpened(subdocType) && numC <=1
&& subdocType != libmwaw::DOC_TEXT_BOX)
;
else {
if (listener->isSectionOpened())
listener->closeSection();
listener->openSection(section);
numCol = numC;
}
}
actPage++;
}
if (num != 1) f << "break,";
sendData = true;
// update numbreaks
if (++actBreak < numBreaks)
actBreakPos = listBreaks[size_t(actBreak)];
else
actBreakPos = -1;
}
num++;
FullWrtTextInternal::LineHeader lHeader;
if (!readLineHeader(zone, lHeader)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
f << lHeader;
// update prevHeight
lHeader.m_prevHeight = prevHeight;
prevHeight = lHeader.height();
if (lHeader.m_fontSet) {
font.m_font.setId(lHeader.m_font.id());
font.m_font.setSize(lHeader.m_font.size());
}
long debPos=input->tell();
if (lHeader.m_numChar)
ascii.addDelimiter(debPos,'|');
long lastPos = debPos+lHeader.m_numChar;
if (listener) {
std::string str;
if (!ruler.isTable() || !sendTable(zone, lHeader, font, ruler, str)) {
str="";
input->seek(debPos, librevenge::RVNG_SEEK_SET);
send(zone, lHeader.m_numChar, font, ruler, str);
}
f << str;
}
input->seek(lastPos, librevenge::RVNG_SEEK_SET);
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
if (long(input->tell()) >= zone->m_end)
break;
}
return true;
}
bool FullWrtText::sendHiddenItem(int id, FullWrtTextInternal::Font &font, FullWrtTextInternal::Paragraph &ruler)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) {
MWAW_DEBUG_MSG(("FullWrtText::sendHiddenItem can not find the listener\n"));
return false;
}
if (m_state->m_itemMap.find(id)==m_state->m_itemMap.end()) {
MWAW_DEBUG_MSG(("FullWrtText::sendHiddenItem: can not find item %d\n", id));
return false;
}
auto &item = m_state->m_itemMap.find(id)->second;
if (!item.m_hidden) {
MWAW_DEBUG_MSG(("FullWrtText::sendHiddenItem: item %d is not hidden\n", id));
return false;
}
// avoid loop if pb
font.m_item=FullWrtTextInternal::Item(); // no previous item
item.m_hidden = false; // item is send
std::shared_ptr<FullWrtTextInternal::Zone> zone=item.m_hiddenZone;
if (!zone) {
MWAW_DEBUG_MSG(("FullWrtText::sendHiddenItem can not find the hidden zone\n"));
return false;
}
MWAWInputStreamPtr input = zone->m_zone->m_input;
libmwaw::DebugFile &ascii = zone->m_zone->getAsciiFile();
libmwaw::DebugStream f;
long pos = zone->m_begin;
input->seek(pos, librevenge::RVNG_SEEK_SET);
f << "ItemData[Collapsed]:";
auto val = static_cast<int>(input->readULong(1)); // alway 40 ?
if (val != 0x40)
f << "#type=" << val << ",";
val = static_cast<int>(input->readULong(1)); // find number between 3 and 7, a unique id ?
if (val) f << "id=" << val << ",";
val = static_cast<int>(input->readULong(2)); // number between 2 and 48
if (val) f << "f0=" << val << ",";
for (int i = 0; i < 4; i++) { // f1=3, f2=0|c0, f3=1, f4=0|1
val = static_cast<int>(input->readULong(1));
if (val) f << "f" << i+1 << "=" << std::hex << val << std::dec << ",";
}
for (int i = 0; i < 3; i++) { // g0=0, g1=1, g2=0
val = static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
val = static_cast<int>(input->readLong(1)); // always 0?
if (val) f << "g3=" << val << ",";
font.m_font.setSize(float(input->readULong(1)));
font.m_font.setId(static_cast<int>(input->readULong(2)));
for (int i = 0; i < 2; i++) { //g4=5|6|45, g5=0
val = static_cast<int>(input->readULong(1));
if (val) f << "g" << 4+i << "=" << std::hex << val << std::dec << ",";
}
for (int i = 0; i < 3; i++) { // always 0 or color?
val = static_cast<int>(input->readLong(2));
if (val) f << "h" << i << "=" << std::hex << val << std::dec << ",";
}
for (int i = 0; i < 5; i++) {
val = static_cast<int>(input->readULong(2));
MWAWColor col;
if (FullWrtStruct::getColor(val, col)) f << "col" << i << "=" << col << ",";
}
for (int i = 0; i < 2; i++) { // h3=id?, h4=id?
val = static_cast<int>(input->readLong(2));
if (val) f << "h" << i+3 << "=" << std::hex << val << std::dec << ",";
}
input->seek(4, librevenge::RVNG_SEEK_CUR); // skip size...
auto numChar=int(zone->m_end-(pos+44));
if (numChar)
ascii.addDelimiter(pos+44,'|');
listener->setParagraph(ruler);
std::string str;
send(zone, numChar, font, ruler, str);
f << str;
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
for (auto child : item.m_childList)
sendHiddenItem(child, font, ruler);
return true;
}
////////////////////////////////////////////////////////////
// read the text data
bool FullWrtText::readTextData(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &ascii = zone->getAsciiFile();
int vers = version();
long pos = zone->begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
bool knownZone = (zone->m_type >= 0xa && zone->m_type <= 0x14) || (zone->m_type == 0x18);
auto header = static_cast<int>(input->readULong(2));
bool hasHeader = false;
if (!knownZone && (header&0xFC00)) return false;
std::shared_ptr<FullWrtTextInternal::Zone> text(new FullWrtTextInternal::Zone);
text->m_zoneType = (zone->m_type==0xa) ?
FullWrtTextInternal::Zone::Main : FullWrtTextInternal::Zone::Normal;
text->m_zone = zone;
int val;
if (!header) { // maybe the main zone
bool ok = true;
// find f0 in [1,270], f1 in [1,d2]
for (int i = 0; i < 2 && ok ; i++) {
val = static_cast<int>(input->readLong(2));
if (val <= 0 || (!i && val >= 0x800) || (i && val >= 0x200))
ok = false;
}
for (int i = 0; i < 2 && ok; i++) {
val = static_cast<int>(input->readULong(1));
if (val>1) ok=false;
}
val = ok ? static_cast<int>(input->readLong(1)) : 0;
if (val && !knownZone)
ok=false;
val = ok ? static_cast<int>(input->readLong(1)) : 0;
if (val < 1 || val > 20)
ok=false;
if (!ok) { // force to try as attachment
header = 1;
input->seek(pos+2, librevenge::RVNG_SEEK_SET);
}
else if (!knownZone)
text->m_zoneType = FullWrtTextInternal::Zone::Main;
}
if (header) { // attachement
hasHeader = true;
for (int i = 0; i < 2; i++) {
val = static_cast<int>(input->readLong(2));
if (val <= 0 || val > 2) return false;
}
val = static_cast<int>(input->readLong(vers==1 ? 1 : 2));
if (val < 0 || val > 4) return false;
if (vers==2) {
if (val== 0) return false;
val = static_cast<int>(input->readLong(2));
if (val) return false;
}
if (!knownZone)
text->m_zoneType = FullWrtTextInternal::Zone::Main;
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
if (hasHeader) {
//find 8, 1c, 71, 76, 8a, 99, 9d, c6, c9, ce, f3, f4
f << "N0=" << static_cast<int>(input->readLong(2)) << ",";
for (int i = 0; i < 2; i++) { // f0=f1=1 ? f2=1/2
val = static_cast<int>(input->readLong(2));
if (val != 1) f << "f" << i << "=" << val << ",";
}
val = static_cast<int>(input->readLong(vers==1 ? 1 : 2));
if (val != 1) f << "f2=" << val << ",";
if (vers == 1) {
ascii.addDelimiter(input->tell(),'|');
input->seek(59, librevenge::RVNG_SEEK_CUR);
ascii.addDelimiter(input->tell(),'|');
}
}
val = static_cast<int>(input->readLong(2)); // always 0 ?
if (val) f << "f3=" << val << ",";
/* N[0] related to number line ? N[1] between 1 and 5e */
int N[2];
for (int i = 0; i < 2; i++) {
N[i] = static_cast<int>(input->readLong(2));
if (N[i])
f << "N" << i+1 << "=" << std::hex << N[i] << std::dec << ",";
}
// two flag header/footer : 0|1, normal/other: 0|1?
for (int i = 0; i < 2; i++) {
text->m_flags[i] = static_cast<int>(input->readLong(1));
if (text->m_flags[i])
f << "fl" << i << "=" << text->m_flags[i] << ",";
}
for (int i=0; i < 2; i++) { // f4=0|27, f5=small number 1/2/f
val = static_cast<int>(input->readLong(1));
if (val!=1) f << "f" << i+4 << "=" << val << ",";
}
// between 1 and 202 ( very similar to dim[2]
auto dimUnk = static_cast<int>(input->readLong(2));
// two flags 0|ce|fa, 0
for (int i = 3; i < 5; i++) {
val = static_cast<int>(input->readLong(1));
if (val)
f << "fl" << i << "=" << val << ",";
}
// between 8 and 58
f << "N3=" << static_cast<int>(input->readLong(2)) << ",";
int dim[4]; // box with almost always y1=y0+1?
for (auto &d : dim) d=static_cast<int>(input->readLong(2));
if (dimUnk != dim[2]) f << "dimUnk=" << dimUnk << ",";
text->m_box=MWAWBox2f(MWAWVec2f(float(dim[0]),float(dim[1])),MWAWVec2f(float(dim[2]),float(dim[3])));
text->m_pages[1] = static_cast<int>(input->readLong(2));
if (text->m_pages[1] == 16000) text->m_pages[1] = 0;
text->m_pages[0] = static_cast<int>(input->readLong(2));
if (text->m_pages[0] == 16000) text->m_pages[0] = text->m_pages[1];
for (int i = 0; i < 2; i++) { // 1, 2 or 16000=infty?
val = static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
text->m_extra = f.str();
f.str("");
f << "Entries(TextData):";
f << *text;
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
if (long(input->tell()) >= zone->end())
return false;
text->m_begin = input->tell();
#if DEBUGII
int numLines=0;
#endif
while (1) {
pos = input->tell();
if (pos+2 >= zone->end()) break;
auto type = static_cast<int>(input->readULong(2));
if (input->isEnd()) {
MWAW_DEBUG_MSG(("FullWrtText::readTextData: internal problem, unexpected EOS!!!\n"));
return false;
}
int lengthSz = 1;
if (type & 0x8000)
lengthSz = 2;
auto numChar = static_cast<int>(input->readULong(lengthSz));
if ((lengthSz==1 && (numChar & 0x80)) ||
long(input->tell()+numChar) > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
#if DEBUGII
numLines++;
#endif
int sz = 0;
if (type & 0x4000) sz+=12;
if (type & 0x2000) sz+=1;
if (type & 0x1000) sz+=1;
if (type & 0x800) sz+=1;
if (type & 0x400) sz+=1;
if (type & 0x200) sz+=2;
if (type & 0x100) sz+=2;
if (type & 0x80) sz+=2;
if (type & 0x40) sz+=2;
if (type & 0x20) sz+=4;
if (type & 0x10) sz+=8;
if (type & 0x8) sz+=2;
if (type & 0x4) {
MWAW_DEBUG_MSG(("FullWrtText::readTextData: find unknown size flags!!!!\n"));
sz+=2;
}
if (type & 0x2) sz+=2;
if (type & 0x1) sz+=2;
if (numChar || sz)
input->seek(numChar+sz, librevenge::RVNG_SEEK_CUR);
}
#if DEBUGII
std::cout << "FIND:N=" << numLines << " [" << N[0] << "]\n";
#endif
pos = text->m_end = input->tell();
// ok, we can insert the data
auto it = m_state->m_entryMap.find(zone->id());
if (it != m_state->m_entryMap.end()) {
MWAW_DEBUG_MSG(("FullWrtText::readTextData: entry %d already exists\n", zone->id()));
}
m_state->m_entryMap.insert
(std::multimap<int, std::shared_ptr<FullWrtTextInternal::Zone> >::value_type(zone->id(), text));
f.str("");
f << "TextData-b:";
val = static_cast<int>(input->readULong(2));
if (val || static_cast<int>(input->readULong(1)) != 0xFF) {
f << "##";
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
return true;
}
f << "numCols=" << static_cast<int>(input->readLong(2)) << ",";
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
int actPage = 0;
while (1) {
pos = input->tell();
if (pos >= zone->end()) break;
val = static_cast<int>(input->readULong(2));
int sz = 0;
if ((val%50)==30) {
int num = val/50;
sz = 32+num*52;
input->seek(9, librevenge::RVNG_SEEK_CUR);
if (pos+sz > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
FullWrtTextInternal::PageInfo page;
page.m_page = actPage++;
ascii.addPos(pos);
ascii.addNote("TextData-c[ColH]");
pos+=32;
input->seek(pos, librevenge::RVNG_SEEK_SET);
for (int i = 0; i < num; i++) {
pos = input->tell();
f.str("");
f << "TextData-c[ColDef]:";
FullWrtTextInternal::ColumnInfo col;
col.m_column = i;
col.m_beginPos = static_cast<int>(input->readLong(2));
for (auto &d : dim) d = static_cast<int>(input->readLong(2));
col.m_box = MWAWBox2i(MWAWVec2i(dim[0],dim[2]), MWAWVec2i(dim[1], dim[3]));
f << col;
page.m_columns.push_back(col);
ascii.addDelimiter(input->tell(),'|');
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
input->seek(pos+52, librevenge::RVNG_SEEK_SET);
}
text->m_pagesInfo.push_back(page);
continue;
}
// ok, probably link to a col field, but...
// let try to correct
int high = (val>>8);
if (high==0x21)
sz = 42;
else if (high==0x61||high==0x63) {
input->seek(12, librevenge::RVNG_SEEK_CUR);
auto numData = static_cast<int>(input->readULong(2));
if (!numData) break;
sz = 26+9*numData;
}
else if (high==0xe1) {
input->seek(14, librevenge::RVNG_SEEK_CUR);
auto numData = static_cast<int>(input->readULong(2));
if (!numData) break;
sz = 30+9*numData;
}
else if (high==0 && (val%50)!=30)
sz=26;
if (sz == 0 || pos+sz > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
ascii.addPos(pos);
ascii.addNote("TextData-d:");
input->seek(pos+sz, librevenge::RVNG_SEEK_SET);
}
if (input->tell() < zone->end()) {
ascii.addPos(input->tell());
ascii.addNote("TextData-d:");
}
return true;
}
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
bool FullWrtText::readItem(FullWrtStruct::EntryPtr zone, int id, bool hidden)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &ascii = zone->getAsciiFile();
libmwaw::DebugStream f;
long pos = input->tell();
if (pos+25 > zone->end())
return false;
FullWrtTextInternal::Item item;
item.m_hidden = hidden;
int val;
int numOk = 0, numBad = 0;
int numZone = m_mainParser->getNumDocZoneStruct();
for (int i = 0; i < 4; i++) {
val = static_cast<int>(input->readLong(2));
if (val < 0 || val >= numZone) {
numBad++;
f << "#id" << i << "=" << val << ",";
continue;
}
item.m_structId[i]=val;
if (val) numOk++;
}
val = static_cast<int>(input->readLong(2));
if (val > 0) {
item.m_index=val;
numOk++;
}
else {
f << "#index=" << val << ",";
numBad++;
}
for (int i = 0; i < 2; i++) { // f1=[1-4]:listId?,f2=0
val = static_cast<int>(input->readLong(2));
if (!val) continue;
if (val < -100||val > 100) {
numBad++;
f << "#";
}
else
numOk++;
f << "f" << i << "=" << std::hex << val << std::dec << ",";
}
if (numBad > numOk) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
val = static_cast<int>(input->readULong(1)); // 68 or e8
if (val!=0x68)
f << "fl=" << std::hex << val << std::dec << ",";
item.m_level = static_cast<int>(input->readLong(2));
val = static_cast<int>(input->readULong(1)); // always 0
if (val)
f << "fl1=" << val << ",";
item.m_structId[FullWrtTextInternal::Item::Main] = static_cast<int>(input->readLong(2));
if (id > 0 && id != item.m_structId[FullWrtTextInternal::Item::Main]) {
numBad+=3;
f << "###id,";
}
for (int i = 1; i < 3; i++) { // always 0 ?
val = static_cast<int>(input->readLong(2));
if (!val) continue;
if (i > 1 && (val < -100 || val > 100)) {
f << "#";
numBad++;
}
else
numOk++;
f << "g" << i << "=" << std::hex << val << std::dec << ",";
}
if (numBad >= numOk) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
val = static_cast<int>(input->readULong(1)); // always 0
if (val)
f << "fl2=" << val << ",";
item.m_extra=f.str();
f.str("");
f << "Entries(ItemData):" << item;
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
bool set = false;
if (id < 0) ;
else if (m_state->m_itemMap.find(id) == m_state->m_itemMap.end()) {
m_state->m_itemMap.insert
(std::map<int,FullWrtTextInternal::Item>::value_type(id,item));
set=true;
}
else {
MWAW_DEBUG_MSG(("FullWrtText::readItem: id %d already exists\n", id));
}
if (!hidden)
return true;
pos = input->tell();
if (pos+44 > zone->end() || input->readULong(1)!=0x40) {
MWAW_DEBUG_MSG(("FullWrtText::readItem: can not find hidden data\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return true;
}
input->seek(pos+42, librevenge::RVNG_SEEK_SET);
auto sz = static_cast<int>(input->readULong(2));
if (pos+44+sz > zone->end()) {
MWAW_DEBUG_MSG(("FullWrtText::readItem: find bad data size\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return true;
}
if (set && m_state->m_itemMap.find(id) != m_state->m_itemMap.end()) {
auto &theItem = m_state->m_itemMap.find(id)->second;
theItem.m_hiddenZone.reset(new FullWrtTextInternal::Zone);
theItem.m_hiddenZone->m_zone = zone;
theItem.m_hiddenZone->m_zoneType = FullWrtTextInternal::Zone::CollapsedItem;
theItem.m_hiddenZone->m_begin = pos;
theItem.m_hiddenZone->m_end = pos+44+sz;
}
if (sz)
input->seek(sz, librevenge::RVNG_SEEK_CUR);
return true;
}
////////////////////////////////////////////////////////////
// read a style
bool FullWrtText::readStyle(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &ascii = zone->getAsciiFile();
libmwaw::DebugStream f;
long pos=input->tell();
auto sz = int(input->readLong(2));
if (sz < 4 || sz >= 0x100) return false;
if (pos+2+sz> zone->end()) return false;
f.str("");
f << "Entries(Style):";
for (int i=0; i<2; ++i) { //f0: small number, f1: number between 0 and 1c: flag?
auto val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
if (sz==4) {
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
return true;
}
if (sz!=0x46) {
f << "###";
MWAW_DEBUG_MSG(("FullWrtText::readStyle: style length seems odd\n"));
input->seek(pos+2+sz, librevenge::RVNG_SEEK_SET);
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
return true;
}
auto nSz=static_cast<int>(input->readULong(1));
if (!nSz || nSz>0x1f) {
f << "###";
MWAW_DEBUG_MSG(("FullWrtText::readStyle: style name length seems odd\n"));
input->seek(pos+2+sz, librevenge::RVNG_SEEK_SET);
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
return true;
}
std::string name("");
for (int c=0; c<nSz; ++c)
name += char(input->readLong(1));
f << name << ",";
input->seek(pos+38, librevenge::RVNG_SEEK_SET);
ascii.addDelimiter(input->tell(),'|');
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
input->seek(pos+2+sz, librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// read the paragraph data
bool FullWrtText::readParagraphTabs(FullWrtStruct::EntryPtr zone, int id)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &ascii = zone->getAsciiFile();
libmwaw::DebugStream f;
int const vers = version();
const int dataSz = vers==1 ? 14 : 10;
const int headerSz = vers==1 ? 24 : 30;
long pos = input->tell();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto sz = long(input->readULong(4));
if (sz<24 || pos+4+sz > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
f.str("");
f << "Entries(Ruler):";
if (id >= 0) f << "P" << id << ",";
int val;
for (int i = 0; i < 2; i++) { // find f0 in 0 c0, f1=0|2
val = static_cast<int>(input->readULong(1));
if (val) f << "fl" << i << "=" << std::hex << val << std::dec << ",";
}
for (int i = 0; i < 2; i++) { // find f0=0|1|2: align ?, f1=0-7
val = static_cast<int>(input->readULong(2));
if (val) f << "f" << i << "=" << val << ",";
}
FullWrtTextInternal::Paragraph para;
float dim[2];
for (float &i : dim) // dim0: height?, dim1:width
i = float(input->readULong(2))/72.f;
para.m_dim=MWAWVec2f(dim[1],dim[0]);
double margins[3];
char const *margNames[] = { "left", "right", "first" };
for (int i = 0; i < 3; i++) {
margins[i] = double(input->readLong(4))/65536./72.0;
if (margins[i] < 0 || margins[i] > 0)
f << "margins[" << margNames[i] << "]=" << margins[i] << ",";
}
para.m_margins[0] = margins[2]-margins[0];
para.m_margins[1] = margins[0];
if (margins[1] < double(dim[1]))
para.m_margins[2]=double(dim[1])-margins[1];
ascii.addDelimiter(input->tell(), '|');
input->seek(vers==1 ? pos+27 : pos+26, librevenge::RVNG_SEEK_SET);
auto N = static_cast<int>(input->readULong(1));
if (headerSz+dataSz *N != sz) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
f << "N=" << N << ",";
if (vers==2) {
for (int i=0; i < 3; i++) {
val = static_cast<int>(input->readULong(1));
if (val) f << "g" << i << "=" << val << ",";
}
if (input->readULong(4)) {
para.m_isTable = true;
f << "table,";
}
input->seek(pos+4+30, librevenge::RVNG_SEEK_SET);
}
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
for (int i = 0; i < N; i++) {
pos = input->tell();
f.str("");
f << "Ruler:Tabs-" << i << ":";
MWAWTabStop tab;
val = static_cast<int>(input->readULong(1));
switch (val>>5) {
case 0:
break;
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 4:
f << "end[table],";
break;
case 5:
f << "justify[table],";
break;
case 6:
f << "justifyAll[table],";
break;
// 4: tableend, 5: justify(begintable), 6: justifyall(begin table)
default:
f << "##tab[type]=" << (val>>5) << ",";
break;
}
para.m_tableFlags.push_back(val>>5);
if (val & 0x1F) f << "#type=" << std::hex << (val & 0x1F) << std::dec << ",";
val = static_cast<int>(input->readULong(1));
if (val) {
int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(val));
if (unicode==-1)
tab.m_decimalCharacter = uint16_t(val);
else
tab.m_decimalCharacter = uint16_t(unicode);
}
tab.m_position = double(input->readLong(4))/65536./72.;
val = static_cast<int>(input->readLong(2));
if (val) f << "repeat=" << val/256.;
val = static_cast<int>(input->readULong(1));
switch (val) {
case 0x1: // fixme: . or dotted
tab.m_leaderCharacter = '.';
break;
case 0x20: // space, ie. none
case 0:
break;
default: {
int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(val));
if (unicode==-1)
tab.m_leaderCharacter = uint16_t(val);
else
tab.m_leaderCharacter = uint16_t(unicode);
break;
}
}
val = static_cast<int>(input->readULong(1));
if (val) f << "#f2=" << std::hex << val << std::dec << ",";
f << tab;
para.m_tabs->push_back(tab);
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
input->seek(pos+dataSz, librevenge::RVNG_SEEK_SET);
}
if (para.m_isTable) {
pos = input->tell();
sz = input->readLong(4);
f.str("");
f << "Ruler[Table:end]:";
if (sz==0x24 && pos+4+sz <= zone->end()) {
for (int i=0; i<11; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val)
f << "f" << i << "=" << val << ",";
}
para.m_tableBorderId=static_cast<int>(input->readLong(2));
if (para.m_tableBorderId)
f << "B" << para.m_tableBorderId-1 << ",";
ascii.addDelimiter(input->tell(), '|');
input->seek(pos+4+0x24, librevenge::RVNG_SEEK_SET);
}
else {
MWAW_DEBUG_MSG(("FullWrtText::readParagraphTabs: can not find table data\n"));
f << "###";
input->seek(pos, librevenge::RVNG_SEEK_SET);
}
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
}
if (id < 0) ;
else if (m_state->m_paragraphMap.find(id) == m_state->m_paragraphMap.end())
m_state->m_paragraphMap.insert
(std::map<int,FullWrtTextInternal::Paragraph>::value_type(id,para));
else {
MWAW_DEBUG_MSG(("FullWrtText::readParagraphTabs: id %d already exists\n", id));
}
return true;
}
/* read font/para modifier in text */
bool FullWrtText::readDataMod(FullWrtStruct::EntryPtr zone, int id)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &ascii = zone->getAsciiFile();
libmwaw::DebugStream f;
long pos = input->tell();
input->seek(pos, librevenge::RVNG_SEEK_SET);
if (pos+10 > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
FullWrtTextInternal::DataModifier mod;
auto val = int(input->readULong(2));
MWAWColor col;
if (FullWrtStruct::getColor(val, col))
mod.m_color = col;
else if (val != 0xFFFF)
f << "#col=" << std::hex << val << std::dec << ",";
for (auto &data : mod.m_data) data = int(input->readULong(2));
mod.m_extra = f.str();
f.str("");
f << "Entries(DataMod):" << mod;
if (m_state->m_dataModMap.find(id) == m_state->m_dataModMap.end())
m_state->m_dataModMap.insert
(std::map<int, FullWrtTextInternal::DataModifier>::value_type(id, mod));
else {
MWAW_DEBUG_MSG(("FullWrtText::readDataMod: Oops id %d already find\n", id));
}
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
return true;
}
/* read para modifier in doc info */
bool FullWrtText::readParaModDocInfo(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
libmwaw::DebugStream f;
long pos = input->tell();
if (input->readULong(4)!=0x65787472 || input->readULong(1)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
long blckSz = input->readLong(4);
long endData = pos+9+blckSz;
int num = static_cast<int>(input->readULong(2)), val;
int const fSz = 18;
f << "Entries(ParaMod):N=" << num << ",";
if (blckSz < 2 || blckSz != 2 + num*fSz || endData > zone->end()) {
MWAW_DEBUG_MSG(("FullWrtText::readParaModDocInfo::readCitationDocInfo: problem reading the data block or the number of data\n"));
f << "###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (endData <= zone->end()) {
input->seek(endData, librevenge::RVNG_SEEK_SET);
return true;
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
m_state->m_paragraphModList.push_back(FullWrtTextInternal::ParaModifier());
for (int i = 0; i < num; i++) {
f.str("");
pos = input->tell();
FullWrtTextInternal::ParaModifier mod;
for (int j = 0; j < 2; j++) {
val = int(input->readLong(2));
if (val==-1)
continue;
if (j==0)
mod.m_beforeSpacing = float(val)/256.f;
else
mod.m_afterSpacing = float(val)/256.f;
}
for (int j = 0; j < 3; j++) { // always ffff
val = static_cast<int>(input->readLong(2));
if (val != -1)
f << "f" << j << "=" << val << ",";
}
for (int j = 0; j < 4; j++) { // always 0
val = static_cast<int>(input->readLong(2));
if (val)
f << "f" << j+3 << "=" << val << ",";
}
mod.m_extra = f.str();
m_state->m_paragraphModList.push_back(mod);
f.str("");
f << "ParaMod-" << i << ":" << mod;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+fSz, librevenge::RVNG_SEEK_SET);
}
return true;
}
////////////////////////////////////////////////////////////
// read the column data
bool FullWrtText::readColumns(FullWrtStruct::EntryPtr zone)
{
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &ascii = zone->getAsciiFile();
libmwaw::DebugStream f;
long pos = input->tell();
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto sz = long(input->readULong(4));
if (sz<34 || pos+4+sz > zone->end()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
input->seek(13, librevenge::RVNG_SEEK_CUR);
auto N = static_cast<int>(input->readULong(1));
if (24+10*N != sz) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
f.str("");
f << "Entries(Columns):" << N;
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
input->seek(pos+4+24, librevenge::RVNG_SEEK_SET);
for (int i = 0; i < N; i++) {
pos = input->tell();
f.str("");
f << "Columns-" << i << ":";
int dim[2]; // checkme: pos as float?
dim[0] = static_cast<int>(input->readLong(2));
auto val = long(input->readULong(2));
if (val) f << "f0=" << std::hex << val << std::dec << ",";
dim[1] = static_cast<int>(input->readLong(2));
f << "pos=" << dim[0] << "<->" << dim[1] << ",";
val = long(input->readULong(2));
if (val) f << "f1=" << std::hex << val << std::dec << ",";
ascii.addDelimiter(input->tell(),'|');
ascii.addPos(pos);
ascii.addNote(f.str().c_str());
input->seek(pos+10, librevenge::RVNG_SEEK_SET);
}
return true;
}
////////////////////////////////////////////////////////////
//! send data to the listener
bool FullWrtText::sendMainText()
{
if (m_state->m_mainZones.empty()) {
MWAW_DEBUG_MSG(("FullWrtText::sendMainText: can not find main zone\n"));
return false;
}
if (!m_parserState->m_textListener) return true;
for (auto id : m_state->m_mainZones) {
auto it = m_state->m_entryMap.find(id);
if (it == m_state->m_entryMap.end() || !it->second) {
MWAW_DEBUG_MSG(("FullWrtText::sendMainText: can not find main zone: internal problem\n"));
continue;
}
m_mainParser->newPage(++m_state->m_actualPage);
send(it->second);
}
return true;
}
int FullWrtText::getHeaderFooterId(bool header, int page, int &numSimilar) const
{
int type=header ? 0x11 : 0x12;
if (m_state->m_mainZones.empty()) {
numSimilar=1;
if (m_state->m_numPages>page)
numSimilar=m_state->m_numPages-page+1;
return -1;
}
int nextPage=-1;
int res=-1;
for (auto it : m_state->m_entryMap) {
int id=it.first;
auto zone=it.second;
if (!zone || !zone->m_zone || zone->m_zone->m_type!=type ||
zone->m_pages[0]<page)
continue;
if (zone->m_pages[1]>=page)
res = id;
else if (nextPage==-1 || zone->m_pages[0]<nextPage)
nextPage=zone->m_pages[0];
}
if (nextPage==-1) nextPage=m_state->m_numPages+1;
numSimilar=nextPage-page;
if (numSimilar<=0) numSimilar=1;
return res;
}
bool FullWrtText::send(int zId, MWAWColor fontColor)
{
auto it = m_state->m_entryMap.find(zId);
if (it == m_state->m_entryMap.end() || !it->second) {
MWAW_DEBUG_MSG(("FullWrtText::send: can not find zone: %d\n", zId));
return false;
}
send(it->second, fontColor);
return true;
}
void FullWrtText::flushExtra()
{
for (auto it : m_state->m_entryMap) {
std::shared_ptr<FullWrtTextInternal::Zone> &zone = it.second;
if (!zone || !zone->m_zone || zone->m_zone->isParsed())
continue;
send(zone);
}
}
void FullWrtText::sortZones()
{
int numZones = 0, totalNumPages = 0;
std::vector<int> pagesLimits;
for (auto it : m_state->m_entryMap) {
auto &zone = it.second;
if (!zone || !zone->m_zone || zone->m_zoneType != FullWrtTextInternal::Zone::Main)
continue;
int fPage = zone->m_pages[0], lPage = zone->m_pages[1];
if (lPage < fPage) {
MWAW_DEBUG_MSG(("FullWrtText::sortZones: last page is inferior to firstPage\n"));
lPage = fPage;
}
int pos = 0;
while (pos < numZones) {
if (fPage < pagesLimits[size_t(2*pos)])
break;
if (fPage == pagesLimits[size_t(2*pos)] && lPage <= pagesLimits[size_t(2*pos+1)])
break;
pos++;
}
pagesLimits.resize(size_t(2*numZones+2));
m_state->m_mainZones.resize(size_t(numZones+1));
for (int i = numZones-1; i > pos-1; i--) {
pagesLimits[size_t(2*i+2)]=pagesLimits[size_t(2*i)];
pagesLimits[size_t(2*i+3)]=pagesLimits[size_t(2*i+1)];
m_state->m_mainZones[size_t(i+1)]=m_state->m_mainZones[size_t(i)];
}
m_state->m_mainZones[size_t(pos)] = zone->m_zone->id();
pagesLimits[size_t(2*pos)] = fPage;
pagesLimits[size_t(2*pos+1)] = lPage;
numZones++;
int nPages = (lPage-fPage)+1;
if (nPages < int(zone->m_pagesInfo.size())) {
MWAW_DEBUG_MSG(("FullWrtText::sortZones: pages limit seems odd!!!\n"));
nPages = int(zone->m_pagesInfo.size());
}
totalNumPages += nPages;
}
m_state->m_numPages = totalNumPages;
}
void FullWrtText::createItemStructures()
{
for (auto it=m_state->m_itemMap.begin(); it != m_state->m_itemMap.end(); ++it) {
auto &item = it->second;
int childId=item.m_structId[FullWrtTextInternal::Item::Child];
int id=item.m_structId[FullWrtTextInternal::Item::Main];
if (childId<=0)
continue;
int prevId=0;
std::set<int> seens;
while (childId>0) {
if (seens.find(childId)!=seens.end()) {
MWAW_DEBUG_MSG(("FullWrtText::createItemStructures:find loop\n"));
break;
}
seens.insert(childId);
auto it2=m_state->m_itemMap.find(childId);
if (it2==m_state->m_itemMap.end()) {
MWAW_DEBUG_MSG(("FullWrtText::createItemStructures: can not find child: %d\n", childId));
break;
}
FullWrtTextInternal::Item &child = it2->second;
if (child.m_structId[FullWrtTextInternal::Item::Father] != id ||
child.m_structId[FullWrtTextInternal::Item::Prev] != prevId) {
MWAW_DEBUG_MSG(("FullWrtText::createItemStructures: find unexpected child %d\n", childId));
break;
}
item.m_childList.push_back(childId);
if (child.m_hidden) item.m_collapsed=true;
prevId=childId;
childId=child.m_structId[FullWrtTextInternal::Item::Next];
}
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: