/* -*- 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 "MWAWTextListener.hxx"
#include "MWAWPictMac.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSubDocument.hxx"
#include "MWAWTable.hxx"
#include "HanMacWrdKParser.hxx"
#include "HanMacWrdKGraph.hxx"
#include "libmwaw_internal.hxx"
/** Internal: the structures of a HanMacWrdKGraph */
namespace HanMacWrdKGraphInternal
{
////////////////////////////////////////
//! Internal: the frame header of a HanMacWrdKGraph
struct Frame {
//! constructor
Frame()
: m_type(-1)
, m_fileId(-1)
, m_fileSubId(-1)
, m_id(-1)
, m_page(0)
, m_pos()
, m_baseline(0.f)
, m_posFlags(0)
, m_style()
, m_borderType(0)
, 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);
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Frame const &grph);
//! the graph type
int m_type;
//! the file id
long m_fileId;
//! the file sub id
long m_fileSubId;
//! the local id
int m_id;
//! the page
int m_page;
//! the position
MWAWBox2f m_pos;
//! the baseline
float m_baseline;
//! the graph anchor flags
int m_posFlags;
//! the style
MWAWGraphicStyle m_style;
//! the border type
int m_borderType;
//! the border default size (before using width), 0 means Top, other unknown
MWAWVec2f m_borders[4];
//! true if the frame is a child of a group
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: // main text
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;
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_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 << ",";
if (grph.m_inGroup) o << "inGroup,";
int flag = grph.m_posFlags;
if (flag & 4) o << "wrap=around,"; // else overlap
if (flag & 0x40) o << "lock,";
if (!(flag & 0x80)) o << "transparent,"; // else opaque
if (flag & 0x39) o << "posFlags=" << std::hex << (flag & 0x39) << std::dec << ",";
o << "style=[" << grph.m_style << "],";
if (grph.m_borderType) o << "bord[type]=" << grph.m_borderType << ",";
for (int i = 0; i < 4; ++i) {
if (grph.m_borders[i].x() > 0 || grph.m_borders[i].y() > 0)
o << "border" << i << "=" << grph.m_borders[i] << ",";
}
o << grph.m_extra;
return o;
}
////////////////////////////////////////
//! Internal: the geometrical graph of a HanMacWrdKGraph
struct ShapeGraph final : public Frame {
//! constructor
explicit ShapeGraph(Frame const &orig)
: Frame(orig)
, m_shape()
{
}
//! destructor
~ShapeGraph() final;
//! operator<<
friend std::ostream &operator<<(std::ostream &o, ShapeGraph const &graph)
{
o << graph.print() << ",";
o << static_cast<Frame const &>(graph);
return o;
}
//! return the current style
MWAWGraphicStyle getStyle() const
{
MWAWGraphicStyle style(m_style);
if (m_shape.m_type!=MWAWGraphicShape::Line)
style.m_arrows[0]=style.m_arrows[1]=MWAWGraphicStyle::Arrow();
return style;
}
//! print local data
std::string print() const
{
std::stringstream s;
s << m_shape;
return s.str();
}
//! the shape m_shape
MWAWGraphicShape m_shape;
};
ShapeGraph::~ShapeGraph()
{
}
////////////////////////////////////////
//! Internal: the footnote of a HanMacWrdKGraph
struct FootnoteFrame final : public Frame {
//! constructor
explicit FootnoteFrame(Frame const &orig)
: Frame(orig)
, m_textFileId(-1)
, m_textFileSubId(0)
{
}
//! destructor
~FootnoteFrame() final;
//! operator<<
friend std::ostream &operator<<(std::ostream &o, FootnoteFrame const &ftn)
{
o << ftn.print();
o << static_cast<Frame const &>(ftn);
return o;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_textFileId>0)
s << "textFileId=" << std::hex << m_textFileId << "[" << m_textFileSubId << std::dec << "],";
return s.str();
}
//! the text file id
long m_textFileId;
//! the text file subId
long m_textFileSubId;
};
FootnoteFrame::~FootnoteFrame()
{
}
////////////////////////////////////////
//! Internal: the group of a HanMacWrdKGraph
struct Group final : public Frame {
struct Child;
//! constructor
explicit Group(Frame const &orig)
: Frame(orig)
, m_childsList()
{
}
//! destructor
~Group() final;
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Group const &group)
{
o << group.print();
o << static_cast<Frame const &>(group);
return o;
}
//! print local data
std::string print() const;
//! the list of child
std::vector<Child> m_childsList;
//! struct to store child data in HanMacWrdKGraphInternal::Group
struct Child {
//! constructor
Child()
: m_fileId(-1)
{
for (auto &val : m_values) val=0;
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Child const &ch)
{
if (ch.m_fileId > 0)
o << "fileId=" << std::hex << ch.m_fileId << std::dec << ",";
for (int i = 0; i < 2; ++i) {
if (ch.m_values[i] == 0)
continue;
o << "f" << i << "=" << ch.m_values[i] << ",";
}
return o;
}
//! the child id
long m_fileId;
//! two values
int m_values[2];
};
};
Group::~Group()
{
}
std::string Group::print() const
{
std::stringstream s;
for (size_t i = 0; i < m_childsList.size(); ++i)
s << "chld" << i << "=[" << m_childsList[i] << "],";
return s.str();
}
////////////////////////////////////////
//! Internal: the picture of a HanMacWrdKGraph
struct PictureFrame final : public Frame {
//! constructor
explicit PictureFrame(Frame const &orig)
: Frame(orig)
, m_pictureType(0)
, m_dim(0,0)
, m_borderDim(0,0)
{
for (auto &val : m_values) val=0;
}
//! destructor
~PictureFrame() final;
//! operator<<
friend std::ostream &operator<<(std::ostream &o, PictureFrame const &picture)
{
o << picture.print();
o << static_cast<Frame const &>(picture);
return o;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_pictureType) s << "type?=" << m_pictureType << ",";
if (m_dim[0] || m_dim[1])
s << "dim?=" << m_dim << ",";
if (m_borderDim[0] > 0 || m_borderDim[1] > 0)
s << "borderDim?=" << m_borderDim << ",";
for (int i = 0; i < 7; ++i) {
if (m_values[i]) s << "f" << i << "=" << m_values[i];
}
return s.str();
}
//! a type
int m_pictureType;
//! a dim?
MWAWVec2i m_dim;
//! the border dim?
MWAWVec2f m_borderDim;
//! some unknown int
int m_values[7];
};
PictureFrame::~PictureFrame()
{
}
////////////////////////////////////////
//! a table cell in a table in HanMacWrdKGraph
struct TableCell final : public MWAWCell {
//! constructor
TableCell()
: MWAWCell()
, m_id(-1)
, m_fileId(-1)
, m_flags(0)
, m_extra("")
{
}
//! 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 id ( corresponding to the last data in the main zones list )
long m_id;
//! the file id
long m_fileId;
//! the cell data
int m_flags;
//! extra data
std::string m_extra;
};
std::ostream &operator<<(std::ostream &o, TableCell const &cell)
{
o << static_cast<MWAWCell const &>(cell);
if (cell.m_flags&0x10) o << "lock,";
if (cell.m_flags&0xFFE2)
o << "linesFlags=" << std::hex << (cell.m_flags&0xFFE2) << std::dec << ",";
if (cell.m_id > 0)
o << "cellId=" << std::hex << cell.m_id << std::dec << ",";
if (cell.m_fileId > 0)
o << "fileId=" << std::hex << cell.m_fileId << std::dec << ",";
o << cell.m_extra;
return o;
}
////////////////////////////////////////
//! Internal: the table of a HanMacWrdKGraph
struct Table final : public Frame, public MWAWTable {
//! constructor
Table(Frame const &orig, HanMacWrdKGraph &parser)
: Frame(orig)
, MWAWTable(MWAWTable::CellPositionBit|MWAWTable::SizeBit)
, m_parser(&parser)
, m_rows(0)
, m_columns(0)
, m_numCells(0)
, m_textFileId(-1)
{
}
//! destructor
~Table() final;
//! return the i^th table cell
TableCell *get(int i)
{
auto cell=MWAWTable::get(i);
return static_cast<TableCell *>(cell.get());
}
//! send a text zone
bool sendText(long textId, long id) const
{
return m_parser->sendText(textId, id);
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Table const &table)
{
o << table.print();
o << static_cast<Frame const &>(table);
return o;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_rows)
s << "nRows=" << m_rows << ",";
if (m_columns)
s << "nColumns=" << m_columns << ",";
if (m_numCells)
s << "nCells=" << m_numCells << ",";
if (m_textFileId>0)
s << "textFileId=" << std::hex << m_textFileId << std::dec << ",";
return s.str();
}
//! the graph parser
HanMacWrdKGraph *m_parser;
//! the number of row
int m_rows;
//! the number of columns
int m_columns;
//! the number of cells
int m_numCells;
//! the text file id
long m_textFileId;
private:
Table(Table const &orig) = delete;
Table &operator=(Table const &orig) = delete;
};
Table::~Table()
{
}
////////////////////////////////////////
//! Internal: the textbox of a HanMacWrdKGraph
struct TextBox final : public Frame {
//! constructor
TextBox(Frame const &orig, bool isComment)
: Frame(orig)
, m_commentBox(isComment)
, m_textFileId(-1)
, m_linkedIdList()
, m_isLinked(false)
{
for (auto &d : m_dim) d = 0;
}
//! destructor
~TextBox() final;
//! returns true if the box is linked to other textbox
bool isLinked() const
{
return !m_linkedIdList.empty() || m_isLinked;
}
//! add property to frame extra values
void addTo(MWAWGraphicStyle &style) const
{
if (m_type == 10) {
MWAWBorder border;
border.m_width=double(m_style.m_lineWidth);
border.m_color = m_style.m_lineColor;
style.setBorders(libmwaw::LeftBit|libmwaw::BottomBit|libmwaw::RightBit, border);
border.m_width=double(m_borders[0][1]*m_style.m_lineWidth);
style.setBorders(libmwaw::TopBit, border);
}
else 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(("HanMacWrdKGraphInternal::TextBox::addTo: unexpected type\n"));
break;
}
style.setBorders(15, border);
}
// now the link
if (m_type==4 && m_isLinked) {
librevenge::RVNGString fName;
fName.sprintf("Frame%ld", m_fileId);
style.m_frameName=fName.cstr();
}
if (m_type==4 && !m_linkedIdList.empty()) {
librevenge::RVNGString fName;
fName.sprintf("Frame%ld", m_linkedIdList[0]);
style.m_frameNextName=fName.cstr();
}
if (m_style.hasSurfaceColor())
style.setBackgroundColor(m_style.m_surfaceColor);
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, TextBox const &textbox)
{
o << textbox.print();
o << static_cast<Frame const &>(textbox);
return o;
}
//! print local data
std::string print() const
{
std::stringstream s;
if (m_dim[0] > 0 || m_dim[1] > 0)
s << "commentsDim2=" << m_dim[0] << "x" << m_dim[1] << ",";
if (m_textFileId>0)
s << "textFileId=" << std::hex << m_textFileId << std::dec << ",";
if (!m_linkedIdList.empty()) {
s << "link[to]=[";
for (auto &id : m_linkedIdList)
s << std::hex << id << std::dec << ",";
s << "],";
}
return s.str();
}
//! a flag to know if this is a comment textbox
bool m_commentBox;
//! the text file id
long m_textFileId;
//! two auxilliary dim for memo textbox
float m_dim[2];
//! the list of linked remaining textbox id
std::vector<long> m_linkedIdList;
//! a flag to know if this textbox is linked to a previous box
bool m_isLinked;
};
TextBox::~TextBox()
{
}
bool TableCell::sendContent(MWAWListenerPtr, MWAWTable &table)
{
if (m_id < 0)
return true;
return static_cast<Table &>(table).sendText(m_fileId, m_id);
}
////////////////////////////////////////
//! Internal: the picture of a HanMacWrdKGraph
struct Picture {
//! constructor
explicit Picture(std::shared_ptr<HanMacWrdKZone> const &zone)
: m_zone(zone)
, m_fileId(-1)
, m_fileSubId(-1)
, m_parsed(false)
, m_extra("")
{
for (auto &pos : m_pos) pos=0;
}
//! destructor
~Picture()
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Picture const &pict)
{
if (pict.m_fileId >= 0)
o << "fileId=" << std::hex << pict.m_fileId << std::dec << ",";
o << pict.m_extra;
return o;
}
//! the main zone
std::shared_ptr<HanMacWrdKZone> m_zone;
//! the first and last position of the picture data in the zone
long m_pos[2];
//! the file id
long m_fileId;
//! the file subid
long m_fileSubId;
//! a flag to know if the picture was send to the receiver
mutable bool m_parsed;
//! extra data
std::string m_extra;
};
////////////////////////////////////////
//! Internal: the pattern of a HanMacWrdKGraph
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 HanMacWrdKGraph
struct State {
//! constructor
State()
: m_numPages(0)
, m_framesMap()
, m_picturesMap()
, m_colorList()
, m_patternList()
{
}
//! 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(("HanMacWrdKGraphInternal::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(("HanMacWrdKGraphInternal::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();
int m_numPages /* the number of pages */;
//! a map fileId -> frame
std::multimap<long, std::shared_ptr<Frame> > m_framesMap;
//! a map fileId -> picture
std::map<long, std::shared_ptr<Picture> > m_picturesMap;
//! a list colorId -> color
std::vector<MWAWColor> m_colorList;
//! the patterns list
std::vector<Pattern> m_patternList;
};
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 HanMacWrdKGraph
class SubDocument final : public MWAWSubDocument
{
public:
//! the document type
enum Type { Picture, FrameInFrame, Group, Text, UnformattedTable, EmptyPicture };
//! constructor
SubDocument(HanMacWrdKGraph &pars, MWAWInputStreamPtr const &input, Type type, long id, long subId=0)
: MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
, m_graphParser(&pars)
, m_type(type)
, m_id(id)
, m_subId(subId)
, m_pos()
{
}
//! constructor
SubDocument(HanMacWrdKGraph &pars, MWAWInputStreamPtr const &input, MWAWPosition const &pos, Type type, long id, long subId=0)
: MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
, m_graphParser(&pars)
, m_type(type)
, m_id(id)
, m_subId(subId)
, 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 */
HanMacWrdKGraph *m_graphParser;
//! the zone type
Type m_type;
//! the zone id
long m_id;
//! the zone subId ( for table cell )
long m_subId;
//! 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(("HanMacWrdKGraphInternal::SubDocument::parse: no listener\n"));
return;
}
if (!m_graphParser) {
MWAW_DEBUG_MSG(("HanMacWrdKGraphInternal::SubDocument::parse: no parser\n"));
return;
}
long pos = m_input->tell();
if (listener->getType()==MWAWListener::Graphic) {
if (m_type!=Text) {
MWAW_DEBUG_MSG(("HanMacWrdKGraphInternal::SubDocument::parse: unexpected type\n"));
return;
}
m_graphParser->sendText(m_id, m_subId, listener);
}
else {
switch (m_type) {
case FrameInFrame:
m_graphParser->sendFrame(m_id, m_pos);
break;
case Group:
m_graphParser->sendGroup(m_id, m_pos);
break;
case Picture:
m_graphParser->sendPicture(m_id, m_pos);
break;
case UnformattedTable:
m_graphParser->sendTableUnformatted(m_id);
break;
case Text:
m_graphParser->sendText(m_id, m_subId);
break;
case EmptyPicture:
m_graphParser->sendEmptyPicture(m_pos);
break;
#if !defined(__clang__)
default:
MWAW_DEBUG_MSG(("HanMacWrdKGraphInternal::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_subId != sDoc->m_subId) return true;
if (m_pos != sDoc->m_pos) return true;
return false;
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
HanMacWrdKGraph::HanMacWrdKGraph(HanMacWrdKParser &parser)
: m_parserState(parser.getParserState())
, m_state(new HanMacWrdKGraphInternal::State)
, m_mainParser(&parser)
{
}
HanMacWrdKGraph::~HanMacWrdKGraph()
{ }
int HanMacWrdKGraph::version() const
{
return m_parserState->m_version;
}
bool HanMacWrdKGraph::getColor(int colId, int patternId, MWAWColor &color) const
{
if (patternId==0) // ie. the empty pattern
return false;
if (!m_state->getColor(colId, color)) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::getColor: can not find color for id=%d\n", colId));
return false;
}
HanMacWrdKGraphInternal::Pattern pattern;
if (!m_state->getPattern(patternId, pattern)) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::getColor: can not find pattern for id=%d\n", patternId));
return false;
}
color = m_state->getColor(color, pattern.m_percent);
return true;
}
int HanMacWrdKGraph::numPages() const
{
if (m_state->m_numPages)
return m_state->m_numPages;
int nPages = 0;
for (auto fIt : m_state->m_framesMap) {
if (!fIt.second) continue;
int page = fIt.second->m_page+1;
if (page <= nPages) continue;
if (page >= nPages+100) continue; // a pb ?
nPages = page;
}
m_state->m_numPages = nPages;
return nPages;
}
bool HanMacWrdKGraph::sendText(long textId, long id, MWAWListenerPtr const &listener)
{
return m_mainParser->sendText(textId, id, listener);
}
std::map<long,int> HanMacWrdKGraph::getTextFrameInformations() const
{
std::map<long,int> mapIdType;
for (auto fIt : m_state->m_framesMap) {
if (!fIt.second) continue;
auto const &frame = *fIt.second;
std::vector<long> listId;
if (frame.m_type!=3 && frame.m_type!=4 && frame.m_type != 9 && frame.m_type!=10)
continue;
switch (frame.m_type) {
case 3:
listId.push_back(static_cast<HanMacWrdKGraphInternal::FootnoteFrame const &>(frame).m_textFileId);
break;
case 4:
case 10:
listId.push_back(static_cast<HanMacWrdKGraphInternal::TextBox const &>(frame).m_textFileId);
break;
case 9: {
auto &table = const_cast<HanMacWrdKGraphInternal::Table &>
(static_cast<HanMacWrdKGraphInternal::Table const &>(frame));
for (int c=0; c < table.numCells(); ++c) {
if (table.get(c))
listId.push_back(table.get(c)->m_fileId);
}
break;
}
default:
break;
}
for (auto zId : listId) {
if (mapIdType.find(zId) == mapIdType.end())
mapIdType[zId] = frame.m_type;
else if (mapIdType.find(zId)->second != frame.m_type) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::getTextFrameInformations: id %lx already set\n", static_cast<long unsigned int>(zId)));
}
}
}
return mapIdType;
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
// a small zone: related to frame pos + data?
bool HanMacWrdKGraph::readFrames(std::shared_ptr<HanMacWrdKZone> zone)
{
if (!zone) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readFrames: called without any zone\n"));
return false;
}
long dataSz = zone->length();
if (dataSz < 70) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readFrames: the zone seems too short\n"));
return false;
}
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->ascii();
libmwaw::DebugStream f;
zone->m_parsed = true;
long pos=0;
input->seek(pos, librevenge::RVNG_SEEK_SET);
long val;
HanMacWrdKGraphInternal::Frame graph;
graph.m_type = static_cast<int>(input->readULong(1));
val = long(input->readULong(1));
if (val) f << "#f0=" << std::hex << val << std::dec << ",";
graph.m_posFlags = static_cast<int>(input->readULong(1));
if (graph.m_posFlags&2) graph.m_inGroup=true;
val = long(input->readULong(1));
if (val) f << "#f1=" << std::hex << val << std::dec << ",";
graph.m_page = static_cast<int>(input->readLong(2));
float dim[4];
for (float &i : dim)
i = float(input->readLong(4))/65536.f;
graph.m_pos = MWAWBox2f(MWAWVec2f(dim[0],dim[1]),MWAWVec2f(dim[2],dim[3]));
for (auto &border : graph.m_borders) { // border size, 0=Top, other unknown
float bd[2];
for (auto &j : bd) j = float(input->readLong(4))/65536.f;
border = MWAWVec2f(bd[0],bd[1]);
}
MWAWGraphicStyle &style = graph.m_style;
style.m_lineWidth = float(input->readLong(4))/65536.f;
graph.m_borderType= static_cast<int>(input->readULong(2));
if (val) f << "#g0=" << val << ","; // border type?
for (int i = 0; i < 2; ++i) {
auto color = static_cast<int>(input->readULong(2));
MWAWColor col;
if (!m_state->getColor(color, col)) {
f << "#color[" << i << "]=" << color << ", pat="<<input->readULong(2) << ",";
continue;
}
auto pattern = static_cast<int>(input->readULong(2));
if (pattern==0) {
if (i==0)
style.m_lineOpacity=0;
else
style.m_surfaceOpacity=0;
continue;
}
HanMacWrdKGraphInternal::Pattern pat;
if (m_state->getPattern(pattern, pat)) {
pat.m_colors[1]=col;
if (!pat.getUniqueColor(col)) {
pat.getAverageColor(col);
if (i) style.setPattern(pat);
}
}
else
f << "#pattern[" << i << "]=" << pattern << ",";
if (i==0)
style.m_lineColor=col;
else
style.setSurfaceColor(col,1);
}
graph.m_id=static_cast<int>(input->readLong(2));
graph.m_baseline = float(input->readLong(4))/65536.f;
for (int i = 1; i < 3; ++i) {
val = long(input->readULong(2));
if (val) f << "#g" << i << "=" << val << ",";
}
graph.m_extra=f.str();
f.str("");
f << zone->name() << "(A):PTR=" << std::hex << zone->fileBeginPos() << std::dec << "," << graph;
graph.m_fileId = zone->m_id;
graph.m_fileSubId = zone->m_subId;
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
std::shared_ptr<HanMacWrdKGraphInternal::Frame> frame;
switch (graph.m_type) {
case 3:
frame = readFootnoteFrame(zone, graph);
break;
case 4:
case 10:
frame = readTextBox(zone, graph,graph.m_type==10);
break;
case 6:
frame = readPictureFrame(zone, graph);
break;
case 8:
frame = readShapeGraph(zone, graph);
break;
case 9:
frame = readTable(zone, graph);
break;
case 11:
frame = readGroup(zone, graph);
break;
default:
break;
}
if (frame)
m_state->m_framesMap.insert
(std::multimap<long, std::shared_ptr<HanMacWrdKGraphInternal::Frame> >::value_type
(zone->m_id, frame));
return true;
}
// read a picture
bool HanMacWrdKGraph::readPicture(std::shared_ptr<HanMacWrdKZone> zone)
{
if (!zone) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readPicture: called without any zone\n"));
return false;
}
long dataSz = zone->length();
if (dataSz < 86) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readPicture: the zone seems too short\n"));
return false;
}
MWAWInputStreamPtr input = zone->m_input;
libmwaw::DebugFile &asciiFile = zone->ascii();
libmwaw::DebugStream f;
zone->m_parsed = true;
std::shared_ptr<HanMacWrdKGraphInternal::Picture> picture(new HanMacWrdKGraphInternal::Picture(zone));
long pos=0;
input->seek(pos, librevenge::RVNG_SEEK_SET);
picture->m_fileId = long(input->readULong(4));
for (int i=0; i < 39; ++i) {
long val = input->readLong(2);
if (val) f << "f" << i << "=" << val << ",";
}
auto pictSz = long(input->readULong(4));
if (pictSz < 0 || pictSz+86 > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readPicture: problem reading the picture size\n"));
return false;
}
picture->m_pos[0] = input->tell();
picture->m_pos[1] = picture->m_pos[0]+pictSz;
picture->m_extra = f.str();
long fId = picture->m_fileId;
if (!fId) fId = zone->m_id;
picture->m_fileSubId = zone->m_subId;
if (m_state->m_picturesMap.find(fId) != m_state->m_picturesMap.end())
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readPicture: oops I already find a picture for %lx\n", static_cast<long unsigned int>(fId)));
else
m_state->m_picturesMap[fId] = picture;
f.str("");
f << zone->name() << ":PTR=" << std::hex << zone->fileBeginPos() << std::dec << "," << *picture;
f << "pictSz=" << pictSz << ",";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
asciiFile.skipZone(picture->m_pos[0], picture->m_pos[1]-1);
return true;
}
////////////////////////////////////////////////////////////
// send data to a listener
////////////////////////////////////////////////////////////
bool HanMacWrdKGraph::sendPicture(long pictId, MWAWPosition const &pos)
{
if (!m_parserState->m_textListener) return true;
auto pIt = m_state->m_picturesMap.find(pictId);
if (pIt == m_state->m_picturesMap.end() || !pIt->second) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendPicture: can not find the picture %lx\n", static_cast<long unsigned int>(pictId)));
return false;
}
sendPicture(*pIt->second, pos);
return true;
}
bool HanMacWrdKGraph::sendPicture(HanMacWrdKGraphInternal::Picture const &picture, MWAWPosition const &pos)
{
#ifdef DEBUG_WITH_FILES
bool firstTime = picture.m_parsed == false;
#endif
picture.m_parsed = true;
if (!m_parserState->m_textListener) return true;
if (!picture.m_zone || picture.m_pos[0] >= picture.m_pos[1]) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendPicture: can not find the picture\n"));
return false;
}
MWAWInputStreamPtr input = picture.m_zone->m_input;
librevenge::RVNGBinaryData data;
input->seek(picture.m_pos[0], librevenge::RVNG_SEEK_SET);
input->readDataBlock(picture.m_pos[1]-picture.m_pos[0], data);
#ifdef DEBUG_WITH_FILES
if (firstTime) {
libmwaw::DebugStream f;
static int volatile pictName = 0;
f << "Pict" << ++pictName << ".pct";
libmwaw::Debug::dumpFile(data, f.str().c_str());
}
#endif
m_parserState->m_textListener->insertPicture(pos, MWAWEmbeddedObject(data, "image/pict"));
return true;
}
bool HanMacWrdKGraph::sendFrame(long frameId, MWAWPosition const &lPos)
{
if (!m_parserState->m_textListener) return true;
auto fIt=m_state->m_framesMap.find(frameId);
if (fIt == m_state->m_framesMap.end() || !fIt->second) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendFrame: can not find frame %lx\n", static_cast<long unsigned int>(frameId)));
return false;
}
auto pos(lPos);
if (pos.size()[0]<=0 || pos.size()[1]<=0)
pos.setSize(fIt->second->m_pos.size());
return sendFrame(*fIt->second, pos);
}
bool HanMacWrdKGraph::sendFrame(HanMacWrdKGraphInternal::Frame const &frame, MWAWPosition const &lPos)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) return true;
auto pos(lPos);
frame.m_parsed = true;
MWAWInputStreamPtr &input= m_parserState->m_input;
switch (frame.m_type) {
case 3: {
auto const &ftnote=static_cast<HanMacWrdKGraphInternal::FootnoteFrame const &>(frame);
MWAWSubDocumentPtr subdoc
(new HanMacWrdKGraphInternal::SubDocument(*this, input, HanMacWrdKGraphInternal::SubDocument::Text,
ftnote.m_textFileId, ftnote.m_textFileSubId));
listener->insertNote(MWAWNote(MWAWNote::FootNote),subdoc);
break;
}
case 4:
// fixme: check also for border
if (frame.m_style.hasPattern()) {
auto const &textbox=static_cast<HanMacWrdKGraphInternal::TextBox const &>(frame);
if (!textbox.isLinked() && m_mainParser->canSendTextAsGraphic(textbox.m_textFileId,0)) {
textbox.m_parsed=true;
MWAWSubDocumentPtr subdoc
(new HanMacWrdKGraphInternal::SubDocument(*this, input, HanMacWrdKGraphInternal::SubDocument::Text, textbox.m_textFileId));
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, textbox.m_style);
graphicListener.endDocument();
MWAWEmbeddedObject picture;
if (!graphicEncoder.getBinaryResult(picture))
return false;
listener->insertPicture(pos, picture);
return true;
}
}
MWAW_FALLTHROUGH;
case 10:
return sendTextBox(static_cast<HanMacWrdKGraphInternal::TextBox const &>(frame), pos);
case 6: {
auto const &pict = static_cast<HanMacWrdKGraphInternal::PictureFrame const &>(frame);
if (pict.m_fileId==0) {
if (pos.size()[0] <= 0 || pos.size()[1] <= 0)
pos.setSize(pict.getBdBox().size());
MWAWPosition framePos(pos);
framePos.m_anchorTo = MWAWPosition::Frame;
framePos.setOrigin(MWAWVec2f(0,0));
MWAWSubDocumentPtr subdoc
(new HanMacWrdKGraphInternal::SubDocument
(*this, input, framePos, HanMacWrdKGraphInternal::SubDocument::EmptyPicture, pict.m_fileId));
listener->insertTextBox(pos, subdoc);
return true;
}
return sendPictureFrame(pict, pos);
}
case 8:
return sendShapeGraph(static_cast<HanMacWrdKGraphInternal::ShapeGraph const &>(frame), pos);
case 9: {
auto &table = const_cast<HanMacWrdKGraphInternal::Table &>
(static_cast<HanMacWrdKGraphInternal::Table const &>(frame));
if (!table.updateTable()) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendFrame: can not find the table structure\n"));
MWAWSubDocumentPtr subdoc
(new HanMacWrdKGraphInternal::SubDocument
(*this, input, HanMacWrdKGraphInternal::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 HanMacWrdKGraphInternal::SubDocument
(*this, input, framePos, HanMacWrdKGraphInternal::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 11: {
auto const &group=static_cast<HanMacWrdKGraphInternal::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));
MWAWSubDocumentPtr subdoc
(new HanMacWrdKGraphInternal::SubDocument
(*this, input, framePos, HanMacWrdKGraphInternal::SubDocument::Group, frame.m_fileId));
listener->insertTextBox(pos, subdoc);
return true;
}
sendGroup(group, pos);
break;
}
default:
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendFrame: sending type %d is not implemented\n", frame.m_type));
break;
}
return false;
}
bool HanMacWrdKGraph::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;
}
bool HanMacWrdKGraph::sendPictureFrame(HanMacWrdKGraphInternal::PictureFrame 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());
//fixme: check if we have border
sendPicture(pict.m_fileId, pos);
return true;
}
bool HanMacWrdKGraph::sendTextBox(HanMacWrdKGraphInternal::TextBox const &textbox, MWAWPosition const &lPos)
{
if (!m_parserState->m_textListener) return true;
MWAWVec2f textboxSz = textbox.getBdBox().size();
auto pos(lPos);
if (textbox.m_type==10) {
if (textbox.m_dim[0] > textboxSz[0]) textboxSz[0]=textbox.m_dim[0];
if (textbox.m_dim[1] > textboxSz[1]) textboxSz[1]=textbox.m_dim[1];
pos.setSize(textboxSz);
pos.setOrder(100); // put note in front
}
else if (pos.size()[0] <= 0 || pos.size()[1] <= 0)
pos.setSize(textboxSz);
MWAWGraphicStyle style;
textbox.addTo(style);
MWAWSubDocumentPtr subdoc;
if (!textbox.m_isLinked)
subdoc.reset(new HanMacWrdKGraphInternal::SubDocument(*this, m_parserState->m_input, HanMacWrdKGraphInternal::SubDocument::Text, textbox.m_textFileId));
m_parserState->m_textListener->insertTextBox(pos, subdoc, style);
return true;
}
bool HanMacWrdKGraph::sendShapeGraph(HanMacWrdKGraphInternal::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());
pos.setOrigin(pos.origin());
pos.setSize(pos.size()+MWAWVec2f(4,4));
m_parserState->m_textListener->insertShape(pos,pict.m_shape,pict.getStyle());
return true;
}
// ----- table
bool HanMacWrdKGraph::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() || !fIt->second || fIt->second->m_type != 9) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendTableUnformatted: can not find table %lx\n", static_cast<long unsigned int>(fId)));
return false;
}
auto &table = static_cast<HanMacWrdKGraphInternal::Table &>(*fIt->second);
return table.sendAsText(m_parserState->m_textListener);
}
////////////////////////////////////////////////////////////
// low level
////////////////////////////////////////////////////////////
// try to read a small graphic
std::shared_ptr<HanMacWrdKGraphInternal::ShapeGraph> HanMacWrdKGraph::readShapeGraph(std::shared_ptr<HanMacWrdKZone> zone, HanMacWrdKGraphInternal::Frame const &header)
{
std::shared_ptr<HanMacWrdKGraphInternal::ShapeGraph> graph;
if (!zone) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readShapeGraph: called without any zone\n"));
return graph;
}
MWAWInputStreamPtr input = zone->m_input;
long dataSz = zone->length();
long pos = input->tell();
if (pos+26 > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readShapeGraph: the zone seems too short\n"));
return graph;
}
graph.reset(new HanMacWrdKGraphInternal::ShapeGraph(header));
libmwaw::DebugFile &asciiFile = zone->ascii();
libmwaw::DebugStream f;
auto graphType = static_cast<int>(input->readLong(1));
long val;
bool ok = true;
MWAWBox2f bdbox=graph->m_pos;
MWAWGraphicShape &shape=graph->m_shape;
shape = MWAWGraphicShape();
shape.m_bdBox = shape.m_formBox = bdbox;
switch (graphType) {
case 0:
case 3: { // lines
if (pos+28 > dataSz) {
f << "###";
ok = false;
break;
}
shape.m_type=MWAWGraphicShape::Line;
auto arrowFlags=static_cast<int>(input->readULong(1));
if (arrowFlags&1) graph->m_style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain();
if (arrowFlags&2) graph->m_style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
if (arrowFlags&0xFC) f << "#arrowsFl=" << (arrowFlags & 0xFC) << ",";
for (int i = 0; i < 5; ++i) { // always 0
val = input->readLong(2);
if (val) f << "f" << i << "=" << val << ",";
}
float coord[2];
for (int pt = 0; pt < 2; ++pt) {
for (auto &c : coord) c = float(input->readLong(4))/65536.f;
MWAWVec2f vertex=MWAWVec2f(coord[1],coord[0]);
shape.m_vertices.push_back(vertex);
}
break;
}
case 1: // rectangle
case 2: // circle
shape.m_type = graphType==1?
MWAWGraphicShape::Rectangle : MWAWGraphicShape::Circle;
for (int i = 0; i < 13; ++i) { // always 0
val = input->readLong(2);
if (val) f << "f" << i << "=" << val << ",";
}
break;
case 4: { // rectOval
if (pos+28 > dataSz) {
f << "###";
ok = false;
break;
}
for (int i = 0; i < 4; ++i) {
val = input->readLong(i ? 2 : 1);
if (val) f << "f" << i << "=" << val << ",";
}
shape.m_type=MWAWGraphicShape::Rectangle;
float cornerDim = float(input->readLong(4))/65536.f;
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;
}
for (int i = 0; i < 8; ++i) {
val = input->readLong(i);
if (val) f << "g" << i << "=" << val << ",";
}
break;
}
case 5: { // arc
val = input->readLong(2);
if (val) f << "f0=" << val << ",";
auto transf = static_cast<int>(input->readULong(1));
float angles[2];
if (transf>=0 && transf <= 3) {
int decal = (transf%2) ? 4-transf : transf;
angles[0] = float(-90*decal);
angles[1] = float(90-90*decal);
}
else {
f << "#transf=" << transf << ",";
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readShapeGraph: find unexpected transformation for arc\n"));
ok = false;
break;
}
// 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]);
for (int i = 0; i < 12; ++i) { // always 0
val = input->readLong(2);
if (val) f << "f" << i+1 << "=" << val << ",";
}
break;
}
case 6: { // poly
for (int i = 0; i < 5; ++i) { // find f3=0|1, other 0
val = input->readLong(1);
if (val) f << "f" << i << "=" << val << ",";
}
auto numPt = static_cast<int>(input->readLong(2));
if (numPt < 0 || 28+8*numPt > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readShapeGraph: find unexpected number of points\n"));
f << "#pt=" << numPt << ",";
ok = false;
break;
}
for (int i = 0; i < 10; ++i) { //always 0
val = input->readLong(2);
if (val) f << "g" << i << "=" << val << ",";
}
shape.m_type=MWAWGraphicShape::Polygon;
MWAWVec2f minPt(0,0);
for (int i = 0; i < numPt; ++i) {
float dim[2];
for (auto &d : dim) d = float(input->readLong(4))/65536.f;
MWAWVec2f vertex=MWAWVec2f(dim[1], dim[0])+bdbox[0];
shape.m_vertices.push_back(vertex);
}
break;
}
default:
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readShapeGraph: find unexpected graphic subType\n"));
f << "###type=" << graphType << ",";
ok = false;
break;
}
std::string extra = f.str();
graph->m_extra += extra;
f.str("");
f << "FrameDef(graphData):" << graph->print() << extra;
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
if (!ok)
graph.reset();
return graph;
}
// try to read the group data
std::shared_ptr<HanMacWrdKGraphInternal::Group> HanMacWrdKGraph::readGroup(std::shared_ptr<HanMacWrdKZone> zone, HanMacWrdKGraphInternal::Frame const &header)
{
std::shared_ptr<HanMacWrdKGraphInternal::Group> group;
if (!zone) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readGroup: called without any zone\n"));
return group;
}
MWAWInputStreamPtr input = zone->m_input;
long dataSz = zone->length();
long pos = input->tell();
if (pos+2 > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readGroup: the zone seems too short\n"));
return group;
}
auto N = static_cast<int>(input->readULong(2));
if (pos+2+8*N > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readGroup: can not read N\n"));
return group;
}
group.reset(new HanMacWrdKGraphInternal::Group(header));
libmwaw::DebugFile &asciiFile = zone->ascii();
libmwaw::DebugStream f;
for (int i = 0; i < N; ++i) {
HanMacWrdKGraphInternal::Group::Child child;
child.m_fileId = long(input->readULong(4));
for (auto &val : child.m_values) // f0=4|6|8, f1=0
val = static_cast<int>(input->readLong(2));
group->m_childsList.push_back(child);
}
f << "FrameDef(groupData):" << group->print();
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return group;
}
// try to read a picture frame
std::shared_ptr<HanMacWrdKGraphInternal::PictureFrame> HanMacWrdKGraph::readPictureFrame(std::shared_ptr<HanMacWrdKZone> zone, HanMacWrdKGraphInternal::Frame const &header)
{
std::shared_ptr<HanMacWrdKGraphInternal::PictureFrame> picture;
if (!zone) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readPicture: called without any zone\n"));
return picture;
}
MWAWInputStreamPtr input = zone->m_input;
long dataSz = zone->length();
long pos = input->tell();
if (pos+32 > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readPicture: the zone seems too short\n"));
return picture;
}
picture.reset(new HanMacWrdKGraphInternal::PictureFrame(header));
libmwaw::DebugFile &asciiFile = zone->ascii();
libmwaw::DebugStream f;
picture->m_pictureType = static_cast<int>(input->readLong(2)); // 0 or 4 : or maybe wrapping
for (int i = 0; i < 5; ++i) // always 0
picture->m_values[i] = static_cast<int>(input->readLong(2));
float bDim[2];
for (auto &d : bDim) d = float(input->readLong(4))/65536.f;
picture->m_borderDim = MWAWVec2f(bDim[0], bDim[1]);
for (int i = 5; i < 7; ++i) // always 0
picture->m_values[i] = static_cast<int>(input->readLong(2));
int dim[2]; //0,0 or 3e,3c
for (auto &d : dim) d = static_cast<int>(input->readLong(2));
picture->m_dim = MWAWVec2i(dim[0], dim[1]);
picture->m_fileId = long(input->readULong(4));
f << "FrameDef(pictureData):";
if (picture->m_fileId)
f << "fId=" << std::hex << picture->m_fileId << std::dec << ",";
f << picture->print();
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return picture;
}
// try to read the footnote data
std::shared_ptr<HanMacWrdKGraphInternal::FootnoteFrame> HanMacWrdKGraph::readFootnoteFrame(std::shared_ptr<HanMacWrdKZone> zone, HanMacWrdKGraphInternal::Frame const &header)
{
std::shared_ptr<HanMacWrdKGraphInternal::FootnoteFrame> ftn;
if (!zone) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readFootnoteFrame: called without any zone\n"));
return ftn;
}
MWAWInputStreamPtr input = zone->m_input;
long dataSz = zone->length();
long pos = input->tell();
if (pos+24 > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readFootnoteFrame: the zone seems too short\n"));
return ftn;
}
ftn.reset(new HanMacWrdKGraphInternal::FootnoteFrame(header));
libmwaw::DebugFile &asciiFile = zone->ascii();
libmwaw::DebugStream f;
for (int i = 0; i < 9; ++i) { // always 0?
long val = input->readLong(2);
if (val) f << "f" << i << "=" << val << ",";
}
ftn->m_textFileSubId = long(input->readULong(2));
ftn->m_textFileId = long(input->readULong(4));
std::string extra = f.str();
ftn->m_extra += extra;
f.str("");
f << "FrameDef(footnoteData):" << ftn->print() << extra;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return ftn;
}
// try to read the textbox data
std::shared_ptr<HanMacWrdKGraphInternal::TextBox> HanMacWrdKGraph::readTextBox(std::shared_ptr<HanMacWrdKZone> zone, HanMacWrdKGraphInternal::Frame const &header, bool isMemo)
{
std::shared_ptr<HanMacWrdKGraphInternal::TextBox> textbox;
if (!zone) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readTextBox: called without any zone\n"));
return textbox;
}
MWAWInputStreamPtr input = zone->m_input;
long dataSz = zone->length();
long pos = input->tell();
int expectedSize = isMemo ? 20 : 12;
if (pos+expectedSize > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readTextBox: the zone seems too short\n"));
return textbox;
}
textbox.reset(new HanMacWrdKGraphInternal::TextBox(header, isMemo));
libmwaw::DebugFile &asciiFile = zone->ascii();
libmwaw::DebugStream f;
for (int i = 0; i < 3; ++i) { // 0|1,0|1,0|1,numtextbox linked
auto val = static_cast<int>(input->readLong(1));
if (val) f << "f" << i << "=" << val << ",";
}
auto numLinks=static_cast<int>(input->readLong(1));
if (numLinks!=(isMemo ? 0 : 1)) f << "numLinks=" << numLinks << ",";
auto fChar=long(input->readULong(4));
if (fChar) f << "first[char]=" << fChar << ",";
textbox->m_textFileId = long(input->readULong(4));
if (isMemo) { // checkme
for (int i = 0; i < 2; ++i)
textbox->m_dim[1-i] = float(input->readLong(4))/65536.f;
}
else if (numLinks>1 && pos+12+4*(numLinks-1) <= dataSz) {
for (int l=1; l<numLinks; ++l)
textbox->m_linkedIdList.push_back(input->readLong(4));
}
textbox->m_extra=f.str();
f.str("");
f << "FrameDef(textboxData):";
f << "fId=" << std::hex << textbox->m_textFileId << std::dec << "," << textbox->print();
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
return textbox;
}
// try to read the table data
std::shared_ptr<HanMacWrdKGraphInternal::Table> HanMacWrdKGraph::readTable(std::shared_ptr<HanMacWrdKZone> zone, HanMacWrdKGraphInternal::Frame const &header)
{
std::shared_ptr<HanMacWrdKGraphInternal::Table> table;
if (!zone) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readTable: called without any zone\n"));
return table;
}
MWAWInputStreamPtr input = zone->m_input;
long dataSz = zone->length();
long pos = input->tell();
if (pos+20 > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readTable: the zone seems too short\n"));
return table;
}
table.reset(new HanMacWrdKGraphInternal::Table(header, *this));
libmwaw::DebugFile &asciiFile = zone->ascii();
libmwaw::DebugStream f, f2;
long val;
for (int i = 0; i < 4; ++i) {
val = input->readLong(2);
if (val) f << "f" << i << "=" << val << ",";
}
table->m_textFileId = long(input->readULong(4));
table->m_rows = static_cast<int>(input->readLong(2));
table->m_columns = static_cast<int>(input->readLong(2));
table->m_numCells = static_cast<int>(input->readLong(2));
val = input->readLong(2);
if (val) f << "f4=" << val << ",";
std::string extra = f.str();
table->m_extra += extra;
f.str("");
f << "FrameDef(tableData):";
f << "fId=" << std::hex << table->m_textFileId << std::dec << ","
<< table->print() << extra;
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
for (int i = 0; i < table->m_numCells; ++i) {
if (input->isEnd()) break;
pos = input->tell();
f.str("");
if (pos+80 > dataSz) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readTable: can not read cell %d\n", i));
f << "FrameDef(tableCell-" << i << "):###";
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
break;
}
std::shared_ptr<HanMacWrdKGraphInternal::TableCell> cell(new HanMacWrdKGraphInternal::TableCell);
int posi[2];
for (auto &p : posi) p = static_cast<int>(input->readLong(2));
cell->setPosition(MWAWVec2i(posi[1],posi[0]));
int span[2];
for (auto &s : span) s = static_cast<int>(input->readLong(2));
if (span[0]>=1 && span[1]>=1)
cell->setNumSpannedCells(MWAWVec2i(span[1], span[0]));
else {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::readTable: can not read cell span\n"));
f << "##span=" << span[1] << "x" << span[0] << ",";
}
float dim[2];
for (auto &d : dim) d = float(input->readLong(4))/65536.f;
cell->setBdSize(MWAWVec2f(dim[0], dim[1]));
auto color = static_cast<int>(input->readULong(2));
MWAWColor backCol = MWAWColor::white();
if (!m_state->getColor(color, backCol))
f << "#backcolor=" << color << ",";
auto pattern = static_cast<int>(input->readULong(2));
HanMacWrdKGraphInternal::Pattern pat;
if (pattern && m_state->getPattern(pattern, pat))
cell->setBackgroundColor(m_state->getColor(backCol, pat.m_percent));
else if (pattern)
f << "#backPattern=" << pattern << ",";
cell->m_flags = static_cast<int>(input->readULong(2));
if (cell->m_flags&1) cell->setVAlignment(MWAWCell::VALIGN_CENTER);
switch ((cell->m_flags>>2) & 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 << "f2=" << val << ",";
static char const *what[] = {"T", "L", "B", "R"};
static int const which[] = { libmwaw::TopBit, libmwaw::LeftBit, libmwaw::BottomBit, libmwaw::RightBit };
for (int b = 0; b < 4; ++b) { // find _,4000,_,_,1,_, and 1,_,_,_,1,_,
f2.str("");
MWAWBorder border;
border.m_width=double(input->readLong(4))/65536.;
auto type = int(input->readLong(2));
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(2));
MWAWColor col = MWAWColor::black();
if (!m_state->getColor(color, col))
f2 << "#color=" << color << ",";
pattern = static_cast<int>(input->readULong(2));
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(2));
if (val) f2 << "unkn=" << val << ",";
cell->setBorders(which[b],border);
if (cell->hasExtraLine() && b==3) {
// extra line border seems to correspond to the right border
MWAWBorder extraL;
extraL.m_width=border.m_width;
extraL.m_color=border.m_color;
cell->setExtraLine(cell->extraLine(), extraL);
}
if (f2.str().length())
f << "bord" << what[b] << "=[" << f2.str() << "],";
}
cell->m_fileId = long(input->readULong(4));
cell->m_id = long(input->readULong(4));
cell->m_extra = f.str();
table->add(cell);
f.str("");
f << "FrameDef(tableCell-" << i << "):" << *cell;
asciiFile.addDelimiter(input->tell(),'|');
asciiFile.addPos(pos);
asciiFile.addNote(f.str().c_str());
input->seek(pos+80, librevenge::RVNG_SEEK_SET);
}
return table;
}
////////////////////////////////////////////////////////////
// group
////////////////////////////////////////////////////////////
bool HanMacWrdKGraph::sendGroup(long groupId, MWAWPosition const &pos)
{
if (!m_parserState->m_textListener) return true;
auto fIt=m_state->m_framesMap.find(groupId);
if (fIt == m_state->m_framesMap.end()) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendGroup: can not find group %lx\n", static_cast<long unsigned int>(groupId)));
return false;
}
auto frame=fIt->second;
if (!frame || frame->m_type!=11) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendGroup: %lx seems bad\n", static_cast<long unsigned int>(groupId)));
return false;
}
return sendGroup(static_cast<HanMacWrdKGraphInternal::Group const &>(*frame), pos);
}
bool HanMacWrdKGraph::sendGroup(HanMacWrdKGraphInternal::Group const &group, MWAWPosition const &pos)
{
group.m_parsed=true;
sendGroupChild(group,pos);
return true;
}
bool HanMacWrdKGraph::canCreateGraphic(HanMacWrdKGraphInternal::Group const &group)
{
int page = group.m_page;
for (auto const &child :group.m_childsList) {
long fId=child.m_fileId;
auto fIt=m_state->m_framesMap.find(fId);
if (fIt == m_state->m_framesMap.end() || fIt->first!=fId || !fIt->second)
continue;
auto const &frame=*fIt->second;
if (frame.m_page!=page) return false;
switch (frame.m_type) {
case 4: {
auto const &text=static_cast<HanMacWrdKGraphInternal::TextBox const &>(frame);
if (text.isLinked() || !m_mainParser->canSendTextAsGraphic(text.m_textFileId,0))
return false;
break;
}
case 8: // shape
break;
case 11:
if (!canCreateGraphic(static_cast<HanMacWrdKGraphInternal::Group const &>(frame)))
return false;
break;
default:
return false;
}
}
return true;
}
void HanMacWrdKGraph::sendGroup(HanMacWrdKGraphInternal::Group const &group, MWAWGraphicListenerPtr &listener)
{
if (!listener) return;
group.m_parsed=true;
MWAWInputStreamPtr &input= m_parserState->m_input;
for (auto const &child : group.m_childsList) {
long fId=child.m_fileId;
auto fIt=m_state->m_framesMap.find(fId);
if (fIt == m_state->m_framesMap.end() || fIt->first!=fId || !fIt->second)
continue;
auto const &frame=*fIt->second;
MWAWBox2f box=frame.getBdBox();
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<HanMacWrdKGraphInternal::TextBox const &>(frame);
MWAWSubDocumentPtr subdoc
(new HanMacWrdKGraphInternal::SubDocument(*this, input, HanMacWrdKGraphInternal::SubDocument::Text, textbox.m_textFileId));
listener->insertTextBox(pictPos, subdoc, textbox.m_style);
break;
}
case 8: {
frame.m_parsed=true;
auto const &shape=static_cast<HanMacWrdKGraphInternal::ShapeGraph const &>(frame);
listener->insertShape(pictPos, shape.m_shape, shape.getStyle());
break;
}
case 11:
sendGroup(static_cast<HanMacWrdKGraphInternal::Group const &>(frame), listener);
break;
default:
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendGroup: unexpected type %d\n", frame.m_type));
break;
}
}
}
void HanMacWrdKGraph::sendGroupChild(HanMacWrdKGraphInternal::Group const &group, MWAWPosition const &pos)
{
MWAWTextListenerPtr listener=m_parserState->m_textListener;
if (!listener) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::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;
for (size_t c=0; c<numChilds; ++c) {
long fId=group.m_childsList[c].m_fileId;
auto fIt=m_state->m_framesMap.find(fId);
if (fIt == m_state->m_framesMap.end() || fIt->first!=fId || !fIt->second) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendGroupChild: can not find child %lx\n", static_cast<long unsigned int>(fId)));
continue;
}
HanMacWrdKGraphInternal::Frame const &frame=*fIt->second;
bool canMerge=false;
if (frame.m_page==group.m_page) {
switch (frame.m_type) {
case 4: {
auto const &text=static_cast<HanMacWrdKGraphInternal::TextBox const &>(frame);
canMerge=!text.isLinked()&&m_mainParser->canSendTextAsGraphic(text.m_textFileId,0);
break;
}
case 8: // shape
canMerge = true;
break;
case 11:
canMerge = canCreateGraphic(static_cast<HanMacWrdKGraphInternal::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].m_fileId;
fIt=m_state->m_framesMap.find(localFId);
if (fIt == m_state->m_framesMap.end() || fIt->first!=localFId || !fIt->second)
continue;
HanMacWrdKGraphInternal::Frame const &child=*fIt->second;
MWAWBox2f box=child.getBdBox();
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<HanMacWrdKGraphInternal::TextBox const &>(child);
MWAWSubDocumentPtr subdoc
(new HanMacWrdKGraphInternal::SubDocument(*this, input, HanMacWrdKGraphInternal::SubDocument::Text, textbox.m_textFileId));
graphicListener->insertTextBox(pictPos, subdoc, textbox.m_style);
break;
}
case 8: {
child.m_parsed=true;
auto const &shape=static_cast<HanMacWrdKGraphInternal::ShapeGraph const &>(child);
graphicListener->insertShape(pictPos, shape.m_shape, shape.getStyle());
break;
}
case 11:
sendGroup(static_cast<HanMacWrdKGraphInternal::Group const &>(child), graphicListener);
break;
default:
MWAW_DEBUG_MSG(("HanMacWrdKGraph::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].m_fileId;
fIt=m_state->m_framesMap.find(localFId);
if (fIt != m_state->m_framesMap.end() && fIt->first==localFId && fIt->second) {
auto const &childFrame=*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);
continue;
}
MWAW_DEBUG_MSG(("HanMacWrdKGraph::sendGroupChild: can not find child %lx\n", static_cast<long unsigned int>(localFId)));
}
numDataToMerge=0;
}
}
void HanMacWrdKGraph::prepareStructures()
{
for (auto fIt : m_state->m_framesMap) {
if (!fIt.second) continue;
HanMacWrdKGraphInternal::Frame &frame = *fIt.second;
if (frame.m_type==11 && !frame.m_inGroup) {
std::multimap<long, long> seens;
checkGroupStructures(fIt.first, frame.m_fileSubId, seens, false);
}
if (frame.m_type==4) {
auto &text=static_cast<HanMacWrdKGraphInternal::TextBox &>(frame);
size_t numLink=text.m_linkedIdList.size();
for (size_t l=0; l < numLink; ++l) {
auto fIt2=m_state->m_framesMap.find(text.m_linkedIdList[l]);
if (fIt2==m_state->m_framesMap.end() || fIt2->first!=text.m_linkedIdList[l] || !fIt2->second || fIt2->second->m_type!=4) {
MWAW_DEBUG_MSG(("HanMacWrdKGraph::prepareStructures: can not find frame %lx\n", static_cast<long unsigned int>(text.m_linkedIdList[l])));
text.m_linkedIdList.resize(l);
break;
}
auto &follow=static_cast<HanMacWrdKGraphInternal::TextBox &>(*fIt2->second);
follow.m_isLinked=true;
if (l+1!=numLink)
follow.m_linkedIdList.push_back(text.m_linkedIdList[l+1]);
}
}
}
}
bool HanMacWrdKGraph::checkGroupStructures(long fileId, long fileSubId, std::multimap<long, long> &seens, bool inGroup)
{
auto it=seens.lower_bound(fileId);
while (it!=seens.end() && it->first==fileId) {
if (it->second != fileSubId)
continue;
MWAW_DEBUG_MSG(("HanMacWrdKGraph::checkGroupStructures: zone %ld[%ld] already find\n", fileId, fileSubId));
return false;
}
seens.insert(std::multimap<long, long>::value_type(fileId, fileSubId));
auto fIt = m_state->m_framesMap.lower_bound(fileId);
for (; fIt != m_state->m_framesMap.end(); ++fIt) {
if (fIt->first!=fileId) break;
if (!fIt->second) continue;
auto &frame = *fIt->second;
frame.m_inGroup=inGroup;
if (frame.m_fileSubId != fileSubId) continue;
if (frame.m_type==11) {
auto &group=static_cast<HanMacWrdKGraphInternal::Group &>(frame);
for (size_t c=0; c < group.m_childsList.size(); ++c) {
if (checkGroupStructures(group.m_childsList[c].m_fileId, 0, seens, true))
continue;
group.m_childsList.resize(c);
break;
}
}
return true;
}
MWAW_DEBUG_MSG(("HanMacWrdKGraph::checkGroupStructures: can not find zone %ld[%ld]\n", fileId, fileSubId));
return true;
}
////////////////////////////////////////////////////////////
// send data
////////////////////////////////////////////////////////////
bool HanMacWrdKGraph::sendPageGraphics(std::vector<long> const &doNotSendIds)
{
std::set<long> notSend;
for (auto &id : doNotSendIds)
notSend.insert(id);
for (auto fIt : m_state->m_framesMap) {
if (notSend.find(fIt.first) != notSend.end() || !fIt.second) continue;
auto const &frame = *fIt.second;
if (frame.m_parsed || frame.m_type==3 || frame.m_inGroup)
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 HanMacWrdKGraph::flushExtra()
{
for (auto fIt : m_state->m_framesMap) {
if (!fIt.second) continue;
auto const &frame = *fIt.second;
if (frame.m_parsed || frame.m_type==3)
continue;
MWAWPosition pos(MWAWVec2f(0,0),MWAWVec2f(0,0),librevenge::RVNG_POINT);
pos.setRelativePosition(MWAWPosition::Char);
sendFrame(frame, pos);
}
for (auto pIt : m_state->m_picturesMap) {
if (!pIt.second) continue;
auto const &picture = *pIt.second;
if (picture.m_parsed)
continue;
MWAWPosition pos(MWAWVec2f(0,0),MWAWVec2f(100,100),librevenge::RVNG_POINT);
pos.setRelativePosition(MWAWPosition::Char);
sendPicture(picture, pos);
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: