/* -*- 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 <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <set>
#include <sstream>
#include <librevenge/librevenge.h>
#include "MWAWCell.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWGraphicEncoder.hxx"
#include "MWAWGraphicListener.hxx"
#include "MWAWGraphicShape.hxx"
#include "MWAWGraphicStyle.hxx"
#include "MWAWPictMac.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSubDocument.hxx"
#include "MWAWTable.hxx"
#include "MWAWTextListener.hxx"
#include "HanMacWrdJParser.hxx"
#include "HanMacWrdJGraph.hxx"
/** Internal: the structures of a HanMacWrdJGraph */
namespace HanMacWrdJGraphInternal
{
////////////////////////////////////////
//! a cell format in HanMacWrdJGraph
struct CellFormat {
public:
//! constructor
CellFormat()
: m_backColor(MWAWColor::white())
, m_borders()
, m_extra("")
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, CellFormat const &frmt)
{
if (!frmt.m_backColor.isWhite())
o << "backColor=" << frmt.m_backColor << ",";
char const *what[] = {"T", "L", "B", "R"};
for (size_t b = 0; b < frmt.m_borders.size(); b++)
o << "bord" << what[b] << "=[" << frmt.m_borders[b] << "],";
o << frmt.m_extra;
return o;
}
//! the background color
MWAWColor m_backColor;
//! the border: order defined by MWAWBorder::Pos
std::vector<MWAWBorder> m_borders;
//! extra data
std::string m_extra;
};
////////////////////////////////////////
//! a table cell in a table in HanMacWrdJGraph
struct TableCell final : public MWAWCell {
//! constructor
explicit TableCell(long tId)
: MWAWCell()
, m_zId(0)
, m_tId(tId)
, m_cPos(-1)
, m_fileId(0)
, m_formatId(0)
, m_flags(0)
, m_extra("")
{
}
//! use cell format to finish updating cell
void update(CellFormat const &format);
//! call when the content of a cell must be send
bool sendContent(MWAWListenerPtr listener, MWAWTable &table) final;
//! operator<<
friend std::ostream &operator<<(std::ostream &o, TableCell const &cell);
//! the cell zone id
long m_zId;
//! the cell text zone id
long m_tId;
//! the first character position in m_zId
long m_cPos;
//! the file id
long m_fileId;
//! the cell format id
int m_formatId;
//! the cell flags
int m_flags;
//! extra data
std::string m_extra;
};
void TableCell::update(CellFormat const &format)
{
setBackgroundColor(format.m_backColor);
static int const wh[] = { libmwaw::LeftBit, libmwaw::RightBit, libmwaw::TopBit, libmwaw::BottomBit};
for (size_t b = 0; b < format.m_borders.size(); b++)
setBorders(wh[b], format.m_borders[b]);
if (hasExtraLine() && format.m_borders.size()>=2) {
MWAWBorder extraL;
extraL.m_width=format.m_borders[1].m_width;
extraL.m_color=format.m_borders[1].m_color;
setExtraLine(extraLine(), extraL);
}
}
std::ostream &operator<<(std::ostream &o, TableCell const &cell)
{
o << static_cast<MWAWCell const &>(cell);
if (cell.m_flags&0x100) o << "justify[full],";
if (cell.m_flags&0x800) o << "lock,";
if (cell.m_flags&0x1000) o << "merge,";
if (cell.m_flags&0x2000) o << "inactive,";
if (cell.m_flags&0xC07F)
o << "#linesFlags=" << std::hex << (cell.m_flags&0xC07F) << std::dec << ",";
if (cell.m_zId > 0)
o << "cellId=" << std::hex << cell.m_zId << std::dec << "[" << cell.m_cPos << "],";
if (cell.m_formatId > 0)
o << "formatId=" << std::hex << cell.m_formatId << std::dec << ",";
o << cell.m_extra;
return o;
}
////////////////////////////////////////
//! Internal: the table of a HanMacWrdJGraph
struct Table final : public MWAWTable {
//! constructor
explicit Table(HanMacWrdJGraph &parser)
: MWAWTable(MWAWTable::CellPositionBit|MWAWTable::TableDimBit)
, m_parser(&parser)
, m_rows(1)
, m_columns(1)
, m_height(0)
, m_textFileId(0)
, m_formatsList()
{
}
//! destructor
~Table() final;
//! update all cells using the formats list
void updateCells();
//! send a text zone
bool sendText(long id, long cPos) const
{
return m_parser->sendText(id, cPos);
}
//! the graph parser
HanMacWrdJGraph *m_parser;
//! the number of row
int m_rows;
//! the number of columns
int m_columns;
//! the table height
int m_height;
//! the text file id
long m_textFileId;
//! a list of cell format
std::vector<CellFormat> m_formatsList;
private:
Table(Table const &orig) = delete;
Table &operator=(Table const &orig) = delete;
};
Table::~Table()
{
}
bool TableCell::sendContent(MWAWListenerPtr, MWAWTable &table)
{
if (m_tId)
return static_cast<Table &>(table).sendText(m_tId, m_cPos);
return true;
}
void Table::updateCells()
{
auto numFormats=static_cast<int>(m_formatsList.size());
for (int c=0; c<numCells(); ++c) {
if (!get(c)) continue;
TableCell &cell=static_cast<TableCell &>(*get(c));
if (cell.m_formatId < 0 || cell.m_formatId>=numFormats) {
static bool first = true;
if (first) {
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::Table::updateCells: can not find the format\n"));
first = false;
}
continue;
}
cell.update(m_formatsList[size_t(cell.m_formatId)]);
}
}
////////////////////////////////////////
//! a frame format in HanMacWrdJGraph
struct FrameFormat {
public:
//! constructor
FrameFormat()
: m_style()
, m_borderType(0)
{
m_style.m_lineWidth=0;
for (auto &wrap : m_intWrap) wrap=1.0;
for (auto &wrap : m_extWrap) wrap=1.0;
}
//! add property to frame extra values
void addTo(MWAWGraphicStyle &style) const
{
if (m_style.hasLine()) {
MWAWBorder border;
border.m_width=double(m_style.m_lineWidth);
border.m_color=m_style.m_lineColor;
switch (m_borderType) {
case 0: // solid
break;
case 1:
border.m_type = MWAWBorder::Double;
break;
case 2:
border.m_type = MWAWBorder::Double;
border.m_widthsList.resize(3,1.);
border.m_widthsList[0]=2.0;
break;
case 3:
border.m_type = MWAWBorder::Double;
border.m_widthsList.resize(3,1.);
border.m_widthsList[2]=2.0;
break;
default:
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::FrameFormat::addTo: unexpected type\n"));
break;
}
style.setBorders(15, border);
}
if (m_style.hasSurfaceColor())
style.setBackgroundColor(m_style.m_surfaceColor);
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, FrameFormat const &frmt)
{
o << "style=[" << frmt.m_style << "],";
if (frmt.m_borderType) o << "border[type]=" << frmt.m_borderType << ",";
bool intDiff=false, extDiff=false;
for (int i=1; i < 4; ++i) {
if (frmt.m_intWrap[i]<frmt.m_intWrap[0] || frmt.m_intWrap[i]>frmt.m_intWrap[0])
intDiff=true;
if (frmt.m_extWrap[i]<frmt.m_extWrap[0] || frmt.m_extWrap[i]>frmt.m_extWrap[0])
extDiff=true;
}
if (intDiff) {
o << "dim/intWrap/border=[";
for (double i : frmt.m_intWrap)
o << i << ",";
o << "],";
}
else
o << "dim/intWrap/border=" << frmt.m_intWrap[0] << ",";
if (extDiff) {
o << "exterior[wrap]=[";
for (auto &wrap : frmt.m_extWrap)
o << wrap << ",";
o << "],";
}
else
o << "exterior[wrap]=" << frmt.m_extWrap[0] << ",";
return o;
}
//! the graphic style
MWAWGraphicStyle m_style;
//! the border type
int m_borderType;
//! the interior wrap dim
double m_intWrap[4];
//! the exterior wrap dim
double m_extWrap[4];
};
////////////////////////////////////////
//! Internal: the frame header of a HanMacWrdJGraph
struct Frame {
//! constructor
Frame()
: m_type(-1)
, m_fileId(-1)
, m_id(-1)
, m_formatId(0)
, m_page(0)
, m_pos()
, m_baseline(0.f)
, m_inGroup(false)
, m_parsed(false)
, m_extra("")
{
}
//! destructor
virtual ~Frame();
//! return the frame bdbox
MWAWBox2f getBdBox() const
{
MWAWVec2f minPt(m_pos[0][0], m_pos[0][1]);
MWAWVec2f maxPt(m_pos[1][0], m_pos[1][1]);
for (int c=0; c<2; ++c) {
if (m_pos.size()[c]>=0) continue;
minPt[c]=m_pos[1][c];
maxPt[c]=m_pos[0][c];
}
return MWAWBox2f(minPt,maxPt);
}
//! returns true if the frame data are read
virtual bool valid() const
{
return false;
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Frame const &grph);
//! the graph type
int m_type;
//! the file id
long m_fileId;
//! the local id
int m_id;
//! the format id
int m_formatId;
//! the page
int m_page;
//! the position
MWAWBox2f m_pos;
//! the baseline
float m_baseline;
//! true if this node is a group's child
bool m_inGroup;
//! true if we have send the data
mutable bool m_parsed;
//! an extra string
std::string m_extra;
};
Frame::~Frame()
{
}
std::ostream &operator<<(std::ostream &o, Frame const &grph)
{
switch (grph.m_type) {
case 0: // text or column
break;
case 1:
o << "header,";
break;
case 2:
o << "footer,";
break;
case 3:
o << "footnote[frame],";
break;
case 4:
o << "textbox,";
break;
case 6:
o << "picture,";
break;
case 8:
o << "basicGraphic,";
break;
case 9:
o << "table,";
break;
case 10:
o << "comments,"; // memo
break;
case 11:
o << "group";
break;
case 12:
o << "footnote[sep],";
break;
default:
o << "#type=" << grph.m_type << ",";
case -1:
break;
}
if (grph.m_fileId > 0)
o << "fileId=" << std::hex << grph.m_fileId << std::dec << ",";
if (grph.m_id>0)
o << "id=" << grph.m_id << ",";
if (grph.m_formatId > 0)
o << "formatId=" << grph.m_formatId << ",";
if (grph.m_page) o << "page=" << grph.m_page+1 << ",";
o << "pos=" << grph.m_pos << ",";
if (grph.m_baseline < 0 || grph.m_baseline>0) o << "baseline=" << grph.m_baseline << ",";
o << grph.m_extra;
return o;
}
////////////////////////////////////////
//! Internal: the comment frame of a HanMacWrdJGraph
struct CommentFrame final : public Frame {
public:
//! constructor
explicit CommentFrame(Frame const &orig)
: Frame(orig)
, m_zId(0)
, m_width(0)
, m_cPos(0)
, m_dim(0,0)
{
}
//! destructor
~CommentFrame() final;
//! returns true if the frame data are read
bool valid() const final
{
return true;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_zId) s << "zId[TZone]=" << std::hex << m_zId << std::dec << ",";
if (m_dim[0]>0 || m_dim[1] > 0)
s << "auxi[dim]=" << m_dim << ",";
if (m_width > 0)
s << "width=" << m_width << ",";
if (m_cPos)
s << "cPos[first]=" << m_cPos << ",";
return s.str();
}
//! the text id
long m_zId;
//! the zone width
double m_width;
//! the first char pos
long m_cPos;
//! the auxilliary dim
MWAWVec2f m_dim;
};
CommentFrame::~CommentFrame()
{
}
////////////////////////////////////////
//! Internal: a group of a HanMacWrdJGraph
struct Group final : public Frame {
public:
//! constructor
explicit Group(Frame const &orig)
: Frame(orig)
, m_zId(0)
, m_childsList()
{
}
//! destructor
~Group() final;
//! returns true if the frame data are read
bool valid() const final
{
return true;
}
//! the group id
long m_zId;
//! the child list
std::vector<long> m_childsList;
};
Group::~Group()
{
}
////////////////////////////////////////
//! Internal: the picture frame of a HanMacWrdJGraph
struct PictureFrame final : public Frame {
public:
//! constructor
explicit PictureFrame(Frame const &orig)
: Frame(orig)
, m_entry()
, m_zId(0)
, m_dim(100,100)
, m_scale(1,1)
{
}
//! destructor
~PictureFrame() final;
//! returns true if the frame data are read
bool valid() const final
{
return true;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_zId) s << "zId=" << std::hex << m_zId << std::dec << ",";
s << "dim[original]=" << m_dim << ",";
s << "scale=" << m_scale << ",";
return s.str();
}
//! the picture entry
MWAWEntry m_entry;
//! the picture id
long m_zId;
//! the picture size
MWAWVec2i m_dim;
//! the scale
MWAWVec2f m_scale;
};
PictureFrame::~PictureFrame()
{
}
////////////////////////////////////////
//! Internal: a footnote separator of a HanMacWrdJGraph
struct SeparatorFrame final : public Frame {
public:
//! constructor
explicit SeparatorFrame(Frame const &orig) : Frame(orig)
{
}
//! destructor
~SeparatorFrame() final;
//! returns true if the frame data are read
bool valid() const final
{
return true;
}
};
SeparatorFrame::~SeparatorFrame()
{
}
////////////////////////////////////////
//! Internal: the table frame of a HanMacWrdJGraph
struct TableFrame final : public Frame {
public:
//! constructor
explicit TableFrame(Frame const &orig)
: Frame(orig)
, m_zId(0)
, m_width(0)
, m_length(0)
, m_table()
{
}
//! destructor
~TableFrame() final;
//! returns true if the frame data are read
bool valid() const final
{
return true;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_zId) s << "zId[TZone]=" << std::hex << m_zId << std::dec << ",";
if (m_width > 0)
s << "width=" << m_width << ",";
if (m_length)
s << "length[text?]=" << m_length << ",";
return s.str();
}
//! the textzone id
long m_zId;
//! the zone width
double m_width;
//! related to text length?
long m_length;
//! the table
std::shared_ptr<Table> m_table;
};
TableFrame::~TableFrame()
{
}
////////////////////////////////////////
//! Internal: the textbox frame of a HanMacWrdJGraph
struct TextboxFrame final : public Frame {
public:
//! constructor
explicit TextboxFrame(Frame const &orig)
: Frame(orig)
, m_zId(0)
, m_width(0)
, m_cPos(0)
, m_linkToFId(0)
, m_isLinked(false)
{
}
//! destructor
~TextboxFrame() final;
//! returns true if the frame data are read
bool valid() const final
{
return true;
}
//! returns true if the box is linked to other textbox
bool isLinked() const
{
return m_linkToFId || m_isLinked;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_zId) s << "zId[TZone]=" << std::hex << m_zId << std::dec << ",";
if (m_width > 0)
s << "width=" << m_width << ",";
if (m_cPos)
s << "cPos[first]=" << m_cPos << ",";
return s.str();
}
//! the text id
long m_zId;
//! the zone width
double m_width;
//! the first char pos
long m_cPos;
//! the next link zone
long m_linkToFId;
//! true if this zone is linked
bool m_isLinked;
};
TextboxFrame::~TextboxFrame()
{
}
////////////////////////////////////////
//! Internal: the text frame (basic, header, footer, footnote) of a HanMacWrdJGraph
struct TextFrame final : public Frame {
public:
//! constructor
explicit TextFrame(Frame const &orig) : Frame(orig), m_zId(0), m_width(0), m_cPos(0)
{
}
//! destructor
~TextFrame() final;
//! returns true if the frame data are read
bool valid() const final
{
return true;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_zId) s << "zId[TZone]=" << std::hex << m_zId << std::dec << ",";
if (m_width > 0)
s << "width=" << m_width << ",";
if (m_cPos)
s << "cPos[first]=" << m_cPos << ",";
return s.str();
}
//! the text id
long m_zId;
//! the zone width
double m_width;
//! the first char pos
long m_cPos;
};
TextFrame::~TextFrame()
{
}
////////////////////////////////////////
//! Internal: the geometrical graph of a HanMacWrdJGraph
struct ShapeGraph final : public Frame {
//! constructor
explicit ShapeGraph(Frame const &orig)
: Frame(orig)
, m_shape()
, m_arrowsFlag(0)
{
}
//! destructor
~ShapeGraph() final;
//! returns true if the frame data are read
bool valid() const final
{
return true;
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, ShapeGraph const &graph)
{
o << graph.print();
o << static_cast<Frame const &>(graph);
return o;
}
//! print local data
std::string print() const
{
std::stringstream s;
s << m_shape;
if (m_arrowsFlag&1) s << "startArrow,";
if (m_arrowsFlag&2) s << "endArrow,";
return s.str();
}
//! the shape m_shape
MWAWGraphicShape m_shape;
//! the lines arrow flag
int m_arrowsFlag;
};
ShapeGraph::~ShapeGraph()
{
}
////////////////////////////////////////
//! Internal: the pattern of a HanMacWrdJGraph
struct Pattern final : public MWAWGraphicStyle::Pattern {
//! constructor ( 4 int by patterns )
explicit Pattern(uint16_t const *pat=nullptr)
: MWAWGraphicStyle::Pattern()
, m_percent(0)
{
if (!pat) return;
m_colors[0]=MWAWColor::white();
m_colors[1]=MWAWColor::black();
m_dim=MWAWVec2i(8,8);
m_data.resize(8);
for (size_t i=0; i < 4; ++i) {
uint16_t val=pat[i];
m_data[2*i]=static_cast<unsigned char>(val>>8);
m_data[2*i+1]=static_cast<unsigned char>(val&0xFF);
}
int numOnes=0;
for (size_t j=0; j < 8; ++j) {
auto val=static_cast<uint8_t>(m_data[j]);
for (int b=0; b < 8; b++) {
if (val&1) ++numOnes;
val = uint8_t(val>>1);
}
}
m_percent=float(numOnes)/64.f;
}
//! destructor
~Pattern() final;
//! the percentage
float m_percent;
};
Pattern::~Pattern()
{
}
////////////////////////////////////////
//! Internal: the state of a HanMacWrdJGraph
struct State {
//! constructor
State()
: m_framesList()
, m_framesMap()
, m_frameFormatsList()
, m_numPages(0)
, m_colorList()
, m_patternList()
, m_defaultFormat() { }
//! tries to find the lId the frame of a given type
std::shared_ptr<Frame> findFrame(int type, int lId) const
{
int actId = 0;
for (auto frame : m_framesList) {
if (!frame || frame->m_type != type)
continue;
if (actId++==lId) {
if (!frame->valid())
break;
return frame;
}
}
return std::shared_ptr<Frame>();
}
//! returns the frame format corresponding to an id
FrameFormat const &getFrameFormat(int id) const
{
if (id >= 0 && id < static_cast<int>(m_frameFormatsList.size()))
return m_frameFormatsList[size_t(id)];
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::State::getFrameFormat: can not find format %d\n", id));
return m_defaultFormat;
}
//! returns a color correspond to an id
bool getColor(int id, MWAWColor &col)
{
initColors();
if (id < 0 || id >= int(m_colorList.size())) {
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::State::getColor: can not find color %d\n", id));
return false;
}
col = m_colorList[size_t(id)];
return true;
}
//! returns a pattern correspond to an id
bool getPattern(int id, Pattern &pattern)
{
initPatterns();
if (id < 0 || id >= int(m_patternList.size())) {
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::State::getPattern: can not find pattern %d\n", id));
return false;
}
pattern = m_patternList[size_t(id)];
return true;
}
//! returns a color corresponding to a pattern and a color
static MWAWColor getColor(MWAWColor col, float pattern)
{
return MWAWColor::barycenter(pattern,col,1.f-pattern,MWAWColor::white());
}
//! init the color list
void initColors();
//! init the pattenr list
void initPatterns();
/** the list of frames */
std::vector<std::shared_ptr<Frame> > m_framesList;
/** a map zId->frame pos in frames list */
std::map<long, int> m_framesMap;
/** the list of frame format */
std::vector<FrameFormat> m_frameFormatsList;
int m_numPages /* the number of pages */;
//! a list colorId -> color
std::vector<MWAWColor> m_colorList;
//! a list patternId -> pattern
std::vector<Pattern> m_patternList;
//! empty format used to return a default format
FrameFormat m_defaultFormat;
};
void State::initPatterns()
{
if (m_patternList.size()) return;
static uint16_t const s_pattern[4*64] = {
0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff, 0xffff, 0xf7ff, 0xffff, 0x7fff, 0xf7ff, 0x7fff, 0xf7ff,
0xffee, 0xffbb, 0xffee, 0xffbb, 0x77dd, 0x77dd, 0x77dd, 0x77dd, 0xaa55, 0xaa55, 0xaa55, 0xaa55, 0x8822, 0x8822, 0x8822, 0x8822,
0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0x4400, 0xaa00, 0x1100, 0x8800, 0xaa00, 0x8800, 0xaa00, 0x8800, 0x2200, 0x8800, 0x2200,
0x8000, 0x0800, 0x8000, 0x0800, 0x8800, 0x0000, 0x8800, 0x0000, 0x8000, 0x0000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,
0xeedd, 0xbb77, 0xeedd, 0xbb77, 0x3366, 0xcc99, 0x3366, 0xcc99, 0x1122, 0x4488, 0x1122, 0x4488, 0x8307, 0x0e1c, 0x3870, 0xe0c1,
0x0306, 0x0c18, 0x3060, 0xc081, 0x0102, 0x0408, 0x1020, 0x4080, 0xffff, 0x0000, 0x0000, 0x0000, 0xff00, 0x0000, 0x0000, 0x0000,
0x77bb, 0xddee, 0x77bb, 0xddee, 0x99cc, 0x6633, 0x99cc, 0x6633, 0x8844, 0x2211, 0x8844, 0x2211, 0xe070, 0x381c, 0x0e07, 0x83c1,
0xc060, 0x3018, 0x0c06, 0x0381, 0x8040, 0x2010, 0x0804, 0x0201, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0x8080, 0x8080, 0x8080, 0x8080,
0xffaa, 0xffaa, 0xffaa, 0xffaa, 0xe4e4, 0xe4e4, 0xe4e4, 0xe4e4, 0xffff, 0xff00, 0x00ff, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,
0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0x0000, 0xff00, 0x0000, 0x8888, 0x8888, 0x8888, 0x8888, 0xff80, 0x8080, 0x8080, 0x8080,
0x4ecf, 0xfce4, 0x473f, 0xf372, 0x6006, 0x36b1, 0x8118, 0x1b63, 0x2004, 0x4002, 0x1080, 0x0801, 0x9060, 0x0609, 0x9060, 0x0609,
0x8814, 0x2241, 0x8800, 0xaa00, 0x2050, 0x8888, 0x8888, 0x0502, 0xaa00, 0x8000, 0x8800, 0x8000, 0x2040, 0x8000, 0x0804, 0x0200,
0xf0f0, 0xf0f0, 0x0f0f, 0x0f0f, 0x0077, 0x7777, 0x0077, 0x7777, 0xff88, 0x8888, 0xff88, 0x8888, 0xaa44, 0xaa11, 0xaa44, 0xaa11,
0x8244, 0x2810, 0x2844, 0x8201, 0x8080, 0x413e, 0x0808, 0x14e3, 0x8142, 0x2418, 0x1020, 0x4080, 0x40a0, 0x0000, 0x040a, 0x0000,
0x7789, 0x8f8f, 0x7798, 0xf8f8, 0xf1f8, 0x6cc6, 0x8f1f, 0x3663, 0xbf00, 0xbfbf, 0xb0b0, 0xb0b0, 0xff80, 0x8080, 0xff08, 0x0808,
0x1020, 0x54aa, 0xff02, 0x0408, 0x0008, 0x142a, 0x552a, 0x1408, 0x55a0, 0x4040, 0x550a, 0x0404, 0x8244, 0x3944, 0x8201, 0x0101
};
m_patternList.resize(64);
for (size_t i=0; i < 64; ++i)
m_patternList[i] = Pattern(&s_pattern[i*4]);
}
void State::initColors()
{
if (m_colorList.size()) return;
uint32_t const defCol[256] = {
0x000000, 0xffffff, 0xffffcc, 0xffff99, 0xffff66, 0xffff33, 0xffff00, 0xffccff,
0xffcccc, 0xffcc99, 0xffcc66, 0xffcc33, 0xffcc00, 0xff99ff, 0xff99cc, 0xff9999,
0xff9966, 0xff9933, 0xff9900, 0xff66ff, 0xff66cc, 0xff6699, 0xff6666, 0xff6633,
0xff6600, 0xff33ff, 0xff33cc, 0xff3399, 0xff3366, 0xff3333, 0xff3300, 0xff00ff,
0xff00cc, 0xff0099, 0xff0066, 0xff0033, 0xff0000, 0xccffff, 0xccffcc, 0xccff99,
0xccff66, 0xccff33, 0xccff00, 0xccccff, 0xcccccc, 0xcccc99, 0xcccc66, 0xcccc33,
0xcccc00, 0xcc99ff, 0xcc99cc, 0xcc9999, 0xcc9966, 0xcc9933, 0xcc9900, 0xcc66ff,
0xcc66cc, 0xcc6699, 0xcc6666, 0xcc6633, 0xcc6600, 0xcc33ff, 0xcc33cc, 0xcc3399,
0xcc3366, 0xcc3333, 0xcc3300, 0xcc00ff, 0xcc00cc, 0xcc0099, 0xcc0066, 0xcc0033,
0xcc0000, 0x99ffff, 0x99ffcc, 0x99ff99, 0x99ff66, 0x99ff33, 0x99ff00, 0x99ccff,
0x99cccc, 0x99cc99, 0x99cc66, 0x99cc33, 0x99cc00, 0x9999ff, 0x9999cc, 0x999999,
0x999966, 0x999933, 0x999900, 0x9966ff, 0x9966cc, 0x996699, 0x996666, 0x996633,
0x996600, 0x9933ff, 0x9933cc, 0x993399, 0x993366, 0x993333, 0x993300, 0x9900ff,
0x9900cc, 0x990099, 0x990066, 0x990033, 0x990000, 0x66ffff, 0x66ffcc, 0x66ff99,
0x66ff66, 0x66ff33, 0x66ff00, 0x66ccff, 0x66cccc, 0x66cc99, 0x66cc66, 0x66cc33,
0x66cc00, 0x6699ff, 0x6699cc, 0x669999, 0x669966, 0x669933, 0x669900, 0x6666ff,
0x6666cc, 0x666699, 0x666666, 0x666633, 0x666600, 0x6633ff, 0x6633cc, 0x663399,
0x663366, 0x663333, 0x663300, 0x6600ff, 0x6600cc, 0x660099, 0x660066, 0x660033,
0x660000, 0x33ffff, 0x33ffcc, 0x33ff99, 0x33ff66, 0x33ff33, 0x33ff00, 0x33ccff,
0x33cccc, 0x33cc99, 0x33cc66, 0x33cc33, 0x33cc00, 0x3399ff, 0x3399cc, 0x339999,
0x339966, 0x339933, 0x339900, 0x3366ff, 0x3366cc, 0x336699, 0x336666, 0x336633,
0x336600, 0x3333ff, 0x3333cc, 0x333399, 0x333366, 0x333333, 0x333300, 0x3300ff,
0x3300cc, 0x330099, 0x330066, 0x330033, 0x330000, 0x00ffff, 0x00ffcc, 0x00ff99,
0x00ff66, 0x00ff33, 0x00ff00, 0x00ccff, 0x00cccc, 0x00cc99, 0x00cc66, 0x00cc33,
0x00cc00, 0x0099ff, 0x0099cc, 0x009999, 0x009966, 0x009933, 0x009900, 0x0066ff,
0x0066cc, 0x006699, 0x006666, 0x006633, 0x006600, 0x0033ff, 0x0033cc, 0x003399,
0x003366, 0x003333, 0x003300, 0x0000ff, 0x0000cc, 0x000099, 0x000066, 0x000033,
0xee0000, 0xdd0000, 0xbb0000, 0xaa0000, 0x880000, 0x770000, 0x550000, 0x440000,
0x220000, 0x110000, 0x00ee00, 0x00dd00, 0x00bb00, 0x00aa00, 0x008800, 0x007700,
0x005500, 0x004400, 0x002200, 0x001100, 0x0000ee, 0x0000dd, 0x0000bb, 0x0000aa,
0x000088, 0x000077, 0x000055, 0x000044, 0x000022, 0x000011, 0xeeeeee, 0xdddddd,
0xbbbbbb, 0xaaaaaa, 0x888888, 0x777777, 0x555555, 0x444444, 0x222222, 0x111111,
};
m_colorList.resize(256);
for (size_t i = 0; i < 256; ++i)
m_colorList[i] = defCol[i];
}
////////////////////////////////////////
//! Internal: the subdocument of a HanMacWrdJGraph
class SubDocument final : public MWAWSubDocument
{
public:
//! the document type
enum Type { FrameInFrame, Group, Text, UnformattedTable, EmptyPicture };
//! constructor
SubDocument(HanMacWrdJGraph &pars, MWAWInputStreamPtr const &input, Type type, long id, long firstChar=0)
: MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
, m_graphParser(&pars)
, m_type(type)
, m_id(id)
, m_firstChar(firstChar)
, m_pos() {}
//! constructor
SubDocument(HanMacWrdJGraph &pars, MWAWInputStreamPtr const &input, MWAWPosition const &pos, Type type, long id, int firstChar=0)
: MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
, m_graphParser(&pars)
, m_type(type)
, m_id(id)
, m_firstChar(firstChar)
, m_pos(pos) {}
//! destructor
~SubDocument() final {}
//! operator!=
bool operator!=(MWAWSubDocument const &doc) const final;
//! the parser function
void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
protected:
/** the graph parser */
HanMacWrdJGraph *m_graphParser;
//! the zone type
Type m_type;
//! the zone id
long m_id;
//! the first char position
long m_firstChar;
//! the position in a frame
MWAWPosition m_pos;
private:
SubDocument(SubDocument const &orig) = delete;
SubDocument &operator=(SubDocument const &orig) = delete;
};
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
{
if (!listener.get()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::SubDocument::parse: no listener\n"));
return;
}
if (!m_graphParser) {
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::SubDocument::parse: no parser\n"));
return;
}
long pos = m_input->tell();
if (listener->getType()==MWAWListener::Graphic) {
if (m_type==Text)
m_graphParser->sendText(m_id, m_firstChar, listener);
else {
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::SubDocument::parse: send type %d is not implemented\n", m_type));
}
}
else {
switch (m_type) {
case EmptyPicture:
m_graphParser->sendEmptyPicture(m_pos);
break;
case Group:
m_graphParser->sendGroup(m_id, m_pos);
break;
case FrameInFrame:
m_graphParser->sendFrame(m_id, m_pos);
break;
case Text:
m_graphParser->sendText(m_id, m_firstChar);
break;
case UnformattedTable:
m_graphParser->sendTableUnformatted(m_id);
break;
#if !defined(__clang__)
default:
MWAW_DEBUG_MSG(("HanMacWrdJGraphInternal::SubDocument::parse: send type %d is not implemented\n", m_type));
break;
#endif
}
}
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
}
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
{
if (MWAWSubDocument::operator!=(doc)) return true;
auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
if (!sDoc) return true;
if (m_graphParser != sDoc->m_graphParser) return true;
if (m_type != sDoc->m_type) return true;
if (m_id != sDoc->m_id) return true;
if (m_firstChar != sDoc->m_firstChar) return true;
if (m_pos != sDoc->m_pos) return true;
return false;
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
HanMacWrdJGraph::HanMacWrdJGraph(HanMacWrdJParser &parser)
: m_parserState(parser.getParserState())
, m_state(new HanMacWrdJGraphInternal::State)
, m_mainParser(&parser)
{
}
HanMacWrdJGraph::~HanMacWrdJGraph()
{
}
int HanMacWrdJGraph::version() const
{
return m_parserState->m_version;
}
bool HanMacWrdJGraph::getColor(int colId, int patternId, MWAWColor &color) const
{
if (!m_state->getColor(colId, color)) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::getColor: can not find color for id=%d\n", colId));
return false;
}
HanMacWrdJGraphInternal::Pattern pattern;
if (!m_state->getPattern(patternId, pattern)) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::getColor: can not find pattern for id=%d\n", patternId));
return false;
}
color = m_state->getColor(color, pattern.m_percent);
return true;
}
int HanMacWrdJGraph::numPages() const
{
if (m_state->m_numPages)
return m_state->m_numPages;
int nPages = 0;
for (auto frame : m_state->m_framesList) {
if (!frame || !frame->valid()) continue;
int page = frame->m_page+1;
if (page <= nPages) continue;
if (page >= nPages+100) continue; // a pb ?
nPages = page;
}
m_state->m_numPages = nPages;
return nPages;
}
bool HanMacWrdJGraph::sendText(long textId, long fPos, MWAWListenerPtr const &listener)
{
return m_mainParser->sendText(textId, fPos, listener);
}
std::map<long,int> HanMacWrdJGraph::getTextFrameInformations() const
{
std::map<long,int> mapIdType;
for (auto frame : m_state->m_framesList) {
if (!frame || !frame->valid())
continue;
long zId=0;
switch (frame->m_type) {
case 0:
case 1:
case 2:
case 3:
zId=static_cast<HanMacWrdJGraphInternal::TextFrame const &>(*frame).m_zId;
break;
case 4:
zId=static_cast<HanMacWrdJGraphInternal::TextboxFrame const &>(*frame).m_zId;
break;
case 9:
zId=static_cast<HanMacWrdJGraphInternal::TableFrame const &>(*frame).m_zId;
break;
case 10:
zId=static_cast<HanMacWrdJGraphInternal::CommentFrame const &>(*frame).m_zId;
break;
default:
break;
}
if (!zId) continue;
if (mapIdType.find(zId) == mapIdType.end())
mapIdType[zId] = frame->m_type;
else if (mapIdType.find(zId)->second != frame->m_type) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::getTextFrameInformations: id %lx already set\n", static_cast<long unsigned int>(zId)));
}
}
return mapIdType;
}
bool HanMacWrdJGraph::getFootnoteInformations(long &textZId, std::vector<long> &fPosList) const
{
fPosList.clear();
textZId = 0;
for (auto frame : m_state->m_framesList) {
if (!frame || !frame->valid() || frame->m_type != 3)
continue;
auto const &text=static_cast<HanMacWrdJGraphInternal::TextFrame const &>(*frame);
if (textZId && text.m_zId != textZId) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrames: find different textIds\n"));
}
else if (!textZId)
textZId = text.m_zId;
fPosList.push_back(text.m_cPos);
}
return fPosList.size();
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool HanMacWrdJGraph::readFrames(MWAWEntry const &entry)
{
if (!entry.valid()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrames: called without any entry\n"));
return false;
}
if (entry.length() <= 8) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrames: the entry seems too short\n"));
return false;
}
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
entry.setParsed(true);
long pos = entry.begin()+8; // skip header
long endPos = entry.end();
input->seek(pos, librevenge::RVNG_SEEK_SET);
// first read the header
f << entry.name() << "[header]:";
HanMacWrdJZoneHeader mainHeader(true);
if (!m_mainParser->readClassicHeader(mainHeader,endPos) || mainHeader.m_fieldSize != 4 ||
16+12+mainHeader.m_n*4 > mainHeader.m_length) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrames: can not read the header\n"));
f << "###sz=" << mainHeader.m_length;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return false;
}
long headerEnd=pos+4+mainHeader.m_length;
long val;
for (int i = 0; i < 2; ++i) {
val = long(input->readULong(4));
f << "id" << i << "=" << std::hex << val << std::dec << ",";
}
for (int i = 0; i < 2; ++i) { // f0:small number, 0
val = input->readLong(2);
if (val)
f << "f" << i << "=" << val << ",";
}
f << "listIds=[";
std::vector<long> lIds(size_t(mainHeader.m_n));
for (int i = 0; i < mainHeader.m_n; ++i) {
val = long(input->readULong(4));
lIds[size_t(i)]=val;
m_state->m_framesMap[val]=i;
f << std::hex << val << std::dec << ",";
}
f << std::dec << "],";
if (input->tell()!=headerEnd) {
asciiFile.addDelimiter(input->tell(),'|');
input->seek(headerEnd, librevenge::RVNG_SEEK_SET);
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
// the data
m_state->m_framesList.resize(size_t(mainHeader.m_n));
for (int i = 0; i < mainHeader.m_n; ++i) {
pos = input->tell();
auto frame=readFrame(i);
if (!frame) {
f << "###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return false;
}
frame->m_fileId = lIds[size_t(i)];
m_state->m_framesList[size_t(i)]=frame;
}
// normally there remains 2 block, ...
// block 0
pos = input->tell();
f.str("");
f << entry.name() << "-Format:";
HanMacWrdJZoneHeader header(false);
if (!m_mainParser->readClassicHeader(header,endPos) || header.m_fieldSize!=48) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrames: can not read auxilliary block A\n"));
f << "###" << header;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return false;
}
long zoneEnd=pos+4+header.m_length;
f << header;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
for (int i = 0; i < header.m_n; ++i) {
HanMacWrdJGraphInternal::FrameFormat format;
MWAWGraphicStyle &style=format.m_style;
pos=input->tell();
f.str("");
val = input->readLong(2);
if (val != -2)
f << "f0=" << val << ",";
val = long(input->readULong(2));
if (val)
f << "f1=" << std::hex << val << std::dec << ",";
for (auto &wrap : format.m_intWrap) wrap = double(input->readLong(4))/65536.;
for (auto &wrap : format.m_extWrap) wrap = double(input->readLong(4))/65536.;
style.m_lineWidth= float(input->readLong(4))/65536.f;
format.m_borderType= static_cast<int>(input->readULong(1));
for (int j = 0; j < 2; j++) {
auto color = static_cast<int>(input->readULong(1));
MWAWColor col = j==0 ? MWAWColor::black() : MWAWColor::white();
if (!m_state->getColor(color, col))
f << "#color[" << j << "]=" << color << ",";
auto pattern = static_cast<int>(input->readULong(1));
if (pattern==0) {
if (i==0) style.m_lineOpacity=0;
else style.m_surfaceOpacity=0;
continue;
}
HanMacWrdJGraphInternal::Pattern pat;
if (m_state->getPattern(pattern, pat)) {
pat.m_colors[1]=col;
if (!pat.getUniqueColor(col)) {
pat.getAverageColor(col);
if (j) style.setPattern(pat);
}
}
else
f << "#pattern[" << j << "]=" << pattern << ",";
if (j==0)
style.m_lineColor=col;
else
style.setSurfaceColor(col,1);
}
for (int j = 0; j < 3; j++) { // always 0
val = static_cast<int>(input->readULong(1));
if (val) f << "g" << j << "=" << val << ",";
}
format.m_style.m_extra=f.str();
m_state->m_frameFormatsList.push_back(format);
f.str("");
f << entry.name() << "-F" << i << ":" << format;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+48, librevenge::RVNG_SEEK_SET);
}
input->seek(zoneEnd, librevenge::RVNG_SEEK_SET);
// block B
pos = input->tell();
f.str("");
f << entry.name() << "-B:";
header=HanMacWrdJZoneHeader(false);
if (!m_mainParser->readClassicHeader(header,endPos) || header.m_fieldSize!=8 ||
16+2+header.m_n*8 > header.m_length) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrames: can not read auxilliary block B\n"));
f << "###" << header;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return false;
}
for (int i = 0; i < 2; ++i) { // f0=1|3|4=N?
val = input->readLong(2);
if (val) f << "f" << i << "=" << val << ",";
}
f << "unk=[";
for (int i = 0; i < header.m_n; ++i) {
f << "[";
for (int j = 0; j < 2; j++) { // always 0?
val = input->readLong(2);
if (val) f << val << ",";
else f << "_,";
}
f << std::hex << input->readULong(4) << std::dec; // id
f << "],";
}
zoneEnd=pos+4+header.m_length;
f << header;
input->seek(zoneEnd, librevenge::RVNG_SEEK_SET);
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
// and for each n, a list
for (int i = 0; i < header.m_n; ++i) {
pos = input->tell();
f.str("");
f << entry.name() << "-B" << i << ":";
HanMacWrdJZoneHeader lHeader(false);
if (!m_mainParser->readClassicHeader(lHeader,endPos) || lHeader.m_fieldSize!=4) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrames: can not read auxilliary block B%d\n",i));
f << "###" << lHeader;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return false;
}
f << "listId?=[" << std::hex;
for (int j = 0; j < lHeader.m_n; j++) {
val = long(input->readULong(4));
f << val << ",";
}
f << std::dec << "],";
zoneEnd=pos+4+lHeader.m_length;
f << header;
input->seek(zoneEnd, librevenge::RVNG_SEEK_SET);
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
asciiFile.addPos(endPos);
asciiFile.addNote("_");
pos = input->tell();
if (pos!=endPos) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrames: find unexpected end data\n"));
f.str("");
f << entry.name() << "###:";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
return true;
}
std::shared_ptr<HanMacWrdJGraphInternal::Frame> HanMacWrdJGraph::readFrame(int id)
{
std::shared_ptr<HanMacWrdJGraphInternal::Frame> res;
HanMacWrdJGraphInternal::Frame graph;
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
long pos = input->tell();
auto len = long(input->readULong(4));
long endPos = pos+4+len;
if (len < 32 || !input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrame: can not read the frame length\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return res;
}
auto fl = static_cast<int>(input->readULong(1));
graph.m_type=(fl>>4);
f << "f0=" << std::hex << (fl&0xf) << std::dec << ",";
int val;
/* fl0=[0|1|2|3|4|6|8|9|a|b|c][2|6], fl1=0|1|20|24,
fl2=0|8|c|e|10|14|14|40|8a, fl3=0|10|80|c0 */
for (int i = 1; i < 4; ++i) {
val = static_cast<int>(input->readULong(1));
if (val) f << "fl" << i << "=" << std::hex << val << std::dec << ",";
}
graph.m_page = static_cast<int>(input->readLong(2));
graph.m_formatId = static_cast<int>(input->readULong(2));
float dim[4];
for (auto &d : dim) d = float(input->readLong(4))/65536.f;
graph.m_pos = MWAWBox2f(MWAWVec2f(dim[0],dim[1]),MWAWVec2f(dim[2],dim[3]));
graph.m_id = static_cast<int>(input->readLong(2)); // check me
val = static_cast<int>(input->readLong(2));
if (val) f << "f1=" << val << ",";
graph.m_baseline = float(input->readLong(4))/65536.f;
graph.m_extra = f.str();
f.str("");
f << "FrameDef-" << id << ":" << graph;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
switch (graph.m_type) {
case 0: // text
case 1: // header
case 2: // footer
case 3: // footnote
res=readTextData(graph, endPos);
break;
case 4:
res=readTextboxData(graph, endPos);
break;
case 6:
res=readPictureData(graph, endPos);
break;
case 8:
res=readShapeGraph(graph, endPos);
break;
case 9:
res=readTableData(graph, endPos);
break;
case 10:
res=readCommentData(graph, endPos);
break;
case 11:
if (len < 36) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrame: can not read the group id\n"));
break;
}
else {
auto group = std::make_shared<HanMacWrdJGraphInternal::Group>(graph);
res = group;
pos =input->tell();
group->m_zId = long(input->readULong(4));
f.str("");
f << "FrameDef-group:zId=" << std::hex << group->m_zId << std::dec << ",";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
break;
}
case 12:
if (len < 52) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readFrame: can not read the footnote[sep] data\n"));
break;
}
else {
auto sep = std::make_shared<HanMacWrdJGraphInternal::SeparatorFrame>(graph);
res = sep;
pos =input->tell();
f.str("");
f << "FrameDef-footnote[sep];";
for (int i = 0; i < 8; ++i) { // f0=256,f2=8,f4=2,f6=146
val = static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
f << "zId=" << std::hex << long(input->readULong(4)) << std::dec << ",";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
break;
}
default:
break;
}
if (!res)
res.reset(new HanMacWrdJGraphInternal::Frame(graph));
if (input->tell() != endPos)
asciiFile.addDelimiter(input->tell(),'|');
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return res;
}
bool HanMacWrdJGraph::readGroupData(MWAWEntry const &entry, int actZone)
{
if (!entry.valid()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGroupData: called without any entry\n"));
return false;
}
if (entry.length() == 8) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGroupData: find an empty zone\n"));
entry.setParsed(true);
return true;
}
if (entry.length() < 12) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGroupData: the entry seems too short\n"));
return false;
}
auto frame = m_state->findFrame(11, actZone);
std::vector<long> dummyList;
std::vector<long> *idsList=&dummyList;
if (!frame) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGroupData: can not find group %d\n", actZone));
}
else {
auto *group = static_cast<HanMacWrdJGraphInternal::Group *>(frame.get());
idsList = &group->m_childsList;
}
long pos = entry.begin()+8; // skip header
long endPos = entry.end();
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
entry.setParsed(true);
input->seek(pos, librevenge::RVNG_SEEK_SET);
// first read the header
f << entry.name() << "[header]:";
HanMacWrdJZoneHeader mainHeader(true);
if (!m_mainParser->readClassicHeader(mainHeader,endPos) || mainHeader.m_fieldSize!=4) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGroupData: can not read an entry\n"));
f << "###sz=" << mainHeader.m_length;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return false;
}
long headerEnd=pos+4+mainHeader.m_length;
f << mainHeader;
f << "listId=[" << std::hex;
idsList->resize(size_t(mainHeader.m_n), 0);
for (int i = 0; i < mainHeader.m_n; ++i) {
auto val = long(input->readULong(4));
(*idsList)[size_t(i)]=val;
f << val << ",";
}
f << std::dec << "],";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (input->tell()!=headerEnd) {
asciiFile.addDelimiter(input->tell(),'|');
input->seek(headerEnd, librevenge::RVNG_SEEK_SET);
}
pos = input->tell();
if (pos!=endPos) {
f.str("");
f << entry.name() << "[last]:###";
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGroupData: find unexpected end of data\n"));
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
return true;
}
// try to read the graph data
bool HanMacWrdJGraph::readGraphData(MWAWEntry const &entry, int actZone)
{
if (!entry.valid()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGraphData: called without any entry\n"));
return false;
}
if (entry.length() < 12) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGraphData: the entry seems too short\n"));
return false;
}
long pos = entry.begin()+8; // skip header
long endPos = entry.end();
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
entry.setParsed(true);
input->seek(pos, librevenge::RVNG_SEEK_SET);
// first read the header
f << entry.name() << "[header]:";
HanMacWrdJZoneHeader mainHeader(false);
if (!m_mainParser->readClassicHeader(mainHeader,endPos) || mainHeader.m_fieldSize!=8) {
// sz=12 is ok, means no data
if (entry.length() != 12) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGraphData: can not read an entry\n"));
f << "###sz=" << mainHeader.m_length;
}
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return false;
}
long headerEnd=pos+4+mainHeader.m_length;
f << mainHeader;
std::vector<MWAWVec2f> lVertices(size_t(mainHeader.m_n));
f << "listPt=[";
for (int i = 0; i < mainHeader.m_n; ++i) {
float point[2];
for (auto &pt : point) pt = float(input->readLong(4))/65536.f;
MWAWVec2f pt(point[1], point[0]);
lVertices[size_t(i)]=pt;
f << pt << ",";
}
f << "],";
auto frame = m_state->findFrame(8, actZone);
if (!frame) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGraphData: can not find basic graph %d\n", actZone));
}
else {
auto *graph = static_cast<HanMacWrdJGraphInternal::ShapeGraph *>(frame.get());
if (graph->m_shape.m_type != MWAWGraphicShape::Polygon) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGraphData: basic graph %d is not a polygon\n", actZone));
}
else {
graph->m_shape.m_vertices = lVertices;
for (auto &vertex : graph->m_shape.m_vertices)
vertex += graph->m_pos[0];
}
}
asciiFile.addPos(entry.begin()+8);
asciiFile.addNote(f.str().c_str());
if (headerEnd!=endPos) {
f.str("");
f << entry.name() << "[last]:###";
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readGraphData: find unexpected end of data\n"));
asciiFile.addPos(headerEnd);
asciiFile.addNote(f.str().c_str());
}
return true;
}
// try to read the picture
bool HanMacWrdJGraph::readPicture(MWAWEntry const &entry, int actZone)
{
if (!entry.valid()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readPicture: called without any entry\n"));
return false;
}
if (entry.length() < 12) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readPicture: the entry seems too short\n"));
return false;
}
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
entry.setParsed(true);
long pos = entry.begin()+8; // skip header
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto sz=long(input->readULong(4));
if (sz+12 != entry.length()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readPicture: the entry sz seems bad\n"));
return false;
}
f << "Picture:pictSz=" << sz;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
asciiFile.skipZone(entry.begin()+12, entry.end()-1);
auto frame = m_state->findFrame(6, actZone);
if (!frame) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readPicture: can not find picture %d\n", actZone));
}
else {
auto *picture = static_cast<HanMacWrdJGraphInternal::PictureFrame *>(frame.get());
picture->m_entry.setBegin(pos+4);
picture->m_entry.setLength(sz);
}
return true;
}
// table
bool HanMacWrdJGraph::readTable(MWAWEntry const &entry, int actZone)
{
if (!entry.valid()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: called without any entry\n"));
return false;
}
if (entry.length() == 8) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: find an empty zone\n"));
entry.setParsed(true);
return true;
}
if (entry.length() < 12) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: the entry seems too short\n"));
return false;
}
long pos = entry.begin()+8; // skip header
long endPos = entry.end();
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
entry.setParsed(true);
input->seek(pos, librevenge::RVNG_SEEK_SET);
// first read the header
f << entry.name() << "[header]:";
HanMacWrdJZoneHeader mainHeader(true);
if (!m_mainParser->readClassicHeader(mainHeader,endPos) || mainHeader.m_fieldSize!=4 ||
mainHeader.m_length < 16+12+4*mainHeader.m_n) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: can not read an entry\n"));
f << "###sz=" << mainHeader.m_length;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return false;
}
long headerEnd=pos+4+mainHeader.m_length;
f << mainHeader;
std::shared_ptr<HanMacWrdJGraphInternal::Table> table(new HanMacWrdJGraphInternal::Table(*this));
long textId = 0;
auto frame = m_state->findFrame(9, actZone);
if (!frame || !frame->valid()) {
MWAW_DEBUG_MSG(("HanMacWrdJTable::readTable: can not find basic table %d\n", actZone));
}
else {
auto *tableFrame = static_cast<HanMacWrdJGraphInternal::TableFrame *>(frame.get());
tableFrame->m_table = table;
textId = tableFrame->m_zId;
}
table->m_rows = static_cast<int>(input->readULong(1));
table->m_columns = static_cast<int>(input->readULong(1));
f << "dim=" << table->m_rows << "x" << table->m_columns << ",";
long val;
for (int i = 0; i < 4; ++i) { // f0=4|5|7|8|9, f1=1|7|107, f2=3|4|5|6, f3=0
val = long(input->readULong(2));
if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
}
table->m_height = static_cast<int>(input->readLong(2));
f << "h=" << table->m_height << ",";
f << "listId=[" << std::hex;
std::vector<long> listIds;
for (int i = 0; i < mainHeader.m_n; ++i) {
val = long(input->readULong(4));
listIds.push_back(val);
f << val << ",";
}
f << std::dec << "],";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (input->tell()!=headerEnd) {
asciiFile.addDelimiter(input->tell(),'|');
input->seek(headerEnd, librevenge::RVNG_SEEK_SET);
}
// first read the row
for (int i = 0; i < mainHeader.m_n; ++i) {
pos = input->tell();
f.str("");
f << entry.name() << "-row" << i << ":";
HanMacWrdJZoneHeader header(false);
if (!m_mainParser->readClassicHeader(header,endPos) || header.m_fieldSize!=16) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: can not read zone %d\n", i));
f << "###" << header;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (header.m_length<16 || pos+4+header.m_length>endPos)
return false;
input->seek(pos+4+header.m_length, librevenge::RVNG_SEEK_SET);
continue;
}
long zoneEnd=pos+4+header.m_length;
f << header;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
// the different cells in a row
for (int j = 0; j < header.m_n; j++) {
pos = input->tell();
f.str("");
std::shared_ptr<HanMacWrdJGraphInternal::TableCell> cell(new HanMacWrdJGraphInternal::TableCell(textId));
cell->setPosition(MWAWVec2i(j,i));
cell->m_cPos = long(input->readULong(4));
cell->m_zId = long(input->readULong(4));
cell->m_flags = static_cast<int>(input->readULong(2));
if (cell->m_flags&0x80)
cell->setVAlignment(MWAWCell::VALIGN_CENTER);
switch ((cell->m_flags>>9)&3) {
case 1:
cell->setExtraLine(MWAWCell::E_Line1);
break;
case 2:
cell->setExtraLine(MWAWCell::E_Line2);
break;
case 3:
cell->setExtraLine(MWAWCell::E_Cross);
break;
case 0: // none
default:
break;
}
val = input->readLong(2);
if (val) f << "#f0=" << val << ",";
cell->m_formatId = static_cast<int>(input->readLong(2));
int dim[2]; // for merge, inactive -> the other limit cell
for (auto &d : dim) d=static_cast<int>(input->readULong(1));
if (cell->m_flags & 0x1000) {
if (dim[1]>=j&&dim[0]>=i)
cell->setNumSpannedCells(MWAWVec2i(dim[1]+1-j,dim[0]+1-i));
else {
static bool first = true;
if (first) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: can not determine the span\n"));
first = false;
}
f << "##span=" << dim[1]+1-j << "x" << dim[0]+1-i << ",";
}
}
cell->m_extra = f.str();
// do not push the ignore cell
if ((cell->m_flags&0x2000)==0)
table->add(cell);
f.str("");
f << entry.name() << "-cell:" << cell;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+16, librevenge::RVNG_SEEK_SET);
}
if (input->tell() != zoneEnd) {
asciiFile.addDelimiter(input->tell(),'|');
input->seek(zoneEnd, librevenge::RVNG_SEEK_SET);
}
}
asciiFile.addPos(endPos);
asciiFile.addNote("_");
if (input->tell()==endPos) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: can not find the 3 last blocks\n"));
return true;
}
for (int i = 0; i < 2; ++i) {
pos = input->tell();
f.str("");
f << entry.name() << "-" << (i==0 ? "rowY" : "colX") << ":";
HanMacWrdJZoneHeader header(false);
if (!m_mainParser->readClassicHeader(header,endPos) || header.m_fieldSize != 4) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: can not read zone %d\n", i));
f << "###" << header;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (header.m_length<16 || pos+4+header.m_length>endPos)
return false;
input->seek(pos+4+header.m_length, librevenge::RVNG_SEEK_SET);
continue;
}
long zoneEnd=pos+4+header.m_length;
f << header;
f << "pos=[";
float prevPos = 0.;
std::vector<float> dim;
for (int j = 0; j < header.m_n; j++) {
float cPos = float(input->readULong(4))/65536.f;
f << cPos << ",";
if (j!=0)
dim.push_back(cPos-prevPos);
prevPos=cPos;
}
f << "],";
if (i==0)
table->setRowsSize(dim);
else
table->setColsSize(dim);
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(zoneEnd, librevenge::RVNG_SEEK_SET);
}
// finally the format
readTableFormatsList(*table, endPos);
table->updateCells();
if (input->tell() != endPos) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTable: find unexpected last block\n"));
pos = input->tell();
f.str("");
f << entry.name() << "-###:";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
}
return true;
}
bool HanMacWrdJGraph::readTableFormatsList(HanMacWrdJGraphInternal::Table &table, long endPos)
{
table.m_formatsList.clear();
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f, f2;
long pos = input->tell();
f.str("");
f << "Table-format:";
HanMacWrdJZoneHeader header(false);
if (!m_mainParser->readClassicHeader(header,endPos) || header.m_fieldSize != 40) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTableFormatsList: can not read format\n"));
f << "###" << header;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
long zoneEnd=pos+4+header.m_length;
f << header;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
table.m_formatsList.resize(size_t(header.m_n));
for (int i = 0; i < header.m_n; ++i) {
HanMacWrdJGraphInternal::CellFormat format;
pos = input->tell();
f.str("");
long val = input->readLong(2); // always -2
if (val != -2)
f << "f0=" << val << ",";
val = long(input->readULong(2)); // 0|2004|51|1dd4
if (val)
f << "#f1=" << std::hex << val << std::dec << ",";
int color, pattern;
format.m_borders.resize(4);
static char const *what[] = {"T", "L", "B", "R"};
static size_t const which[] = { libmwaw::Top, libmwaw::Left, libmwaw::Bottom, libmwaw::Right };
for (int b=0; b < 4; b++) {
f2.str("");
MWAWBorder border;
border.m_width=double(input->readLong(4))/65536.;
auto type = int(input->readLong(1));
switch (type) {
case 0: // solid
break;
case 1:
border.m_type = MWAWBorder::Double;
break;
case 2:
border.m_type = MWAWBorder::Double;
border.m_widthsList.resize(3,1.);
border.m_widthsList[0]=2.0;
break;
case 3:
border.m_type = MWAWBorder::Double;
border.m_widthsList.resize(3,1.);
border.m_widthsList[2]=2.0;
break;
default:
f2 << "#style=" << type << ",";
break;
}
color = static_cast<int>(input->readULong(1));
MWAWColor col = MWAWColor::black();
if (!m_state->getColor(color, col))
f2 << "#color=" << color << ",";
pattern = static_cast<int>(input->readULong(1));
HanMacWrdJGraphInternal::Pattern pat;
if (pattern==0) border.m_style=MWAWBorder::None;
else {
if (!m_state->getPattern(pattern, pat)) {
f2 << "#pattern=" << pattern << ",";
border.m_color = col;
}
else
border.m_color = m_state->getColor(col, pat.m_percent);
}
val = long(input->readULong(1));
if (val) f2 << "unkn=" << val << ",";
format.m_borders[which[b]] = border;
if (f2.str().length())
f << "bord" << what[b] << "=[" << f2.str() << "],";
}
color = static_cast<int>(input->readULong(1));
MWAWColor backCol = MWAWColor::white();
if (!m_state->getColor(color, backCol))
f << "#backcolor=" << color << ",";
pattern = static_cast<int>(input->readULong(1));
HanMacWrdJGraphInternal::Pattern pat;
if (!m_state->getPattern(pattern, pat))
f << "#backPattern=" << pattern << ",";
else
format.m_backColor = m_state->getColor(backCol, pat.m_percent);
format.m_extra = f.str();
table.m_formatsList[size_t(i)]=format;
f.str("");
f << "Table-format" << i << ":" << format;
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+header.m_fieldSize, librevenge::RVNG_SEEK_SET);
}
input->seek(zoneEnd, librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// send data to a listener
////////////////////////////////////////////////////////////
bool HanMacWrdJGraph::sendFrame(long frameId, MWAWPosition const &pos)
{
if (!m_parserState->m_textListener) return true;
auto fIt=m_state->m_framesMap.find(frameId);
if (fIt == m_state->m_framesMap.end() || fIt->second < 0 || fIt->second >= int(m_state->m_framesList.size())) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendFrame: can not find frame %lx\n", static_cast<long unsigned int>(frameId)));
return false;
}
auto frame = m_state->m_framesList[size_t(fIt->second)];
if (!frame || !frame->valid()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendFrame: frame %lx is not initialized\n", static_cast<long unsigned int>(frameId)));
return false;
}
return sendFrame(*frame, pos);
}
// --- basic shape
bool HanMacWrdJGraph::sendShapeGraph(HanMacWrdJGraphInternal::ShapeGraph const &pict, MWAWPosition const &lPos)
{
if (!m_parserState->m_textListener) return true;
auto pos(lPos);
if (pos.size()[0] <= 0 || pos.size()[1] <= 0)
pos.setSize(pict.getBdBox().size());
auto const &format=m_state->getFrameFormat(pict.m_formatId);
MWAWGraphicStyle style(format.m_style);;
if (pict.m_shape.m_type==MWAWGraphicShape::Line) {
if (pict.m_arrowsFlag&1) style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain();
if (pict.m_arrowsFlag&2) style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
}
pos.setOrigin(pos.origin());
pos.setSize(pos.size()+MWAWVec2f(4,4));
m_parserState->m_textListener->insertShape(pos,pict.m_shape,style);
return true;
}
// picture
bool HanMacWrdJGraph::sendPictureFrame(HanMacWrdJGraphInternal::PictureFrame const &pict, MWAWPosition const &lPos)
{
if (!m_parserState->m_textListener) return true;
#ifdef DEBUG_WITH_FILES
bool firstTime = pict.m_parsed == false;
#endif
pict.m_parsed = true;
auto pos(lPos);
if (pos.size()[0] <= 0 || pos.size()[1] <= 0)
pos.setSize(pict.getBdBox().size());
if (!pict.m_entry.valid()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendPictureFrame: can not find picture data\n"));
sendEmptyPicture(pos);
return true;
}
//fixme: check if we have border
MWAWInputStreamPtr input = m_parserState->m_input;
long fPos = input->tell();
input->seek(pict.m_entry.begin(), librevenge::RVNG_SEEK_SET);
librevenge::RVNGBinaryData data;
input->readDataBlock(pict.m_entry.length(), data);
input->seek(fPos, librevenge::RVNG_SEEK_SET);
#ifdef DEBUG_WITH_FILES
if (firstTime) {
libmwaw::DebugStream f;
static int volatile pictName = 0;
f << "Pict" << ++pictName << ".pct1";
libmwaw::Debug::dumpFile(data, f.str().c_str());
}
#endif
m_parserState->m_textListener->insertPicture(pos, MWAWEmbeddedObject(data, "image/pict"));
return true;
}
bool HanMacWrdJGraph::sendEmptyPicture(MWAWPosition const &pos)
{
if (!m_parserState->m_textListener)
return true;
MWAWVec2f pictSz = pos.size();
std::shared_ptr<MWAWPict> pict;
MWAWPosition pictPos(MWAWVec2f(0,0), pictSz, librevenge::RVNG_POINT);
pictPos.setRelativePosition(MWAWPosition::Frame);
pictPos.setOrder(-1);
MWAWBox2f box=MWAWBox2f(MWAWVec2f(0,0),pictSz);
MWAWPosition shapePos(MWAWVec2f(0,0),pictSz, librevenge::RVNG_POINT);
shapePos.m_anchorTo=MWAWPosition::Page;
MWAWGraphicEncoder graphicEncoder;
MWAWGraphicListener graphicListener(*m_parserState, box, &graphicEncoder);
graphicListener.startDocument();
MWAWGraphicStyle defStyle;
graphicListener.insertShape(shapePos, MWAWGraphicShape::rectangle(box), defStyle);
graphicListener.insertShape(shapePos, MWAWGraphicShape::line(box[0],box[1]), defStyle);
graphicListener.insertShape(shapePos, MWAWGraphicShape::line(MWAWVec2f(0,pictSz[1]), MWAWVec2f(pictSz[0],0)), defStyle);
graphicListener.endDocument();
MWAWEmbeddedObject picture;
if (!graphicEncoder.getBinaryResult(picture)) return false;
m_parserState->m_textListener->insertPicture(pictPos, picture);
return true;
}
// ----- comment box
bool HanMacWrdJGraph::sendComment(HanMacWrdJGraphInternal::CommentFrame const &comment, MWAWPosition const &lPos, librevenge::RVNGPropertyList const &extras)
{
if (!m_parserState->m_textListener) return true;
MWAWVec2f commentSz = comment.getBdBox().size();
if (comment.m_dim[0] > commentSz[0]) commentSz[0]=comment.m_dim[0];
if (comment.m_dim[1] > commentSz[1]) commentSz[1]=comment.m_dim[1];
auto pos(lPos);
pos.setSize(commentSz);
librevenge::RVNGPropertyList pList(extras);
auto const &format=m_state->getFrameFormat(comment.m_formatId);
MWAWGraphicStyle style=format.m_style;
MWAWBorder border;
border.m_color=style.m_lineColor;
border.m_width=double(style.m_lineWidth);
style.setBorders(libmwaw::LeftBit|libmwaw::BottomBit|libmwaw::RightBit, border);
border.m_width=20*double(style.m_lineWidth);
style.setBorders(libmwaw::TopBit, border);
if (style.hasSurfaceColor())
style.setBackgroundColor(style.m_surfaceColor);
MWAWSubDocumentPtr subdoc(new HanMacWrdJGraphInternal::SubDocument(*this, m_parserState->m_input, HanMacWrdJGraphInternal::SubDocument::Text, comment.m_zId));
m_parserState->m_textListener->insertTextBox(pos, subdoc, style);
return true;
}
// ----- textbox
bool HanMacWrdJGraph::sendTextbox(HanMacWrdJGraphInternal::TextboxFrame const &textbox, MWAWPosition const &lPos)
{
if (!m_parserState->m_textListener) return true;
auto pos(lPos);
if (pos.size()[0] <= 0 || pos.size()[1] <= 0)
pos.setSize(textbox.getBdBox().size());
auto const &format=m_state->getFrameFormat(textbox.m_formatId);
MWAWGraphicStyle style;
format.addTo(style);
MWAWSubDocumentPtr subdoc;
if (!textbox.m_isLinked)
subdoc.reset(new HanMacWrdJGraphInternal::SubDocument(*this, m_parserState->m_input, HanMacWrdJGraphInternal::SubDocument::Text, textbox.m_zId));
else {
librevenge::RVNGString fName;
fName.sprintf("Frame%ld", textbox.m_fileId);
style.m_frameName=fName.cstr();
}
if (textbox.m_linkToFId) {
librevenge::RVNGString fName;
fName.sprintf("Frame%ld", textbox.m_linkToFId);
style.m_frameNextName=fName.cstr();
}
m_parserState->m_textListener->insertTextBox(pos, subdoc, style);
return true;
}
// ----- table
bool HanMacWrdJGraph::sendTableUnformatted(long fId)
{
if (!m_parserState->m_textListener)
return true;
auto fIt = m_state->m_framesMap.find(fId);
if (fIt == m_state->m_framesMap.end()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendTableUnformatted: can not find the table frame %lx\n", static_cast<long unsigned int>(fId)));
return false;
}
int id = fIt->second;
if (id < 0 || id >= static_cast<int>(m_state->m_framesList.size()))
return false;
auto &frame = *m_state->m_framesList[size_t(id)];
if (!frame.valid() || frame.m_type != 9) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendTableUnformatted: can not find the table frame %lx(II)\n", static_cast<long unsigned int>(fId)));
return false;
}
auto &tableFrame = static_cast<HanMacWrdJGraphInternal::TableFrame &>(frame);
if (!tableFrame.m_table) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendTableUnformatted: can not find the table\n"));
return false;
}
tableFrame.m_table->sendAsText(m_parserState->m_textListener);
return true;
}
////////////////////////////////////////////////////////////
// low level
////////////////////////////////////////////////////////////
bool HanMacWrdJGraph::sendFrame(HanMacWrdJGraphInternal::Frame const &frame, MWAWPosition const &lPos)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) return true;
if (!frame.valid()) {
frame.m_parsed = true;
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendFrame: called with invalid frame\n"));
return false;
}
MWAWInputStreamPtr &input= m_parserState->m_input;
auto pos(lPos);
switch (frame.m_type) {
case 4: {
frame.m_parsed = true;
auto const &format=m_state->getFrameFormat(frame.m_formatId);
if (format.m_style.hasPattern()) {
auto const &textbox=static_cast<HanMacWrdJGraphInternal::TextboxFrame const &>(frame);
if (!textbox.isLinked() && m_mainParser->canSendTextAsGraphic(textbox.m_zId,0)) {
MWAWSubDocumentPtr subdoc
(new HanMacWrdJGraphInternal::SubDocument(*this, input, HanMacWrdJGraphInternal::SubDocument::Text, textbox.m_zId));
MWAWBox2f box(MWAWVec2f(0,0),pos.size());
MWAWGraphicEncoder graphicEncoder;
MWAWGraphicListener graphicListener(*m_parserState, box, &graphicEncoder);
graphicListener.startDocument();
MWAWPosition textPos(box[0], box.size(), librevenge::RVNG_POINT);
textPos.m_anchorTo=MWAWPosition::Page;
graphicListener.insertTextBox(textPos, subdoc, format.m_style);
graphicListener.endDocument();
MWAWEmbeddedObject picture;
if (!graphicEncoder.getBinaryResult(picture))
return false;
listener->insertPicture(pos, picture);
return true;
}
}
return sendTextbox(static_cast<HanMacWrdJGraphInternal::TextboxFrame const &>(frame), pos);
}
case 6: {
auto const &pict = static_cast<HanMacWrdJGraphInternal::PictureFrame const &>(frame);
if (!pict.m_entry.valid()) {
pos.setSize(pict.getBdBox().size());
frame.m_parsed = true;
MWAWPosition framePos(pos);
framePos.m_anchorTo = MWAWPosition::Frame;
framePos.setOrigin(MWAWVec2f(0,0));
MWAWSubDocumentPtr subdoc
(new HanMacWrdJGraphInternal::SubDocument
(*this, input, framePos, HanMacWrdJGraphInternal::SubDocument::EmptyPicture, 0));
listener->insertTextBox(pos, subdoc);
return true;
}
return sendPictureFrame(pict, pos);
}
case 8:
frame.m_parsed = true;
return sendShapeGraph(static_cast<HanMacWrdJGraphInternal::ShapeGraph const &>(frame), pos);
case 9: {
frame.m_parsed = true;
auto const &tableFrame = static_cast<HanMacWrdJGraphInternal::TableFrame const &>(frame);
if (!tableFrame.m_table) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendFrame: can not find the table\n"));
return false;
}
auto &table = *tableFrame.m_table;
if (!table.updateTable()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendFrame: can not find the table structure\n"));
MWAWSubDocumentPtr subdoc
(new HanMacWrdJGraphInternal::SubDocument
(*this, input, HanMacWrdJGraphInternal::SubDocument::UnformattedTable, frame.m_fileId));
listener->insertTextBox(pos, subdoc);
return true;
}
if (pos.m_anchorTo==MWAWPosition::Page ||
(pos.m_anchorTo!=MWAWPosition::Frame && table.hasExtraLines())) {
MWAWPosition framePos(pos);
framePos.m_anchorTo = MWAWPosition::Frame;
framePos.setOrigin(MWAWVec2f(0,0));
MWAWSubDocumentPtr subdoc
(new HanMacWrdJGraphInternal::SubDocument
(*this, input, framePos, HanMacWrdJGraphInternal::SubDocument::FrameInFrame, frame.m_fileId));
pos.setSize(MWAWVec2f(-0.01f,-0.01f)); // autosize
listener->insertTextBox(pos, subdoc);
return true;
}
if (table.sendTable(listener, pos.m_anchorTo==MWAWPosition::Frame))
return true;
return table.sendAsText(listener);
}
case 10:
frame.m_parsed = true;
return sendComment(static_cast<HanMacWrdJGraphInternal::CommentFrame const &>(frame), pos);
case 11: {
auto const &group=static_cast<HanMacWrdJGraphInternal::Group const &>(frame);
if ((pos.m_anchorTo==MWAWPosition::Char || pos.m_anchorTo==MWAWPosition::CharBaseLine) && !canCreateGraphic(group)) {
MWAWPosition framePos(pos);
framePos.m_anchorTo = MWAWPosition::Frame;
framePos.setOrigin(MWAWVec2f(0,0));
pos.setSize(group.getBdBox().size());
MWAWSubDocumentPtr subdoc
(new HanMacWrdJGraphInternal::SubDocument
(*this, input, framePos, HanMacWrdJGraphInternal::SubDocument::Group, group.m_fileId));
listener->insertTextBox(pos, subdoc);
return true;
}
sendGroup(group, pos);
break;
}
default:
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendFrame: sending type %d is not implemented\n", frame.m_type));
break;
}
frame.m_parsed = true;
return false;
}
// try to read a basic comment zone
std::shared_ptr<HanMacWrdJGraphInternal::CommentFrame> HanMacWrdJGraph::readCommentData(HanMacWrdJGraphInternal::Frame const &header, long endPos)
{
std::shared_ptr<HanMacWrdJGraphInternal::CommentFrame> comment;
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
long pos = input->tell();
if (endPos<pos+40) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readCommentData: the zone seems too short\n"));
return comment;
}
comment.reset(new HanMacWrdJGraphInternal::CommentFrame(header));
comment->m_width = double(input->readLong(4))/65536.;
long val = input->readLong(2); // small number between 1 and 0x17
if (val!=1)
f << "f0=" << val << ",";
val = input->readLong(2);// always 0?
if (val)
f << "f1=" << val << ",";
comment->m_cPos = long(input->readULong(4));
val = long(input->readULong(4));
f << "id0=" << std::hex << val << std::dec << ",";
comment->m_zId = long(input->readULong(4));
for (int i=0; i < 4; ++i) { // g2=8000 if close?
val = input->readLong(2);
if (val)
f << "g" << i << "=" << val << ",";
}
float dim[2];
for (auto &d : dim) d = float(input->readLong(4))/65536.f;
comment->m_dim=MWAWVec2f(dim[1],dim[0]);
for (int i=0; i < 2; ++i) {
val = input->readLong(2);
if (val)
f << "g" << i+4 << "=" << val << ",";
}
std::string extra=f.str();
comment->m_extra += extra;
f.str("");
f << "FrameDef(Comment-data):" << comment->print() << extra;
if (input->tell() != endPos)
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return comment;
}
// try to read a basic picture zone
std::shared_ptr<HanMacWrdJGraphInternal::PictureFrame> HanMacWrdJGraph::readPictureData(HanMacWrdJGraphInternal::Frame const &header, long endPos)
{
std::shared_ptr<HanMacWrdJGraphInternal::PictureFrame> picture;
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
long pos = input->tell();
if (endPos<pos+40) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readPictureData: the zone seems too short\n"));
return picture;
}
picture.reset(new HanMacWrdJGraphInternal::PictureFrame(header));
long val;
for (int i=0; i < 2; ++i) { // always 0
val = input->readLong(2);
if (val)
f << "f" << i << "=" << val << ",";
}
float fDim[2]; // a small size, typically 1x1
for (auto &d : fDim) d = float(input->readLong(4))/65536.f;
picture->m_scale = MWAWVec2f(fDim[0], fDim[1]);
picture->m_zId = long(input->readULong(4));
for (int i = 0; i < 2; ++i) { // f2=0, f3=0|-1 : maybe front/back color?
val = input->readLong(4);
if (val)
f << "f" << i << "=" << val << ",";
}
int dim[2];
for (auto &d: dim) d = int(input->readLong(2));
picture->m_dim=MWAWVec2i(dim[0],dim[1]); // checkme: xy
for (int i = 0; i < 6; ++i) { // g2=8400
val = long(input->readULong(2));
if (val)
f << "g" << i << "=" << std::hex << val << std::dec << ",";
}
std::string extra = f.str();
picture->m_extra += extra;
f.str("");
f << "FrameDef(picture-data):" << picture->print() << extra;
if (input->tell() != endPos)
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return picture;
}
// try to read a basic table zone
std::shared_ptr<HanMacWrdJGraphInternal::TableFrame> HanMacWrdJGraph::readTableData(HanMacWrdJGraphInternal::Frame const &header, long endPos)
{
std::shared_ptr<HanMacWrdJGraphInternal::TableFrame> table;
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
long pos = input->tell();
if (endPos<pos+28) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTableData: the zone seems too short\n"));
return table;
}
table.reset(new HanMacWrdJGraphInternal::TableFrame(header));
table->m_width = double(input->readLong(4))/65536.;
long val = input->readLong(2); // small number between 1 and 3
if (val!=1)
f << "f0=" << val << ",";
val = input->readLong(2);// always 0?
if (val)
f << "f1=" << val << ",";
table->m_length = long(input->readULong(4));
val = long(input->readULong(4));
f << "id0=" << std::hex << val << std::dec << ",";
table->m_zId = long(input->readULong(4));
for (int i = 0; i < 2; ++i) {
val = input->readLong(2);
if (val) f << "f" << i+2 << "=" << val << ",";
}
val = long(input->readULong(4));
f << "id1=" << std::hex << val << std::dec << ",";
std::string extra=f.str();
table->m_extra += extra;
f.str("");
f << "FrameDef(table-data):" << table->print() << extra;
if (input->tell() != endPos)
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return table;
}
// try to read a basic text box zone
std::shared_ptr<HanMacWrdJGraphInternal::TextboxFrame> HanMacWrdJGraph::readTextboxData(HanMacWrdJGraphInternal::Frame const &header, long endPos)
{
std::shared_ptr<HanMacWrdJGraphInternal::TextboxFrame> textbox;
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
long pos = input->tell();
if (endPos<pos+24) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTextboxData: the zone seems too short\n"));
return textbox;
}
textbox.reset(new HanMacWrdJGraphInternal::TextboxFrame(header));
textbox->m_width = double(input->readLong(4))/65536.;
long val = input->readLong(2); // small number between 1 and 0x17
if (val!=1)
f << "f0=" << val << ",";
val = input->readLong(2);// always 0?
if (val)
f << "f1=" << val << ",";
textbox->m_cPos = long(input->readULong(4));
val = long(input->readULong(4));
f << "id0=" << std::hex << val << std::dec << ",";
textbox->m_zId = long(input->readULong(4));
float dim = float(input->readLong(4))/65536.f; // a small negative number: 0, -4 or -6.5
if (dim < 0 || dim > 0)
f << "dim?=" << dim << ",";
std::string extra=f.str();
textbox->m_extra += extra;
f.str("");
f << "FrameDef(Textbox-data):" << textbox->print() << extra;
if (input->tell() != endPos)
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return textbox;
}
// try to read a basic text zone
std::shared_ptr<HanMacWrdJGraphInternal::TextFrame> HanMacWrdJGraph::readTextData(HanMacWrdJGraphInternal::Frame const &header, long endPos)
{
std::shared_ptr<HanMacWrdJGraphInternal::TextFrame> text;
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
long pos = input->tell();
if (endPos<pos+20) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readTextData: the zone seems too short\n"));
return text;
}
text.reset(new HanMacWrdJGraphInternal::TextFrame(header));
text->m_width = double(input->readLong(4))/65536.;
long val = input->readLong(2); // small number between 1 and 0x17
if (val!=1)
f << "f0=" << val << ",";
val = input->readLong(2);// always 0?
if (val)
f << "f1=" << val << ",";
text->m_cPos = long(input->readULong(4));
val = long(input->readULong(4));
f << "id0=" << std::hex << val << std::dec << ",";
text->m_zId = long(input->readULong(4));
std::string extra=f.str();
text->m_extra += extra;
f.str("");
f << "FrameDef(Text-data):" << text->print() << extra;
if (input->tell() != endPos)
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return text;
}
// try to read a small graphic
std::shared_ptr<HanMacWrdJGraphInternal::ShapeGraph> HanMacWrdJGraph::readShapeGraph(HanMacWrdJGraphInternal::Frame const &header, long endPos)
{
std::shared_ptr<HanMacWrdJGraphInternal::ShapeGraph> graph;
MWAWInputStreamPtr input = m_parserState->m_input;
libmwaw::DebugFile &asciiFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
long pos = input->tell();
if (endPos<pos+36) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::readShapeGraph: the zone seems too short\n"));
return graph;
}
graph.reset(new HanMacWrdJGraphInternal::ShapeGraph(header));
long val = static_cast<int>(input->readULong(1));
auto graphType = static_cast<int>(val>>4);
auto flag = int(val&0xf);
bool isLine = graphType==0 || graphType==3;
bool ok = graphType >= 0 && graphType < 7;
MWAWBox2f bdbox=graph->m_pos;
MWAWGraphicShape &shape=graph->m_shape;
shape = MWAWGraphicShape();
shape.m_bdBox = shape.m_formBox = bdbox;
if (isLine) {
graph->m_arrowsFlag = (flag>>2)&0x3;
flag &= 0x3;
}
auto flag1 = static_cast<int>(input->readULong(1));
float angles[2]= {0,0};
if (graphType==5) { // arc
auto transf = static_cast<int>((2*(flag&1)) | (flag1>>7));
int decal = (transf%2) ? 4-transf : transf;
angles[0] = float(-90*decal);
angles[1] = float(90-90*decal);
flag &= 0xe;
flag1 &= 0x7f;
}
if (flag) f << "#fl0=" << std::hex << flag << std::dec << ",";
if (flag1) f << "#fl1=" << std::hex << flag1 << std::dec << ",";
val = input->readLong(2); // always 0
if (val) f << "f0=" << val << ",";
val = input->readLong(4);
float cornerDim=0;
if (graphType==4)
cornerDim = float(val)/65536.f;
else if (val)
f << "#cornerDim=" << val << ",";
if (isLine) {
shape.m_type=MWAWGraphicShape::Line;
float coord[2];
for (int pt = 0; pt < 2; ++pt) {
for (auto &c : coord) c = float(input->readLong(4))/65536.f;
shape.m_vertices.push_back(MWAWVec2f(coord[1],coord[0]));
}
}
else {
switch (graphType) {
// case 0: already treated in isLine block
case 1:
shape.m_type = MWAWGraphicShape::Rectangle;
break;
case 2:
shape.m_type = MWAWGraphicShape::Circle;
break;
// case 3: already treated in isLine block
case 4:
shape.m_type = MWAWGraphicShape::Rectangle;
for (int c=0; c < 2; ++c) {
if (2.f*cornerDim <= bdbox.size()[c])
shape.m_cornerWidth[c]=cornerDim;
else
shape.m_cornerWidth[c]=bdbox.size()[c]/2.0f;
}
break;
case 5: {
// we must compute the real bd box: first the box on the unit circle
float minVal[2] = { 0, 0 }, maxVal[2] = { 0, 0 };
int limitAngle[2];
for (int i = 0; i < 2; ++i)
limitAngle[i] = (angles[i] < 0) ? int(angles[i]/90.f)-1 : int(angles[i]/90.f);
for (int bord = limitAngle[0]; bord <= limitAngle[1]+1; ++bord) {
float ang = (bord == limitAngle[0]) ? float(angles[0]) :
(bord == limitAngle[1]+1) ? float(angles[1]) : float(90 * bord);
ang *= float(M_PI/180.);
float actVal[2] = { std::cos(ang), -std::sin(ang)};
if (actVal[0] < minVal[0]) minVal[0] = actVal[0];
else if (actVal[0] > maxVal[0]) maxVal[0] = actVal[0];
if (actVal[1] < minVal[1]) minVal[1] = actVal[1];
else if (actVal[1] > maxVal[1]) maxVal[1] = actVal[1];
}
float factor[2]= {bdbox.size()[0]/(maxVal[0]>minVal[0]?maxVal[0]-minVal[0]:0.f),
bdbox.size()[1]/(maxVal[1]>minVal[1]?maxVal[1]-minVal[1]:0.f)
};
float delta[2]= {bdbox[0][0]-minVal[0] *factor[0],bdbox[0][1]-minVal[1] *factor[1]};
shape.m_formBox=MWAWBox2f(MWAWVec2f(delta[0]-factor[0],delta[1]-factor[1]),
MWAWVec2f(delta[0]+factor[0],delta[1]+factor[1]));
shape.m_type=MWAWGraphicShape::Pie;
shape.m_arcAngles=MWAWVec2f(angles[0],angles[1]);
break;
}
case 6:
shape.m_type = MWAWGraphicShape::Polygon;
break;
default:
break;
}
for (int i = 0; i < 4; ++i) {
val = input->readLong(4);
if (val) f << "#coord" << i << "=" << val << ",";
}
}
auto id = long(input->readULong(4));
if (id) {
if (graphType!=6)
f << "#id0=" << std::hex << id << std::dec << ",";
else
f << "id[poly]=" << std::hex << id << std::dec << ",";
}
id = long(input->readULong(4));
f << "id=" << std::hex << id << std::dec << ",";
for (int i = 0; i < 2; ++i) { // always 1|0
val = input->readLong(2);
if (val) f << "g" << i << "=" << val << ",";
}
std::string extra = f.str();
graph->m_extra += extra;
f.str("");
f << "FrameDef(basicGraphic-data):" << graph->print() << extra;
if (input->tell() != endPos)
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (!ok)
graph.reset();
return graph;
}
////////////////////////////////////////////////////////////
// prepare data
////////////////////////////////////////////////////////////
void HanMacWrdJGraph::prepareStructures()
{
std::multimap<long,size_t> textZoneFrameMap;
auto numFrames = int(m_state->m_framesList.size());
for (auto fIt : m_state->m_framesMap) {
int id = fIt.second;
if (id < 0 || id >= numFrames || !m_state->m_framesList[size_t(id)])
continue;
auto const &frame = *m_state->m_framesList[size_t(id)];
if (!frame.valid() || frame.m_type!=4)
continue;
auto const &text = static_cast<HanMacWrdJGraphInternal::TextboxFrame const &>(frame);
if (!text.m_zId) continue;
textZoneFrameMap.insert(std::multimap<long,size_t>::value_type(text.m_zId, size_t(id)));
}
auto tbIt=textZoneFrameMap.begin();
while (tbIt!=textZoneFrameMap.end()) {
long textId=tbIt->first;
std::map<long, HanMacWrdJGraphInternal::TextboxFrame *> nCharTextMap;
bool ok=true;
while (tbIt!=textZoneFrameMap.end() && tbIt->first==textId) {
size_t id=tbIt++->second;
auto &text = static_cast<HanMacWrdJGraphInternal::TextboxFrame &>(*m_state->m_framesList[size_t(id)]);
if (nCharTextMap.find(text.m_cPos)!=nCharTextMap.end()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::prepareStructures: pos %ld already exist for textZone %lx\n",
text.m_cPos, static_cast<long unsigned int>(textId)));
ok=false;
}
else
nCharTextMap[text.m_cPos]=&text;
}
size_t numIds=nCharTextMap.size();
if (!ok || numIds<=1) continue;
HanMacWrdJGraphInternal::TextboxFrame *prevText=nullptr;
for (auto ctIt : nCharTextMap) {
HanMacWrdJGraphInternal::TextboxFrame *newText=ctIt.second;
if (prevText) {
prevText->m_linkToFId=newText->m_fileId;
newText->m_isLinked=true;
}
prevText=newText;
}
}
// now check that there is no loop
for (auto fIt : m_state->m_framesMap) {
int id = fIt.second;
if (id < 0 || id >= numFrames || !m_state->m_framesList[size_t(id)])
continue;
auto const &frame = *m_state->m_framesList[size_t(id)];
if (!frame.valid() || frame.m_inGroup || frame.m_type!=11)
continue;
std::set<long> seens;
checkGroupStructures(fIt.first, seens, false);
}
}
bool HanMacWrdJGraph::checkGroupStructures(long zId, std::set<long> &seens, bool inGroup)
{
while (seens.find(zId)!=seens.end()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::checkGroupStructures: zone %ld already find\n", zId));
return false;
}
seens.insert(zId);
auto fIt= m_state->m_framesMap.find(zId);
if (fIt==m_state->m_framesMap.end() || fIt->second < 0 ||
fIt->second >= static_cast<int>(m_state->m_framesList.size()) || !m_state->m_framesList[size_t(fIt->second)]) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::checkGroupStructures: can not find zone %ld\n", zId));
return false;
}
auto &frame = *m_state->m_framesList[size_t(fIt->second)];
frame.m_inGroup=inGroup;
if (!frame.valid() || frame.m_type!=11)
return true;
auto &group = static_cast<HanMacWrdJGraphInternal::Group &>(frame);
for (size_t c=0; c < group.m_childsList.size(); ++c) {
if (checkGroupStructures(group.m_childsList[c], seens, true))
continue;
group.m_childsList.resize(c);
break;
}
return true;
}
////////////////////////////////////////////////////////////
// send group
////////////////////////////////////////////////////////////
bool HanMacWrdJGraph::sendGroup(long fId, MWAWPosition const &pos)
{
if (!m_parserState->m_textListener)
return true;
auto fIt = m_state->m_framesMap.find(fId);
if (fIt == m_state->m_framesMap.end()) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendGroup: can not find table %lx\n", static_cast<long unsigned int>(fId)));
return false;
}
int id = fIt->second;
if (id < 0 || id >= static_cast<int>(m_state->m_framesList.size()))
return false;
auto &frame = *m_state->m_framesList[size_t(id)];
if (!frame.valid() || frame.m_type != 11) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendGroup: can not find table %lx(II)\n", static_cast<long unsigned int>(fId)));
return false;
}
return sendGroup(static_cast<HanMacWrdJGraphInternal::Group &>(frame), pos);
}
bool HanMacWrdJGraph::sendGroup(HanMacWrdJGraphInternal::Group const &group, MWAWPosition const &pos)
{
group.m_parsed=true;
sendGroupChild(group,pos);
return true;
}
bool HanMacWrdJGraph::canCreateGraphic(HanMacWrdJGraphInternal::Group const &group)
{
int page = group.m_page;
auto numFrames = int(m_state->m_framesList.size());
for (auto fId : group.m_childsList) {
auto fIt=m_state->m_framesMap.find(fId);
if (fIt == m_state->m_framesMap.end() || fIt->second < 0 || fIt->second >= numFrames ||
!m_state->m_framesList[size_t(fIt->second)])
continue;
auto const &frame=*m_state->m_framesList[size_t(fIt->second)];
if (frame.m_page!=page) return false;
switch (frame.m_type) {
case 4: {
auto const &text=static_cast<HanMacWrdJGraphInternal::TextboxFrame const &>(frame);
if (text.isLinked() || !m_mainParser->canSendTextAsGraphic(text.m_zId,0))
return false;
break;
}
case 8: // shape
break;
case 11:
if (!canCreateGraphic(static_cast<HanMacWrdJGraphInternal::Group const &>(frame)))
return false;
break;
default:
return false;
}
}
return true;
}
void HanMacWrdJGraph::sendGroup(HanMacWrdJGraphInternal::Group const &group, MWAWGraphicListenerPtr const &listener)
{
if (!listener) return;
group.m_parsed=true;
MWAWInputStreamPtr &input= m_parserState->m_input;
auto numFrames = int(m_state->m_framesList.size());
for (auto fId : group.m_childsList) {
auto fIt=m_state->m_framesMap.find(fId);
if (fIt == m_state->m_framesMap.end() || fIt->second < 0 || fIt->second >= numFrames ||
!m_state->m_framesList[size_t(fIt->second)])
continue;
auto const &frame=*m_state->m_framesList[size_t(fIt->second)];
MWAWBox2f box=frame.getBdBox();
auto const &format=m_state->getFrameFormat(frame.m_formatId);
MWAWPosition pictPos(box[0], box.size(), librevenge::RVNG_POINT);
pictPos.m_anchorTo=MWAWPosition::Page;
switch (frame.m_type) {
case 4: {
frame.m_parsed=true;
auto const &textbox=static_cast<HanMacWrdJGraphInternal::TextboxFrame const &>(frame);
MWAWSubDocumentPtr subdoc
(new HanMacWrdJGraphInternal::SubDocument(*this, input, HanMacWrdJGraphInternal::SubDocument::Text, textbox.m_zId));
listener->insertTextBox(pictPos, subdoc, format.m_style);
break;
}
case 8: {
frame.m_parsed=true;
auto const &shape=static_cast<HanMacWrdJGraphInternal::ShapeGraph const &>(frame);
MWAWGraphicStyle style(format.m_style);;
if (shape.m_shape.m_type==MWAWGraphicShape::Line) {
if (shape.m_arrowsFlag&1) style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain();
if (shape.m_arrowsFlag&2) style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
}
listener->insertShape(pictPos, shape.m_shape, style);
break;
}
case 11:
sendGroup(static_cast<HanMacWrdJGraphInternal::Group const &>(frame), listener);
break;
default:
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendGroup: unexpected type %d\n", frame.m_type));
break;
}
}
}
void HanMacWrdJGraph::sendGroupChild(HanMacWrdJGraphInternal::Group const &group, MWAWPosition const &pos)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendGroupChild: can not find the listeners\n"));
return;
}
size_t numChilds=group.m_childsList.size(), childNotSent=0;
if (!numChilds) return;
int numDataToMerge=0;
MWAWBox2f partialBdBox;
MWAWPosition partialPos(pos);
MWAWInputStreamPtr &input= m_parserState->m_input;
auto numFrames = int(m_state->m_framesList.size());
for (size_t c=0; c<numChilds; ++c) {
long fId=group.m_childsList[c];
auto fIt=m_state->m_framesMap.find(fId);
if (fIt == m_state->m_framesMap.end() || fIt->second < 0 || fIt->second >= numFrames ||
!m_state->m_framesList[size_t(fIt->second)]) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendGroupChild: can not find child %lx\n", static_cast<long unsigned int>(fId)));
continue;
}
auto const &frame=*m_state->m_framesList[size_t(fIt->second)];
bool canMerge=false;
if (frame.m_page==group.m_page) {
switch (frame.m_type) {
case 4: {
auto const &text=static_cast<HanMacWrdJGraphInternal::TextboxFrame const &>(frame);
canMerge=!text.isLinked()&&m_mainParser->canSendTextAsGraphic(text.m_zId,0);
break;
}
case 8: // shape
canMerge = true;
break;
case 11:
canMerge = canCreateGraphic(static_cast<HanMacWrdJGraphInternal::Group const &>(frame));
break;
default:
break;
}
}
bool isLast=false;
if (canMerge) {
MWAWBox2f box=frame.getBdBox();
if (numDataToMerge == 0)
partialBdBox=box;
else
partialBdBox=partialBdBox.getUnion(box);
++numDataToMerge;
if (c+1 < numChilds)
continue;
isLast=true;
}
if (numDataToMerge>1) {
partialBdBox.extend(3);
MWAWGraphicEncoder graphicEncoder;
MWAWGraphicListenerPtr graphicListener(new MWAWGraphicListener(*m_parserState, partialBdBox, &graphicEncoder));
graphicListener->startDocument();
size_t lastChild = isLast ? c : c-1;
for (size_t ch=childNotSent; ch <= lastChild; ++ch) {
long localFId=group.m_childsList[ch];
fIt=m_state->m_framesMap.find(localFId);
if (fIt == m_state->m_framesMap.end() || fIt->second < 0 || fIt->second >= numFrames ||
!m_state->m_framesList[size_t(fIt->second)])
continue;
auto const &child=*m_state->m_framesList[size_t(fIt->second)];
MWAWBox2f box=child.getBdBox();
auto const &format=m_state->getFrameFormat(child.m_formatId);
MWAWPosition pictPos(box[0], box.size(), librevenge::RVNG_POINT);
pictPos.m_anchorTo=MWAWPosition::Page;
switch (child.m_type) {
case 4: {
child.m_parsed=true;
auto const &textbox=static_cast<HanMacWrdJGraphInternal::TextboxFrame const &>(child);
MWAWSubDocumentPtr subdoc
(new HanMacWrdJGraphInternal::SubDocument(*this, input, HanMacWrdJGraphInternal::SubDocument::Text, textbox.m_zId));
graphicListener->insertTextBox(pictPos, subdoc, format.m_style);
break;
}
case 8: {
child.m_parsed=true;
auto const &shape=static_cast<HanMacWrdJGraphInternal::ShapeGraph const &>(child);
MWAWGraphicStyle style(format.m_style);
if (shape.m_shape.m_type==MWAWGraphicShape::Line) {
if (shape.m_arrowsFlag&1) style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain();
if (shape.m_arrowsFlag&2) style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
}
graphicListener->insertShape(pictPos, shape.m_shape, style);
break;
}
case 11:
sendGroup(static_cast<HanMacWrdJGraphInternal::Group const &>(child), graphicListener);
break;
default:
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendGroupChild: unexpected type %d\n", child.m_type));
break;
}
}
graphicListener->endDocument();
MWAWEmbeddedObject picture;
if (graphicEncoder.getBinaryResult(picture)) {
partialPos.setOrigin(pos.origin()+partialBdBox[0]-group.m_pos[0]);
partialPos.setSize(partialBdBox.size());
listener->insertPicture(partialPos, picture);
if (isLast)
break;
childNotSent=c;
}
}
// time to send back the data
for (; childNotSent <= c; ++childNotSent) {
long localFId=group.m_childsList[childNotSent];
fIt=m_state->m_framesMap.find(localFId);
if (fIt == m_state->m_framesMap.end() || fIt->second < 0 || fIt->second >= numFrames ||
!m_state->m_framesList[size_t(fIt->second)]) {
MWAW_DEBUG_MSG(("HanMacWrdJGraph::sendGroup: can not find child %lx\n", static_cast<long unsigned int>(localFId)));
continue;
}
auto const &childFrame=*m_state->m_framesList[size_t(fIt->second)];
MWAWPosition fPos(pos);
fPos.setOrigin(childFrame.m_pos[0]-group.m_pos[0]+pos.origin());
fPos.setSize(childFrame.m_pos.size());
sendFrame(childFrame, fPos);
}
numDataToMerge=0;
}
}
////////////////////////////////////////////////////////////
// send data
////////////////////////////////////////////////////////////
bool HanMacWrdJGraph::sendPageGraphics(std::vector<long> const &doNotSendIds)
{
if (!m_parserState->m_textListener)
return true;
std::set<long> notSend;
for (auto id : doNotSendIds)
notSend.insert(id);
auto numFrames = int(m_state->m_framesList.size());
for (auto fIt : m_state->m_framesMap) {
int id = fIt.second;
if (notSend.find(fIt.first) != notSend.end() || id < 0 || id >= numFrames ||
!m_state->m_framesList[size_t(id)])
continue;
auto const &frame = *m_state->m_framesList[size_t(id)];
if (!frame.valid() || frame.m_parsed || frame.m_inGroup)
continue;
if (frame.m_type <= 3 || frame.m_type == 12) continue;
MWAWPosition pos(frame.m_pos[0],frame.m_pos.size(),librevenge::RVNG_POINT);
pos.setRelativePosition(MWAWPosition::Page);
pos.setPage(frame.m_page+1);
sendFrame(frame, pos);
}
return true;
}
void HanMacWrdJGraph::flushExtra()
{
if (!m_parserState->m_textListener)
return;
for (auto frame : m_state->m_framesList) {
if (!frame || !frame->valid() || frame->m_parsed)
continue;
if (frame->m_type <= 3 || frame->m_type == 12) continue;
MWAWPosition pos(MWAWVec2f(0,0),MWAWVec2f(0,0),librevenge::RVNG_POINT);
pos.setRelativePosition(MWAWPosition::Char);
sendFrame(*frame, pos);
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: