/* -*- 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 "MWAWListener.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWGraphicEncoder.hxx"
#include "MWAWGraphicListener.hxx"
#include "MWAWGraphicShape.hxx"
#include "MWAWGraphicStyle.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPictBitmap.hxx"
#include "MWAWPictMac.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSubDocument.hxx"
#include "MsWksTable.hxx"
#include "MsWksDocument.hxx"
#include "MsWksGraph.hxx"
/** Internal: the structures of a MsWksGraph */
namespace MsWksGraphInternal
{
////////////////////////////////////////
//! Internal: a list of zones ( for v4)
struct RBZone {
RBZone()
: m_isMain(true)
, m_id(-2)
, m_idList()
, m_frame("")
{
}
//! returns a unique id
int getId() const
{
return m_isMain ? -1 : m_id;
}
//! the zone type: rbdr(true) or rbil
bool m_isMain;
//! the zone id
int m_id;
//! the list of rb
std::vector<int> m_idList;
//! the frame name ( if it exist )
std::string m_frame;
};
////////////////////////////////////////
//! Internal: the generic pict
struct Zone {
enum Type { Unknown, Shape, ChartZone, Group, Pict, Text, Textv4, Bitmap, TableZone, OLE};
//! constructor
Zone()
: m_subType(-1)
, m_zoneId(-1)
, m_pos()
, m_dataPos(-1)
, m_fileId(-1)
, m_page(-1)
, m_decal()
, m_finalDecal()
, m_box()
, m_line(-1)
, m_style()
, m_order(0)
, m_extra("")
, m_doNotSend(false)
, m_isSent(false)
{
for (auto &id : m_ids) id = 0;
}
//! destructor
virtual ~Zone() {}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Zone const &pict)
{
pict.print(o);
return o;
}
//! return the type
virtual Type type() const
{
return Unknown;
}
//! return a binary data (if known)
virtual bool getBinaryData(MWAWInputStreamPtr, MWAWEmbeddedObject &picture) const
{
picture=MWAWEmbeddedObject();
return false;
}
//! return the extra border space ( if needed)
virtual float needExtraBorderWidth() const
{
return 0.0;
}
//! add frame parameters to propList (if needed )
virtual void fillFrame(MWAWGraphicStyle &) const { }
//! return the box
MWAWBox2f getLocalBox(bool extendWithBord=true) const
{
float x = m_box.size().x(), y=m_box.size().y();
MWAWVec2f min = m_box.min();
if (x < 0) {
min+=MWAWVec2f(x,0);
x *= -1.0f;
}
if (y < 0) {
min+=MWAWVec2f(0,y);
y *= -1.0f;
}
MWAWBox2f res(min, min+MWAWVec2f(x,y));
if (!extendWithBord) return res;
float bExtra = needExtraBorderWidth();
if (bExtra > 0) res.extend(2.0f*bExtra);
return res;
}
MWAWPosition getPosition(MWAWPosition::AnchorTo rel) const
{
MWAWPosition res;
MWAWBox2f box = getLocalBox();
if (rel==MWAWPosition::Paragraph || rel==MWAWPosition::Frame) {
res = MWAWPosition(box.min()+m_finalDecal, box.size(), librevenge::RVNG_POINT);
res.setRelativePosition(rel);
if (rel==MWAWPosition::Paragraph)
res.m_wrapping = MWAWPosition::WBackground;
}
else if (rel!=MWAWPosition::Page) {
res = MWAWPosition(MWAWVec2f(0,0), box.size(), librevenge::RVNG_POINT);
res.setRelativePosition(MWAWPosition::Char,
MWAWPosition::XLeft, MWAWPosition::YTop);
}
else {
res = MWAWPosition(box.min()+m_finalDecal, box.size(), librevenge::RVNG_POINT);
res.setRelativePosition(MWAWPosition::Page);
if (m_page >=0)
res.setPage(m_page+1);
res.m_wrapping = MWAWPosition::WBackground;
}
if (m_order > 0) res.setOrder(m_order);
return res;
}
//! the virtual print function
virtual void print(std::ostream &o) const;
//! the type
int m_subType;
//! the zone id
int m_zoneId;
//! the file position
MWAWEntry m_pos;
//! the data begin position
long m_dataPos;
//! the file id
int m_fileId;
//! the zones id (main, previous, next)
long m_ids[3];
//! the page
int m_page;
//! the local position
MWAWBox2f m_decal;
//! the final local position
MWAWVec2f m_finalDecal;
//! local bdbox
MWAWBox2f m_box;
//! the line position(v1)
int m_line;
//! the style
MsWksGraph::Style m_style;
//! the picture order
int m_order;
//! extra data
std::string m_extra;
//! a flag used to know if we need to send the data ( or if this is the part of a sub group)
bool m_doNotSend;
//! true if the zone is send
bool m_isSent;
};
void Zone::print(std::ostream &o) const
{
if (m_fileId >= 0) {
o << "P" << m_fileId;
if (m_zoneId >= 0) o << "[" << m_zoneId << "],";
else o << ",";
}
for (int i = 0; i < 3; i++) {
if (m_ids[i] <= 0) continue;
switch (i) {
case 0:
o << "id=";
break;
case 1:
o << "pId=";
break;
default:
o << "nId=";
break;
}
o << std::hex << m_ids[i] << std::dec << ",";
}
switch (m_subType) {
case 0:
o << "line,";
break;
case 1:
o << "rect,";
break;
case 2:
o << "rectOval,";
break;
case 3:
o << "circle,";
break;
case 4:
o << "arc,";
break;
case 5:
o << "poly,";
break;
case 7:
o << "pict,";
break;
case 8:
o << "group,";
break;
case 9:
o << "textbox,";
break;
case 0xa:
o << "chart,";
break;
case 0xc:
o << "equation/graph,";
break;
case 0xd:
o << "bitmap,";
break;
case 0xe:
o << "ssheet,";
break;
case 0xf:
o << "textbox2,";
break;
case 0x10:
o << "table,";
break;
case 0x100:
o << "pict,";
break; // V1 pict
default:
o << "#type=" << m_subType << ",";
}
if (m_page>=0) o << "page=" << m_page << ",";
if (m_decal!=MWAWBox2f())
o << "pos=" << m_decal << ",";
o << "bdbox=" << m_box << ",";
o << "style=[" << m_style << "],";
if (m_line >= 0) o << "line=" << m_line << ",";
if (m_extra.length()) o << m_extra;
}
////////////////////////////////////////
//! Internal: the group of a MsWksGraph
struct GroupZone final : public Zone {
//! constructor
explicit GroupZone(Zone const &z)
: Zone(z)
, m_childs()
{
}
//! destructor
~GroupZone() final;
//! return the type
Type type() const final
{
return Group;
}
//! operator<<
void print(std::ostream &o) const final
{
Zone::print(o);
o << "childs=[";
for (auto id : m_childs)
o << "P" << id << ",";
o << "],";
}
//! list of child id
std::vector<int> m_childs;
};
GroupZone::~GroupZone()
{
}
////////////////////////////////////////
//! Internal: the simple form of a MsWksGraph ( line, rect, ...)
struct BasicShape final : public Zone {
//! constructor
explicit BasicShape(Zone const &z)
: Zone(z)
, m_shape()
{
}
//! destructor
~BasicShape() final;
//! return the type
Type type() const final
{
return Shape;
}
//! operator<<
void print(std::ostream &o) const final
{
Zone::print(o);
o << m_shape << ",";
}
//! return the extra border size
float needExtraBorderWidth() const final
{
float res=m_style.m_lineWidth;
if (m_shape.m_type==MWAWGraphicShape::Line) {
for (const auto &arrow : m_style.m_arrows) {
if (!arrow.isEmpty()) res+=4;
}
}
return 0.5f*res;
}
//! return the shape type
MWAWGraphicStyle getStyle() const
{
MWAWGraphicStyle style(m_style);
if (m_subType!=0)
style.m_arrows[0] = style.m_arrows[1]=MWAWGraphicStyle::Arrow();
return style;
}
//! the basic shape
MWAWGraphicShape m_shape;
private:
BasicShape(BasicShape const &) = delete;
BasicShape &operator=(BasicShape const &) = delete;
};
BasicShape::~BasicShape()
{
}
////////////////////////////////////////
//! Internal: the table of a MsWksGraph
struct Chart final : public Zone {
//! constructor
explicit Chart(Zone const &z)
: Zone(z)
, m_chartId(0)
{
}
//! empty constructor
Chart()
: Zone()
, m_chartId(0)
{
}
//! destructor
~Chart() final;
//! return the type
Type type() const final
{
return ChartZone;
}
//! the chart id
int m_chartId;
};
Chart::~Chart()
{
}
////////////////////////////////////////
//! Internal: the picture of a MsWksGraph
struct DataPict final : public Zone {
//! constructor
explicit DataPict(Zone const &z)
: Zone(z)
, m_dataEndPos(-1)
, m_naturalBox()
{
}
//! empty constructor
DataPict()
: Zone(),
m_dataEndPos(-1)
, m_naturalBox()
{
}
//! return the type
Type type() const final
{
return Pict;
}
//! return a binary data (if known)
bool getBinaryData(MWAWInputStreamPtr ip, MWAWEmbeddedObject &picture) const final;
//! operator<<
void print(std::ostream &o) const final
{
Zone::print(o);
}
//! the end of data (only defined when different to m_pos.end())
long m_dataEndPos;
//! the pict box (if known )
mutable MWAWBox2f m_naturalBox;
};
bool DataPict::getBinaryData(MWAWInputStreamPtr ip, MWAWEmbeddedObject &picture) const
{
picture=MWAWEmbeddedObject();
long endPos = m_dataEndPos<=0 ? m_pos.end() : m_dataEndPos;
long pictSize = endPos-m_dataPos;
if (pictSize < 0) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::DataPict::getBinaryData: picture size is bad\n"));
return false;
}
#ifdef DEBUG_WITH_FILES
if (1) {
librevenge::RVNGBinaryData file;
ip->seek(m_dataPos, librevenge::RVNG_SEEK_SET);
ip->readDataBlock(pictSize, file);
static int volatile pictName = 0;
libmwaw::DebugStream f;
f << "Pict-" << ++pictName << ".pct";
libmwaw::Debug::dumpFile(file, f.str().c_str());
}
#endif
ip->seek(m_dataPos, librevenge::RVNG_SEEK_SET);
auto res = MWAWPictData::check(ip, static_cast<int>(pictSize), m_naturalBox);
if (res == MWAWPict::MWAW_R_BAD) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::DataPict::getBinaryData: can not find the picture\n"));
return false;
}
ip->seek(m_dataPos, librevenge::RVNG_SEEK_SET);
std::shared_ptr<MWAWPict> pict(MWAWPictData::get(ip, static_cast<int>(pictSize)));
return pict && pict->getBinary(picture);
}
////////////////////////////////////////
//! Internal: the bitmap of a MsWksGraph
struct DataBitmap final : public Zone {
//! constructor
explicit DataBitmap(Zone const &z)
: Zone(z)
, m_numRows(0)
, m_numCols(0)
, m_dataSize(0)
, m_naturalBox()
{
}
//! empty constructor
DataBitmap()
: Zone()
, m_numRows(0)
, m_numCols(0)
, m_dataSize(0)
, m_naturalBox()
{
}
//! destructor
~DataBitmap() final;
//! return the type
Type type() const final
{
return Bitmap;
}
//! return a binary data (if known)
bool getPictureData(MWAWInputStreamPtr ip, MWAWEmbeddedObject &picture, std::vector<MWAWColor> const &palette) const;
//! operator<<
void print(std::ostream &o) const final
{
o << "nRows=" << m_numRows << ",";
o << "nCols=" << m_numCols << ",";
if (m_dataSize > 0)
o << "dSize=" << std::hex << m_dataSize << std::dec << ",";
Zone::print(o);
}
int m_numRows /** the number of rows*/, m_numCols/** the number of columns*/;
long m_dataSize /** the bitmap data size */;
//! the pict box (if known )
mutable MWAWBox2f m_naturalBox;
};
bool DataBitmap::getPictureData(MWAWInputStreamPtr ip, MWAWEmbeddedObject &picture, std::vector<MWAWColor> const &palette) const
{
picture=MWAWEmbeddedObject();
if (m_dataSize <= 0 || m_dataSize < m_numRows*m_numCols) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::DataBitmap::getPictureData: dataSize size is bad\n"));
return false;
}
auto szCol = int(m_dataSize/m_numRows);
long pos = m_dataPos;
MWAWPictBitmapIndexed *btmap = new MWAWPictBitmapIndexed(MWAWVec2i(m_numCols, m_numRows));
if (!btmap) return false;
btmap->setColors(palette);
std::shared_ptr<MWAWPict> pict(btmap);
for (int i = 0; i < m_numRows; i++) {
ip->seek(pos, librevenge::RVNG_SEEK_SET);
unsigned long numRead;
uint8_t const *value = ip->read(size_t(m_numCols), numRead);
if (!value || int(numRead) != m_numCols) return false;
btmap->setRow(i, value);
pos += szCol;
}
return pict->getBinary(picture);
}
DataBitmap::~DataBitmap()
{
}
////////////////////////////////////////
//! Internal: the table of a MsWksGraph
struct Table final : public Zone {
//! constructor
explicit Table(Zone const &z)
: Zone(z)
, m_tableId(0)
{
}
//! empty constructor
Table()
: Zone()
, m_tableId(0)
{
}
//! destructor
~Table() final;
//! return the type
Type type() const final
{
return TableZone;
}
//! the table id
int m_tableId;
};
Table::~Table()
{
}
////////////////////////////////////////
//! Internal: the textbox of a MsWksGraph ( v2-v3)
struct TextBox final : public Zone {
//! constructor
explicit TextBox(Zone const &z)
: Zone(z)
, m_numPositions(-1)
, m_fontsList()
, m_positions()
, m_formats()
, m_text("")
, m_justify(MWAWParagraph::JustificationLeft)
{ }
//! destructor
~TextBox() final;
//! return the type
Type type() const final
{
return Text;
}
//! operator<<
void print(std::ostream &o) const final
{
Zone::print(o);
switch (m_justify) {
case MWAWParagraph::JustificationLeft:
break;
case MWAWParagraph::JustificationCenter:
o << ",centered";
break;
case MWAWParagraph::JustificationRight:
o << ",right";
break;
case MWAWParagraph::JustificationFull:
o << ",full";
break;
case MWAWParagraph::JustificationFullAllLines:
o << ",fullAllLines";
break;
#if !defined(__clang__)
default:
o << ",#just=" << m_justify;
break;
#endif
}
}
//! add frame parameters to propList (if needed )
void fillFrame(MWAWGraphicStyle &style) const final
{
if (!m_style.m_baseSurfaceColor.isWhite())
style.setBackgroundColor(m_style.m_baseSurfaceColor);
}
//! the number of positions
int m_numPositions;
//! the list of fonts
std::vector<MWAWFont> m_fontsList;
//! the list of positions
std::vector<int> m_positions;
//! the list of format
std::vector<int> m_formats;
//! the text
std::string m_text;
//! the paragraph alignment
MWAWParagraph::Justification m_justify;
private:
TextBox(TextBox const &) = delete;
TextBox &operator=(TextBox const &) = delete;
};
TextBox::~TextBox()
{
}
////////////////////////////////////////
//! Internal: the ole zone of a MsWksGraph ( v4)
struct OLEZone final : public Zone {
//! constructor
explicit OLEZone(Zone const &z)
: Zone(z)
, m_oleId(-1)
, m_dim()
{ }
//! destructor
~OLEZone() final;
//! return the type
Type type() const final
{
return OLE;
}
//! operator<<
void print(std::ostream &o) const final
{
if (m_oleId >= 0) o << "ole" << m_oleId << ",";
if (m_dim[0] > 0 && m_dim[1] > 0) o << "dim=" << m_dim << ",";
Zone::print(o);
}
//! the ole id
int m_oleId;
//! the dimension
MWAWVec2i m_dim;
};
OLEZone::~OLEZone()
{
}
////////////////////////////////////////
//! Internal: the textbox of a MsWksGraph ( v4)
struct TextBoxv4 final : public Zone {
//! constructor
explicit TextBoxv4(Zone const &z)
: Zone(z)
, m_text()
, m_frame("")
{ }
//! destructor
~TextBoxv4() final;
//! return the type
Type type() const final
{
return Textv4;
}
//! operator<<
void print(std::ostream &o) const final
{
Zone::print(o);
if (m_text.valid()) o << ", textPos=[" << m_text.begin() << "-" << m_text.end() << "]";
}
//! add frame parameters to propList (if needed )
void fillFrame(MWAWGraphicStyle &style) const final
{
if (!m_style.m_baseSurfaceColor.isWhite())
style.setBackgroundColor(m_style.m_baseSurfaceColor);
}
//! the text of positions (0-0: means no text)
MWAWEntry m_text;
//! the frame name
std::string m_frame;
};
TextBoxv4::~TextBoxv4()
{
}
//! Internal the pattern ressource of a MsWksGraph
struct Pattern {
//! constructor ( 4 int by patterns )
Pattern(int num, uint16_t const *data)
: m_num(num)
, m_valuesList()
, m_percentList()
{
if (m_num<=0) return;
m_valuesList.resize(size_t(m_num)*8);
for (size_t i=0; i < size_t(m_num)*4; ++i) {
uint16_t val=data[i];
m_valuesList[2*i]=static_cast<unsigned char>(val>>8);
m_valuesList[2*i+1]=static_cast<unsigned char>(val&0xFF);
}
size_t pat=0;
for (size_t i=0; i < size_t(num); ++i) {
int numOnes=0;
for (int j=0; j < 8; ++j) {
uint8_t val=m_valuesList[pat++];
for (int b=0; b < 8; b++) {
if (val&1) ++numOnes;
val = uint8_t(val>>1);
}
}
m_percentList.push_back(float(numOnes)/64.f);
}
}
//! return the pattern corresponding to an id
bool get(int id, MWAWGraphicStyle::Pattern &pat) const
{
if (id < 0 || id >= m_num) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::Pattern::get: can not find pattern %d\n", id));
return false;
}
pat.m_dim=MWAWVec2i(8,8);
unsigned char const *ptr=&m_valuesList[8*size_t(id)];
pat.m_data.resize(8);
for (auto &data : pat.m_data)
data=*(ptr++);
return true;
}
//! return the percentage corresponding to a pattern
float getPercent(int id) const
{
if (id < 0 || id >= m_num) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::Pattern::getPatternPercent: can not find pattern %d\n", id));
return 1.0;
}
return m_percentList[size_t(id)];
}
//! the number of patterns
int m_num;
//! the pattern values (8 data by pattern)
std::vector<unsigned char> m_valuesList;
//! the pattern percent values
std::vector<float> m_percentList;
};
////////////////////////////////////////
//! Internal: the state of a MsWksGraph
struct State {
//! constructor
State()
: m_version(-1)
, m_leftTopPos(0,0)
, m_zonesList()
, m_RBsMap()
, m_font(20,12)
, m_chartId(0)
, m_tableId(0)
, m_numPages(0)
, m_rsrcPatternMap()
{
}
//! return the pattern corresponding to an id
bool getPattern(MWAWGraphicStyle::Pattern &pat, int id, long rsid=-1);
//! return the percentage corresponding to a pattern
float getPatternPercent(int id, long rsid=-1);
//! init the pattern value
void initPattern(int vers);
//! the version
int m_version;
//! the page left top position in points
MWAWVec2f m_leftTopPos;
//! the list of zone
std::vector<std::shared_ptr<Zone> > m_zonesList;
//! the RBIL zone id->list id
std::map<int, RBZone> m_RBsMap;
//! the actual font
MWAWFont m_font;
//! an index used to store chart
int m_chartId;
//! an index used to store table
int m_tableId;
//! the number of pages
int m_numPages;
//! a map ressource id -> patterns
std::map<long, Pattern> m_rsrcPatternMap;
};
void State::initPattern(int vers)
{
if (!m_rsrcPatternMap.empty()) return;
if (vers <= 2) {
static uint16_t const valuesV2[] = {
0xffff, 0xffff, 0xffff, 0xffff, 0xddff, 0x77ff, 0xddff, 0x77ff, 0xdd77, 0xdd77, 0xdd77, 0xdd77, 0xaa55, 0xaa55, 0xaa55, 0xaa55,
0x55ff, 0x55ff, 0x55ff, 0x55ff, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa, 0xeedd, 0xbb77, 0xeedd, 0xbb77, 0x8888, 0x8888, 0x8888, 0x8888,
0xb130, 0x031b, 0xd8c0, 0x0c8d, 0x8010, 0x0220, 0x0108, 0x4004, 0xff88, 0x8888, 0xff88, 0x8888, 0xff80, 0x8080, 0xff08, 0x0808,
0x0000, 0x0002, 0x0000, 0x0002, 0x8040, 0x2000, 0x0204, 0x0800, 0x8244, 0x3944, 0x8201, 0x0101, 0xf874, 0x2247, 0x8f17, 0x2271,
0x55a0, 0x4040, 0x550a, 0x0404, 0x2050, 0x8888, 0x8888, 0x0502, 0xbf00, 0xbfbf, 0xb0b0, 0xb0b0, 0x0000, 0x0000, 0x0000, 0x0000,
0x8000, 0x0800, 0x8000, 0x0800, 0x8800, 0x2200, 0x8800, 0x2200, 0x8822, 0x8822, 0x8822, 0x8822, 0xaa00, 0xaa00, 0xaa00, 0xaa00,
0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x1122, 0x4488, 0x1122, 0x4488, 0x8040, 0x2000, 0x0204, 0x0800, 0x0102, 0x0408, 0x1020, 0x4080,
0xaa00, 0x8000, 0x8800, 0x8000, 0xff80, 0x8080, 0x8080, 0x8080, 0x0814, 0x2241, 0x8001, 0x0204, 0x8814, 0x2241, 0x8800, 0xaa00,
0x40a0, 0x0000, 0x040a, 0x0000, 0x0384, 0x4830, 0x0c02, 0x0101, 0x8080, 0x413e, 0x0808, 0x14e3, 0x1020, 0x54aa, 0xff02, 0x0408,
0x7789, 0x8f8f, 0x7798, 0xf8f8, 0x0008, 0x142a, 0x552a, 0x1408, 0x0000, 0x0000, 0x0000, 0x0000,
};
m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(-1,Pattern(39, valuesV2)));
}
static uint16_t const values4002[] = {
0xffff, 0xffff, 0xffff, 0xffff, 0x7fff, 0xffff, 0xf7ff, 0xffff, 0x7fff, 0xf7ff, 0x7fff, 0xf7ff, 0x77ff, 0xddff, 0x77ff, 0xddff,
0x55ff, 0xddff, 0x55ff, 0xddff, 0x55ff, 0xeeff, 0x55ff, 0xbbff, 0x55ff, 0x55ff, 0x55ff, 0x55ff, 0x77dd, 0x77dd, 0x77dd, 0x77dd,
0x55bf, 0x55ff, 0x55fb, 0x55ff, 0x55bb, 0x55ff, 0x55bb, 0x55ff, 0x55bf, 0x55ee, 0x55fb, 0x55ee, 0x55bb, 0x55ee, 0x55bb, 0x55ee,
0x55bb, 0x55ea, 0x55bb, 0x55ae, 0x55ba, 0x55ab, 0x55ba, 0x55ab, 0x55ea, 0x55aa, 0x55ae, 0x55aa, 0xaa55, 0xaa55, 0xaa55, 0xaa55,
0xaa15, 0xaa55, 0xaa51, 0xaa55, 0xaa45, 0xaa54, 0xaa45, 0xaa54, 0xaa44, 0xaa15, 0xaa44, 0xaa51, 0xaa44, 0xaa11, 0xaa44, 0xaa11,
0xaa40, 0xaa11, 0xaa04, 0xaa11, 0xaa44, 0xaa00, 0xaa44, 0xaa00, 0xaa40, 0xaa00, 0xaa04, 0xaa00, 0x8822, 0x8822, 0x8822, 0x8822,
0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0x1100, 0xaa00, 0x4400, 0xaa00, 0x2200, 0xaa00, 0x2200, 0x8800, 0x2200, 0x8800, 0x2200,
0x8800, 0x2000, 0x8800, 0x0200, 0x8000, 0x0800, 0x8000, 0x0800, 0x8000, 0x0000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};
m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(4002,Pattern(32, values4002)));
static uint16_t const values4003[] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x8000, 0x0000, 0x0800, 0x0000, 0x8000, 0x0800, 0x8000, 0x0800, 0x8800, 0x2000, 0x8800, 0x0200,
0x8800, 0x2200, 0x8800, 0x2200, 0xaa00, 0x2200, 0xaa00, 0x2200, 0xaa00, 0x1100, 0xaa00, 0x4400, 0xaa00, 0xaa00, 0xaa00, 0xaa00,
0x8822, 0x8822, 0x8822, 0x8822, 0xaa44, 0xaa11, 0xaa44, 0xaa11, 0xaa45, 0xaa54, 0xaa45, 0xaa54, 0xaa55, 0xaa55, 0xaa55, 0xaa55,
0x55ea, 0x55aa, 0x55ae, 0x55aa, 0x55ba, 0x55ab, 0x55ba, 0x55ab, 0x55bb, 0x55ee, 0x55bb, 0x55ee, 0x77dd, 0x77dd, 0x77dd, 0x77dd,
0x55ff, 0x55ff, 0x55ff, 0x55ff, 0x55ff, 0xeeff, 0x55ff, 0xbbff, 0x77ff, 0xddff, 0x77ff, 0xddff, 0x7fff, 0xf7ff, 0x7fff, 0xf7ff,
0x7fff, 0xffff, 0xf7ff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
};
m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(4003,Pattern(22, values4003)));
static uint16_t const values4004[] = {
0xf0f0, 0xf0f0, 0x0f0f, 0x0f0f, 0xcccc, 0x3333, 0xcccc, 0x3333, 0x3333, 0xcccc, 0x3333, 0xcccc
};
m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(4004,Pattern(3, values4004)));
static uint16_t const values7000[] = {
0x0101, 0x1010, 0x0101, 0x1010, 0xcc00, 0x0000, 0x3300, 0x0000, 0x1122, 0x4400, 0x1122, 0x4400, 0x4422, 0x0088, 0x4422, 0x0088,
0xf0f0, 0xf0f0, 0x0f0f, 0x0f0f, 0x9966, 0x6699, 0x9966, 0x6699, 0x0008, 0x1c3e, 0x7f3e, 0x1c08, 0x0008, 0x142a, 0x552a, 0x1408,
0xb130, 0x031b, 0xd8c0, 0x0c8d, 0x8010, 0x0220, 0x0108, 0x4004, 0x0814, 0x2241, 0x8001, 0x0204, 0x80c0, 0x2112, 0x0c04, 0x0201,
0xff80, 0x8080, 0xff08, 0x0808, 0x007f, 0x7f7f, 0x00f7, 0xf7f7, 0x8040, 0x2000, 0x0204, 0x0800, 0x8244, 0x3944, 0x8201, 0x0101,
0xf078, 0x2442, 0x870f, 0x1221, 0x1020, 0x54aa, 0xff02, 0x0408, 0xf874, 0x2247, 0x8f17, 0x2271, 0xbfa0, 0xbfbd, 0xbdfd, 0x05fd,
0x2050, 0x8888, 0x8888, 0x0502, 0x55a0, 0x4040, 0x550a, 0x0404, 0x8844, 0x2211, 0x1122, 0x4488, 0x8142, 0x2418, 0x8142, 0x2418,
0xaa00, 0x8000, 0x8800, 0x8000, 0x0384, 0x4830, 0x0c02, 0x0101, 0x8080, 0x413e, 0x0808, 0x14e3, 0xaf5f, 0xaf5f, 0x0d0b, 0x0d0b,
0x7789, 0x8f8f, 0x7798, 0xf8f8, 0x8814, 0x2241, 0x8800, 0xaa00, 0x40a0, 0x0000, 0x040a, 0x0000, 0xbf00, 0xbfbf, 0xb0b0, 0xb0b0
};
m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(7000,Pattern(32, values7000)));
static uint16_t const values14001[] = {
0x8844, 0x2211, 0x8844, 0x2211, 0x77bb, 0xddee, 0x77bb, 0xddee, 0x1122, 0x4488, 0x1122, 0x4488, 0xeedd, 0xbb77, 0xeedd, 0xbb77,
0x8040, 0x2010, 0x0804, 0x0201, 0x7fbf, 0xdfef, 0xf7fb, 0xfdfe, 0x0102, 0x0408, 0x1020, 0x4080, 0xfefd, 0xfbf7, 0xefdf, 0xbf7f,
0xe070, 0x381c, 0x0e07, 0x83c1, 0x99cc, 0x6633, 0x99cc, 0x6633, 0x8307, 0x0e1c, 0x3870, 0xe0c1, 0x3366, 0xcc99, 0x3366, 0xcc99,
0x8142, 0x2418, 0x1824, 0x4281, 0x7ebd, 0xdbe7, 0xe7db, 0xbd7e, 0x8244, 0x2810, 0x2844, 0x8201, 0x7dbb, 0xd7ef, 0xd7bb, 0x7dfe,
0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa, 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x8888, 0x8888, 0x8888, 0x8888, 0x7777, 0x7777, 0x7777, 0x7777,
0xff00, 0x0000, 0xff00, 0x0000, 0x00ff, 0xffff, 0x00ff, 0xffff, 0x8080, 0x8080, 0x8080, 0x8080, 0x7f7f, 0x7f7f, 0x7f7f, 0x7f7f,
0xff00, 0x0000, 0x0000, 0x0000, 0x00ff, 0xffff, 0xffff, 0xffff, 0xcccc, 0xcccc, 0xcccc, 0xcccc, 0xffff, 0x0000, 0xffff, 0x0000,
0xff88, 0x8888, 0xff88, 0x8888, 0x0077, 0x7777, 0x0077, 0x7777, 0xff80, 0x8080, 0x8080, 0x8080, 0x007f, 0x7f7f, 0x7f7f, 0x7f7f
};
m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(14001,Pattern(32, values14001)));
}
float State::getPatternPercent(int id, long rsid)
{
if (m_rsrcPatternMap.empty())
initPattern(m_version);
if (m_rsrcPatternMap.find(rsid)==m_rsrcPatternMap.end()) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::State::getPatternPercent unknown map for rsdid=%ld\n",rsid));
return 1.0;
}
return m_rsrcPatternMap.find(rsid)->second.getPercent(id);
}
bool State::getPattern(MWAWGraphicStyle::Pattern &pat, int id, long rsid)
{
if (m_rsrcPatternMap.empty())
initPattern(m_version);
if (m_rsrcPatternMap.find(rsid)==m_rsrcPatternMap.end()) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::State::getPattern unknown map for rsdid=%ld\n",rsid));
return false;
}
return m_rsrcPatternMap.find(rsid)->second.get(id, pat);
}
////////////////////////////////////////
//! Internal: the subdocument of a MsWksGraph
class SubDocument final : public MWAWSubDocument
{
public:
enum Type { RBILZone, Chart, Empty, Group, Table, TextBox, TextBoxv4 };
SubDocument(MsWksGraph &pars, MWAWInputStreamPtr const &input, Type type, int zoneId)
: MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
, m_graphParser(&pars)
, m_type(type)
, m_id(zoneId)
, m_frame("")
{
}
SubDocument(MsWksGraph &pars, MWAWInputStreamPtr const &input, Type type,
MWAWEntry const &entry, std::string const &frame=std::string(""))
: MWAWSubDocument(pars.m_mainParser, input, entry)
, m_graphParser(&pars)
, m_type(type)
, m_id(-1)
, m_frame(frame)
{
}
//! destructor
~SubDocument() final {}
//! operator!=
bool operator!=(MWAWSubDocument const &doc) const final;
//! the parser function
void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
private:
SubDocument(SubDocument const &orig) = delete;
SubDocument &operator=(SubDocument const &orig) = delete;
protected:
/** the graph parser */
MsWksGraph *m_graphParser;
/** the type */
Type m_type;
/** the subdocument id*/
int m_id;
/** the frame name: for textv4 */
std::string m_frame;
};
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
{
if (!listener.get()) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::SubDocument::parse: no listener\n"));
return;
}
if (!m_graphParser) {
MWAW_DEBUG_MSG(("MsWksGraphInternal::SubDocument::parse: no parser\n"));
return;
}
long pos = m_input->tell();
switch (m_type) {
case Empty:
break;
case Chart:
m_graphParser->sendChart(m_id);
break;
case Group: {
MWAWPosition gPos;
gPos.setRelativePosition(MWAWPosition::Frame,
MWAWPosition::XLeft, MWAWPosition::YTop);
m_graphParser->sendGroupChild(m_id, gPos);
break;
}
case Table:
m_graphParser->sendTable(m_id);
break;
case TextBox:
m_graphParser->sendTextBox(m_id, listener);
break;
case TextBoxv4:
m_graphParser->sendFrameText(m_zone, m_frame);
break;
case RBILZone: {
MsWksGraph::SendData sendData;
sendData.m_type = MsWksGraph::SendData::RBIL;
sendData.m_id = m_id;
sendData.m_anchor = MWAWPosition::Frame;
m_graphParser->sendObjects(sendData);
break;
}
#if !defined(__clang__)
default:
MWAW_DEBUG_MSG(("MsWksGraph::SubDocument::parse: unexpected zone type\n"));
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_id != sDoc->m_id) return true;
if (m_type != sDoc->m_type) return true;
if (m_frame != sDoc->m_frame) return true;
return false;
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
MsWksGraph::MsWksGraph(MsWksDocument &document)
: m_parserState()
, m_state(new MsWksGraphInternal::State)
, m_mainParser(&document.getMainParser())
, m_document(document)
, m_tableParser()
{
m_parserState=m_mainParser->getParserState();
m_tableParser.reset(new MsWksTable(*m_mainParser, m_document, *this));
}
MsWksGraph::~MsWksGraph()
{
}
void MsWksGraph::setPageLeftTop(MWAWVec2f const &leftTop)
{
m_state->m_leftTopPos=leftTop;
}
int MsWksGraph::version() const
{
if (m_state->m_version < 0)
m_state->m_version = m_parserState->m_version;
return m_state->m_version;
}
int MsWksGraph::numPages(int zoneId) const
{
if (m_state->m_numPages > 0)
return m_state->m_numPages;
int maxPage = 0;
for (auto zone : m_state->m_zonesList) {
if (zoneId >= 0 && zone->m_zoneId!=zoneId) continue;
if (zone->m_page > maxPage)
maxPage = zone->m_page;
}
m_state->m_numPages = maxPage+1;
return m_state->m_numPages;
}
void MsWksGraph::sendFrameText(MWAWEntry const &entry, std::string const &frame)
{
m_document.sendTextbox(entry, frame);
}
void MsWksGraph::sendChart(int zoneId)
{
m_tableParser->sendChart(zoneId);
}
void MsWksGraph::sendTable(int zoneId)
{
m_tableParser->sendTable(zoneId);
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool MsWksGraph::getZoneGraphicStyle(int id, MWAWGraphicStyle &style) const
{
if (id<0 || id>=int(m_state->m_zonesList.size()) || !m_state->m_zonesList[size_t(id)]) {
MWAW_DEBUG_MSG(("MsWksGraph::getZoneGraphicStyle: unknown zone %d\n", id));
return false;
}
style = m_state->m_zonesList[size_t(id)]->m_style;
return true;
}
bool MsWksGraph::getZonePosition(int id, MWAWPosition::AnchorTo anchor, MWAWPosition &pos) const
{
if (id<0 || id>=int(m_state->m_zonesList.size()) || !m_state->m_zonesList[size_t(id)]) {
MWAW_DEBUG_MSG(("MsWksGraph::getZoneGraphicStyle: unknown zone %d\n", id));
return false;
}
pos = m_state->m_zonesList[size_t(id)]->getPosition(anchor);
return true;
}
bool MsWksGraph::readPictHeader(MsWksGraphInternal::Zone &pict)
{
MWAWInputStreamPtr input=m_document.getInput();
long pos=input->tell();
int vers = version();
if (!input->checkPosition(pos+(vers>=3 ? 54 : 38)) || input->readULong(1) != 0) return false;
pict = MsWksGraphInternal::Zone();
pict.m_subType = static_cast<int>(input->readULong(1));
if (pict.m_subType > 0x10 || pict.m_subType == 6 || pict.m_subType == 0xb)
return false;
if (pict.m_subType>9) {
if (vers<=2)
return false;
// we can find chart in v3 spreadsheet file
else if (vers==3 && pict.m_subType != 0xa && m_parserState->m_type!=MWAWParserState::Spreadsheet)
return false;
}
libmwaw::DebugStream f;
int val;
if (vers >= 3) {
val = static_cast<int>(input->readLong(2));
if (vers == 4 || m_parserState->m_type==MWAWParserState::Graphic)
pict.m_page = val==0 ? -2 : val-1;
else if (val)
f << "f0=" << val << ",";
}
// color
Style &style=pict.m_style;
for (int i = 0; i < 2; i++) {
auto rId = static_cast<int>(input->readLong(2));
int cId = (vers <= 2) ? rId+1 : rId;
MWAWColor col;
if (m_document.getColor(cId,col,vers <= 3 ? vers : 3)) {
if (i) style.m_baseSurfaceColor = col;
else style.m_baseLineColor = col;
}
else
f << "#col" << i << "=" << rId << ",";
}
bool hasSurfPatFunction=false;
if (vers <= 2) {
for (int i = 0; i < 2; i++) {
auto pId = static_cast<int>(input->readLong(2));
if (pId==38) { // empty
if (i==0)
style.m_lineWidth=0;
continue;
}
float percent = m_state->getPatternPercent(pId);
MWAWGraphicStyle::Pattern pattern;
if (i==0)
style.m_lineColor=MWAWColor::barycenter(percent, style.m_baseLineColor, 1.f-percent, style.m_baseSurfaceColor);
else if (m_state->getPattern(pattern, pId)) {
pattern.m_colors[0] = style.m_baseSurfaceColor;
pattern.m_colors[1] = style.m_baseLineColor;
style.setPattern(pattern);
}
else
style.setSurfaceColor(MWAWColor::barycenter(percent, style.m_baseLineColor, 1.f-percent, style.m_baseSurfaceColor));
}
auto lineType=static_cast<int>(input->readLong(2));
if (style.m_lineWidth>0) {
switch (lineType) {
case 0:
style.m_lineWidth=0.;
break;
case 1:
style.m_lineWidth=0.5;
break;
case 2: // lineW=1
break;
case 3:
style.m_lineWidth=2;
break;
case 4:
style.m_lineWidth=4;
break;
default:
f << "#lineType=" << lineType << ",";
break;
}
}
}
else {
style.m_lineColor=style.m_baseLineColor;
style.m_surfaceColor=style.m_baseSurfaceColor;
for (int i = 0; i < 2; i++) {
if (i) f << "surface";
else f << "line";
f << "Pattern=[";
long rsid= input->readLong(2);
if (rsid==0) f << "noColor,";
else if (rsid==-1) f << "grad,";
else f << "rsid=" << rsid << ",";
auto patId = static_cast<int>(input->readULong(2));
if (patId) f << "pat=" << patId << ",";
else f << "_";
if (vers==4 && rsid==-1 && patId==0xFFFF)
hasSurfPatFunction=true;
val = static_cast<int>(input->readLong(1));
if (val) f << "unkn=" << val << ",";
auto per = static_cast<int>(input->readULong(1));
f << per << "%,";
if (rsid<=0) {
if (i==0 && rsid==0)
style.m_lineWidth=0.;
}
else {
float percent=1.0;
bool done=false;
MWAWGraphicStyle::Pattern pattern;
if (per >= 0 && per < 100)
percent = float(per)/100.f;
else if (m_state->getPattern(pattern, patId, rsid)) {
percent = m_state->getPatternPercent(patId, rsid);
if (i) {
pattern.m_colors[0] = style.m_baseSurfaceColor;
pattern.m_colors[1] = style.m_baseLineColor;
style.setPattern(pattern);
done = true;
}
}
else {
MWAW_DEBUG_MSG(("MsWksGraph::readPictHeader:find odd pattern\n"));
f << "##";
}
if (done) {
}
else if (i==0)
style.m_lineColor=MWAWColor::barycenter(percent, style.m_baseLineColor, 1.f-percent, style.m_baseSurfaceColor);
else
style.setSurfaceColor(MWAWColor::barycenter(percent, style.m_baseLineColor, 1.f-percent, style.m_baseSurfaceColor));
}
f << "],";
}
int penSize[2];
for (auto &pSize : penSize) pSize = static_cast<int>(input->readLong(2));
if (style.m_lineWidth<=0)
f << "pen=" << penSize[0] << "x" << penSize[1] << ",";
else if (penSize[0]==penSize[1])
style.m_lineWidth=float(penSize[0]);
else {
f << "pen=" << penSize[0] << "x" << penSize[1] << ",";
style.m_lineWidth=0.5f*float(penSize[0]+penSize[1]);
}
if (style.m_lineWidth < 0 || style.m_lineWidth > 10) {
f << "##penSize=" << style.m_lineWidth << ",";
style.m_lineWidth = 1;
}
val = static_cast<int>(input->readLong(2));
if (val)
f << "f1=" << val << ",";
}
float offset[4];
for (auto &off : offset) off = float(input->readLong(2));
pict.m_decal = MWAWBox2f(MWAWVec2f(offset[0],offset[1]), MWAWVec2f(offset[3],offset[2]));
pict.m_finalDecal = MWAWVec2f(float(offset[0]+offset[3]), float(offset[1]+offset[2]));
// the two point which allows to create the form ( in general the bdbox)
float dim[4];
for (auto &d : dim) d = float(input->readLong(4))/65536.f;
pict.m_box=MWAWBox2f(MWAWVec2f(dim[0],dim[1]),MWAWVec2f(dim[2],dim[3]));
auto flags = static_cast<int>(input->readLong(1));
// 2: rotations, 1:lock ?, 0: nothing, other ?
if (vers >= 4 && (flags&1)) {
f << "locked,";
flags &= 0xFE;
}
if (vers >= 4 && (flags&2)) {
f << "Rot=[";
for (int i = 0; i < 32; i++)
f << input->readLong(2) << ",";
f << "],";
flags &= 0xFC;
}
if (flags) f << "fl0=" << flags << ",";
auto lineFlags = static_cast<int>(input->readULong(1));
switch (lineFlags&3) {
case 2:
style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain();
MWAW_FALLTHROUGH;
case 1:
style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
break;
default:
f << "#arrow=3,";
case 0:
break;
}
if (lineFlags&0xFC) f << "#lineFlags=" << std::hex << (lineFlags&0xFC) << std::dec << ",";
if (vers >= 3) pict.m_ids[0] = long(input->readULong(4));
if (vers >= 4 && hasSurfPatFunction) {
pos = input->tell();
if (!readGradient(style)) {
f << "##gradient,";
input->seek(pos, librevenge::RVNG_SEEK_SET);
}
}
pict.m_extra = f.str();
pict.m_dataPos = input->tell();
return true;
}
bool MsWksGraph::readGradient(MsWksGraph::Style &style)
{
MWAWInputStreamPtr input=m_document.getInput();
long pos = input->tell();
if (!input->checkPosition(pos+22))
return false;
libmwaw::DebugStream f;
f << "gradient[unknown]=[";
auto type=static_cast<int>(input->readLong(2));
auto val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(1)); // always 8?
if (val!=8) f << "f1=" << val << ",";
val=static_cast<int>(input->readLong(2)); // find 1 in square
if (val) f << "f2=" << val << ",";
val=static_cast<int>(input->readULong(2)); // always 0 ?
if (val) f << "f3=" << std::hex << val << std::dec << ",";
auto angle =static_cast<int>(input->readLong(2));
val=static_cast<int>(input->readLong(2)); // 89[square]|156[square:linearbi]|255
if (val!=0xff) f << "f4=" << val << ",";
val=static_cast<int>(input->readLong(2)); // 54[square]|0
if (val) f << "f5=" << val << ",";
val=static_cast<int>(input->readLong(2)); // 18
if (val!=0x18) f << "f6=" << val << ",";
val=static_cast<int>(input->readULong(2));
int subType = (val&0xf);
val = (val>>4);
if (val!=0xFF)
f << "sType[high]=" << std::hex << val << std::dec << ",";
val=static_cast<int>(input->readLong(2)); // 0
if (val) f << "f7=" << val << ",";
val=static_cast<int>(input->readLong(1)); // 0
if (val) f << "f8=" << val << ",";
f << "],";
switch (type) {
case 1:
style.m_gradientStopList.resize(2);
style.m_gradientStopList[0]=MWAWGraphicStyle::GradientStop(0.0, style.m_baseSurfaceColor);
style.m_gradientStopList[1]=MWAWGraphicStyle::GradientStop(1.0, style.m_baseLineColor);
style.m_gradientAngle = float(90+angle);
style.m_gradientType = MWAWGraphicStyle::G_Linear;
angle=type=0;
break;
case 2:
style.m_gradientStopList.resize(2);
style.m_gradientStopList[0]=MWAWGraphicStyle::GradientStop(0.0, style.m_baseSurfaceColor);
style.m_gradientStopList[1]=MWAWGraphicStyle::GradientStop(1.0, style.m_baseLineColor);
style.m_gradientAngle = float(90+angle);
style.m_gradientType = MWAWGraphicStyle::G_Axial;
angle=type=0;
break;
case 3:
style.m_gradientStopList.resize(2);
style.m_gradientStopList[0]=MWAWGraphicStyle::GradientStop(0.0, style.m_baseSurfaceColor);
style.m_gradientStopList[1]=MWAWGraphicStyle::GradientStop(1.0, style.m_baseLineColor);
switch (subType) {
case 9:
style.m_gradientPercentCenter=MWAWVec2f(0.25f,0.25f);
break;
case 10:
style.m_gradientPercentCenter=MWAWVec2f(0.25f,0.75f);
break;
case 11:
style.m_gradientPercentCenter=MWAWVec2f(0.75f,0.75f);
break;
case 12:
style.m_gradientPercentCenter=MWAWVec2f(1.f,1.f);
break;
case 13:
style.m_gradientPercentCenter=MWAWVec2f(0.f,0.f);
break;
default:
f << "#subType=" << subType << ",";
case 8: // centered
break;
}
style.m_gradientType = MWAWGraphicStyle::G_Rectangular;
angle=type=0;
break;
case 7:
style.m_gradientStopList.resize(2);
style.m_gradientStopList[0]=MWAWGraphicStyle::GradientStop(0.0, style.m_baseSurfaceColor);
style.m_gradientStopList[1]=MWAWGraphicStyle::GradientStop(1.0, style.m_baseLineColor);
style.m_gradientType = MWAWGraphicStyle::G_Radial;
type = 0;
break;
default:
break;
}
if (type) f << "#type=" << type << ",";
if (angle) f << "#angle=" << angle << ",";
f << "subType=" << subType << ",";
f << "],";
style.m_extra = f.str();
return true;
}
int MsWksGraph::getEntryPicture(int zoneId, MWAWEntry &zone, bool autoSend, int order)
{
MsWksGraphInternal::Zone pict;
MWAWInputStreamPtr input=m_document.getInput();
long pos = input->tell();
if (!readPictHeader(pict))
return -1;
pict.m_zoneId = zoneId;
pict.m_pos.setBegin(pos);
libmwaw::DebugFile &ascFile = m_document.ascii();
libmwaw::DebugStream f;
int vers = version();
long debData = input->tell();
long dataSize = 0;
int versSize = 0;
switch (pict.m_subType) {
case 0:
case 1:
case 2:
case 3:
dataSize = 1;
break;
case 4: // arc
dataSize = 0xd;
break;
case 5: { // poly
input->seek(3, librevenge::RVNG_SEEK_CUR);
auto N = static_cast<int>(input->readULong(2));
dataSize = 9+N*8;
break;
}
case 7: { // picture
if (vers >= 3) versSize = 0x14;
dataSize = 5;
input->seek(debData+5+versSize-2, librevenge::RVNG_SEEK_SET);
dataSize += static_cast<int>(input->readULong(2));
break;
}
case 8: // group
if (vers >= 3) versSize = 4;
dataSize = 0x1b;
break;
case 9: // textbox v<=3
dataSize = 0x21;
if (vers >= 3) dataSize += 0x10;
break;
case 0xa: // chart v4
dataSize = 50;
break;
case 0xc: // equation v4
dataSize = 0x11;
break;
case 0xd: { // bitmap v4
input->seek(debData+0x29, librevenge::RVNG_SEEK_SET);
auto sz = long(input->readULong(4));
dataSize = 0x29+4+sz;
break;
}
case 0xe: { // spreadsheet v4
input->seek(debData+0xa7, librevenge::RVNG_SEEK_SET);
auto pSize = static_cast<int>(input->readULong(2));
if (pSize == 0) return -1;
dataSize = 0xa9+pSize;
if (!input->checkPosition(debData+dataSize))
return -1;
input->seek(debData+dataSize, librevenge::RVNG_SEEK_SET);
for (int i = 0; i < 2; i++) {
auto sz = long(input->readULong(4));
if (sz<0 || (sz>>28)) return -1;
dataSize += 4 + sz;
input->seek(sz, librevenge::RVNG_SEEK_CUR);
}
break;
}
case 0xf: { // textbox v4
input->seek(debData+0x39, librevenge::RVNG_SEEK_SET);
dataSize = 0x3b+ long(input->readULong(2));
break;
}
case 0x10: { // table v4
input->seek(debData+0x57, librevenge::RVNG_SEEK_SET);
dataSize = 0x59+ long(input->readULong(2));
input->seek(debData+dataSize, librevenge::RVNG_SEEK_SET);
for (int i = 0; i < 3; i++) {
auto sz = long(input->readULong(4));
if (sz<0 || ((sz>>28))) return -1;
dataSize += 4 + sz;
input->seek(sz, librevenge::RVNG_SEEK_CUR);
}
break;
}
default:
MWAW_DEBUG_MSG(("MsWksGraph::getEntryPicture: type %d is not umplemented\n", pict.m_subType));
return -1;
}
pict.m_pos.setEnd(debData+dataSize+versSize);
if (!input->checkPosition(pict.m_pos.end()))
return -1;
input->seek(debData, librevenge::RVNG_SEEK_SET);
if (versSize) {
switch (pict.m_subType) {
case 7: {
auto ptr = long(input->readULong(4));
f << std::hex << "ptr2=" << ptr << std::dec << ",";
f << "depth?=" << input->readLong(1) << ",";
float dim[4];
for (auto &d : dim) d = float(input->readLong(4))/65536.f;
MWAWBox2f box(MWAWVec2f(dim[1], dim[0]), MWAWVec2f(dim[3], dim[2]));
f << "bdbox2=" << box << ",";
break;
}
default:
break;
}
}
auto val = static_cast<int>(input->readLong(1)); // 0 and sometimes -1
if (val) f << "g0=" << val << ",";
pict.m_dataPos++;
if (pict.m_subType > 0xd) {
f << ", " << std::hex << input->readULong(4) << std::dec << ", BdMWAWBox2=(";
for (int i = 0; i < 4; i++)
f << float(input->readLong(4))/65536.f << ", ";
f << ")";
}
std::shared_ptr<MsWksGraphInternal::Zone> res;
switch (pict.m_subType) {
case 0: { // line
auto form = std::make_shared<MsWksGraphInternal::BasicShape>(pict);
res = form;
form->m_shape = MWAWGraphicShape::line(pict.m_box.min(), pict.m_box.max());
break;
}
case 1: // rect
case 2: // rectoval
case 3: { // circle
MWAWBox2f bdbox = pict.m_box;
auto form = std::make_shared<MsWksGraphInternal::BasicShape>(pict);
res = form;
form->m_shape.m_bdBox = form->m_shape.m_formBox = bdbox;
form->m_shape.m_type = (pict.m_subType==3) ? MWAWGraphicShape::Circle :
MWAWGraphicShape::Rectangle;
if (pict.m_subType==2) {
float sz=10;
if (bdbox.size().x() > 0 && bdbox.size().x() < 2*sz)
sz = bdbox.size().x()/2.f;
if (bdbox.size().y() > 0 && bdbox.size().y() < 2*sz)
sz = bdbox.size().y()/2.f;
form->m_shape.m_cornerWidth=MWAWVec2f(sz,sz);
}
break;
}
case 4: {
auto form = std::make_shared<MsWksGraphInternal::BasicShape>(pict);
res = form;
auto angle = float(input->readLong(2));
auto deltaAngle = float(input->readLong(2));
float angl2 = angle+((deltaAngle>0) ? deltaAngle : -deltaAngle);
float dim[4]; // real Bdbox
for (auto &d : dim) d = float(input->readLong(2));
MWAWBox2f realBox(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
form->m_shape=MWAWGraphicShape::arc(realBox,pict.m_box,MWAWVec2f(450.f-angl2,450.f-angle));
form->m_box = realBox;
break;
}
case 5: {
auto form = std::make_shared<MsWksGraphInternal::BasicShape>(pict);
res = form;
val = static_cast<int>(input->readULong(2));
bool smooth=false;
if (val==1)
smooth=true;
else if (val) f << "#smooth=" << val << ",";
auto numPt = static_cast<int>(input->readLong(2));
auto ptr = long(input->readULong(4));
if (!input->checkPosition(input->tell()+8*numPt))
return -1;
f << std::hex << "ptr2=" << ptr << std::dec << ",";
std::vector<MWAWVec2f> vertices;
for (int i = 0; i < numPt; i++) {
float x = float(input->readLong(4))/65336.f;
float y = float(input->readLong(4))/65336.f;
vertices.push_back(MWAWVec2f(x,y));
}
if (!smooth || numPt <= 2) {
form->m_shape=MWAWGraphicShape::polygon(pict.m_box);
form->m_shape.m_vertices = vertices;
break;
}
form->m_shape=MWAWGraphicShape::path(pict.m_box);
form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('M', vertices[0]));
MWAWVec2f middle=0.5f*(vertices[1]+vertices[0]);
form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('L', middle));
for (size_t pt=1; pt+1 < size_t(numPt); ++pt) {
middle=0.5f*(vertices[pt+1]+vertices[pt]);
form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('Q', middle, vertices[pt]));
}
form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('L',vertices[size_t(numPt-1)]));
if (vertices[0]==vertices[size_t(numPt)-1])
form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('Z'));
break;
}
case 7: {
val = static_cast<int>(input->readULong(vers >= 3 ? 1 : 2));
if (val) f << "g1=" << val << ",";
// skip size (already read)
pict.m_dataPos = input->tell()+2;
auto pct = std::make_shared<MsWksGraphInternal::DataPict>(pict);
res = pct;
ascFile.skipZone(pct->m_dataPos, pct->m_pos.end()-1);
break;
}
case 8:
res = readGroup(pict);
if (!res)
return -1;
break;
case 9: { // textbox normal
auto justify = MWAWParagraph::JustificationLeft;
val = static_cast<int>(input->readLong(2));
switch (val) {
case 0:
break;
case 1:
justify = MWAWParagraph::JustificationCenter;
break;
case 2:
justify = MWAWParagraph::JustificationFull;
break;
case -1:
justify = MWAWParagraph::JustificationRight;
break;
default:
f << "##align=" << val << ",";
break;
}
if (vers >= 3) {
f << "h=" << input->readLong(4) << ",";
for (int i = 0; i < 6; i++) {
val = static_cast<int>(input->readLong(2));
if (val) f << "g" << i+2 << "=" << val << ",";
}
pict.m_dataPos += 0x10;
}
f << "Fl=[";
for (int i = 0; i < 4; i++) {
val = static_cast<int>(input->readLong(2));
if (val) f << std::hex << val << std::dec << ",";
else f << ",";
}
f << "],";
auto numPos = static_cast<int>(input->readLong(2));
if (numPos < 0) return -1;
f << "numFonts=" << input->readLong(2);
long off[4];
for (auto &d : off) d = long(input->readULong(4));
f << ", Ptrs=[" << std::hex << std::setw(8) << off[2] << ", " << std::setw(8) << off[0]
<< ", " << std::dec << long(off[1]-off[0])
<< ", " << std::dec << long(off[3]-off[0]) << "]";
auto text = std::make_shared<MsWksGraphInternal::TextBox>(pict);
text->m_justify = justify;
text->m_numPositions = numPos;
res = text;
if (!readText(*text)) return -1;
res->m_pos.setEnd(input->tell());
break;
}
case 0xa: { // chart
auto chart = std::make_shared<MsWksGraphInternal::Chart>(pict);
int chartId = m_state->m_chartId++;
if (!m_tableParser->readChart(chartId, chart->m_style)) {
return -1;
}
m_tableParser->setChartZoneId(chartId, int(m_state->m_zonesList.size()));
chart->m_chartId = chartId;
res = chart;
res->m_pos.setEnd(input->tell());
break;
}
case 0xc: { // equation
auto ole = std::make_shared<MsWksGraphInternal::OLEZone>(pict);
res = ole;
int dim[2];
for (auto &d : dim) d = static_cast<int>(input->readLong(4));
ole->m_dim = MWAWVec2i(dim[0], dim[1]);
val = static_cast<int>(input->readULong(2)); // always 0x4f4d ?
f << "g0=" << std::hex << val << std::dec << ",";
ole->m_oleId=static_cast<int>(input->readULong(4));
val = static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "g1=" << val << ",";
break;
}
case 0xd: { // bitmap
libmwaw::DebugStream f2;
f2 << "Graphd(II): fl(";
long actPos = input->tell();
for (int i = 0; i < 2; i++)
f2 << input->readLong(2) << ", ";
f2 << "), ";
auto nCol = static_cast<int>(input->readLong(2));
auto nRow = static_cast<int>(input->readLong(2));
if (nRow <= 0 || nCol <= 0) return -1;
f2 << "nRow=" << nRow << ", " << "nCol=" << nCol << ", ";
f2 << std::hex << input->readULong(4) << std::dec << ", ";
for (int i = 0; i < 3; i++) {
f2 << "bdbox" << i << "=(";
for (int d= 0; d < 4; d++) f2 << input->readLong(2) << ", ";
f2 << "), ";
if (i == 1) f2 << "unk=" << input->readLong(2) << ", ";
}
auto sizeLine = static_cast<int>(input->readLong(2));
f2 << "lineSize(?)=" << sizeLine << ", ";
long bitmapSize = input->readLong(4);
f2 << "bitmapSize=" << std::hex << bitmapSize << ", ";
if (bitmapSize <= 0 || (bitmapSize%nRow) != 0) {
// sometimes, another row is added: only for big picture?
if (bitmapSize>0 && (bitmapSize%(nRow+1)) == 0) nRow++;
else if (bitmapSize < nCol*nRow || bitmapSize > 2*nCol*nRow)
return -1;
else { // maybe a not implemented case
MWAW_DEBUG_MSG(("MsWksGraph::getEntryPicture: bitmap size is a little odd\n"));
f2 << "###";
ascFile.addPos(actPos);
ascFile.addNote(f2.str().c_str());
ascFile.addDelimiter(input->tell(),'|');
break;
}
}
auto szCol = int(bitmapSize/nRow);
if (szCol < nCol) return -1;
ascFile.addPos(actPos);
ascFile.addNote(f2.str().c_str());
pict.m_dataPos = input->tell();
auto pct = std::make_shared<MsWksGraphInternal::DataBitmap>(pict);
pct->m_numRows = nRow;
pct->m_numCols = nCol;
pct->m_dataSize = bitmapSize;
res = pct;
break;
}
case 0xe: {
long actPos = input->tell();
ascFile.addPos(actPos);
ascFile.addNote("Graphe(I)");
// first: the picture ( fixme: kept while we do not parse the spreadsheet )
input->seek(144, librevenge::RVNG_SEEK_CUR);
actPos = input->tell();
ascFile.addPos(actPos);
ascFile.addNote("Graphe(pict)");
auto dSize = long(input->readLong(4));
if (dSize < 0) return -1;
pict.m_dataPos = actPos+4;
auto pct = std::make_shared<MsWksGraphInternal::DataPict>(pict);
pct->m_dataEndPos = actPos+4+dSize;
res = pct;
ascFile.skipZone(pct->m_dataPos, pct->m_dataEndPos-1);
input->seek(actPos+4+dSize, librevenge::RVNG_SEEK_SET);
// now the spreadsheet ( a classic WKS file )
actPos = input->tell();
dSize = long(input->readULong(4));
if (dSize < 0) return -1;
ascFile.addPos(actPos);
ascFile.addNote("Graphe(sheet)");
ascFile.skipZone(actPos+4, actPos+3+dSize);
#ifdef DEBUG_WITH_FILES
if (dSize > 0) {
librevenge::RVNGBinaryData file;
input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
input->readDataBlock(dSize, file);
static int volatile sheetName = 0;
libmwaw::DebugStream f2;
f2 << "Sheet-" << ++sheetName << ".wks";
libmwaw::Debug::dumpFile(file, f2.str().c_str());
}
#endif
input->seek(actPos+4+dSize, librevenge::RVNG_SEEK_SET);
actPos = input->tell();
ascFile.addPos(actPos);
ascFile.addNote("Graphe(colWidth?)"); // blocksize, unknown+list of 100 w
break;
}
case 0xf: { // new text box v4 (a picture is stored)
if (vers < 4) return -1;
auto textbox = std::make_shared<MsWksGraphInternal::TextBoxv4>(pict);
res = textbox;
textbox->m_ids[1] = long(input->readULong(4));
textbox->m_ids[2] = long(input->readULong(4));
f << "," << std::hex << input->readULong(4)<< std::dec << ",";
// always 0 ?
for (int i = 0; i < 6; i++) {
val = static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
textbox->m_text.setBegin(input->readLong(4));
textbox->m_text.setEnd(input->readLong(4));
// always 0 ?
val = static_cast<int>(input->readLong(2));
if (val) f << "f10=" << val << ",";
auto sz = long(input->readULong(4));
if (sz+0x3b != dataSize)
f << "###sz=" << sz << ",";
pict.m_dataPos = input->tell();
if (pict.m_dataPos != pict.m_pos.end()) {
#ifdef DEBUG_WITH_FILES
librevenge::RVNGBinaryData file;
input->readDataBlock(pict.m_pos.end()-pict.m_dataPos, file);
static int volatile textboxName = 0;
libmwaw::DebugStream f2;
f2 << "TextBox-" << ++textboxName << ".pct";
libmwaw::Debug::dumpFile(file, f2.str().c_str());
#endif
ascFile.skipZone(pict.m_dataPos, pict.m_pos.end()-1);
}
break;
}
case 0x10: { // basic table
libmwaw::DebugStream f2;
f2 << "Graph10(II): fl=(";
long actPos = input->tell();
for (int i = 0; i < 3; i++)
f2 << input->readLong(2) << ", ";
f2 << "), ";
auto nRow = static_cast<int>(input->readLong(2));
auto nCol = static_cast<int>(input->readLong(2));
f2 << "nRow=" << nRow << ", " << "nCol=" << nCol << ", ";
// basic name font
auto nbChar = static_cast<int>(input->readULong(1));
if (nbChar > 31) return -1;
std::string fName;
for (int c = 0; c < nbChar; c++)
fName+=char(input->readLong(1));
f2 << fName << ",";
input->seek(actPos+10+32, librevenge::RVNG_SEEK_SET);
auto fSz = static_cast<int>(input->readLong(2));
if (fSz) f << "fSz=" << fSz << ",";
ascFile.addDelimiter(input->tell(),'|');
ascFile.addPos(actPos);
ascFile.addNote(f2.str().c_str());
input->seek(actPos+0x40, librevenge::RVNG_SEEK_SET);
// a pict
actPos = input->tell();
ascFile.addPos(actPos);
ascFile.addNote("Graph10(pict)");
auto dSize = long(input->readLong(4));
if (dSize < 0) return -1;
pict.m_dataPos = actPos+4;
auto pct = std::make_shared<MsWksGraphInternal::DataPict>(pict);
pct->m_dataEndPos = actPos+4+dSize;
res = pct;
ascFile.skipZone(pct->m_dataPos, pct->m_dataEndPos-1);
input->seek(actPos+4+dSize, librevenge::RVNG_SEEK_SET);
// the table
f << "numRows=" << nRow << ",nCols=" << nCol << ",";
std::shared_ptr<MsWksGraphInternal::Table> table(new MsWksGraphInternal::Table(pict));
int tableId = m_state->m_tableId++;
if (m_tableParser->readTable(nCol, nRow, tableId, table->m_style)) {
table->m_tableId = tableId;
res=table;
}
break;
}
default:
ascFile.addDelimiter(debData, '|');
break;
}
if (!res)
res.reset(new MsWksGraphInternal::Zone(pict));
res->m_extra += f.str();
if (order > -1000)
res->m_order = order;
if (!autoSend)
res->m_doNotSend = true;
res->m_fileId = int(m_state->m_zonesList.size());
m_state->m_zonesList.push_back(res);
f.str("");
f << "Entries(Graph" << std::hex << res->m_subType << std::dec << "):" << *res;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
zone = res->m_pos;
zone.setType("Graphic");
input->seek(res->m_pos.end(), librevenge::RVNG_SEEK_SET);
return res->m_fileId;
}
void MsWksGraph::computePositions(int zoneId, std::vector<int> &linesH, std::vector<int> &pagesH)
{
auto numLines = int(linesH.size());
auto nPages = int(pagesH.size());
bool isSpreadsheet=m_parserState->m_type==MWAWParserState::Spreadsheet;
for (auto zone : m_state->m_zonesList) {
if (zone->m_zoneId != -1 && zoneId != zone->m_zoneId) continue;
if (zone->m_line >= 0) {
int h = 0;
if (zone->m_line >= numLines) {
MWAW_DEBUG_MSG(("MsWksGraph::computePositions: linepos is too big\n"));
if (numLines)
h = linesH[size_t(numLines)-1];
}
else
h = linesH[size_t(zone->m_line)];
zone->m_finalDecal = MWAWVec2f(0, float(h));
}
if (zone->m_page < 0 && (isSpreadsheet || zone->m_page != -2)) {
float h = zone->m_finalDecal.y();
float middleH=zone->m_box.center().y();
h+=middleH;
int p = 0;
while (p < nPages) {
if (h < float(pagesH[size_t(p)])) break;
h -= float(pagesH[size_t(p++)]);
}
zone->m_page = p;
zone->m_finalDecal.setY(h-middleH);
}
}
}
int MsWksGraph::getEntryPictureV1(int zoneId, MWAWEntry &zone, bool autoSend)
{
MWAWInputStreamPtr input=m_document.getInput();
if (input->isEnd()) return -1;
long pos = input->tell();
if (input->readULong(1) != 1) return -1;
libmwaw::DebugFile &ascFile = m_document.ascii();
libmwaw::DebugStream f;
auto ptr = long(input->readULong(2));
auto flag = static_cast<int>(input->readULong(1));
long size = long(input->readULong(2))+6;
if (size < 22) return -1;
// check if we can go to the next zone
if (!input->checkPosition(pos+size)) return -1;
std::shared_ptr<MsWksGraphInternal::DataPict> pict(new MsWksGraphInternal::DataPict());
pict->m_zoneId = zoneId;
pict->m_subType = 0x100;
pict->m_pos.setBegin(pos);
pict->m_pos.setLength(size);
if (ptr) f << std::hex << "ptr0=" << ptr << ",";
if (flag) f << std::hex << "fl=" << flag << ",";
ptr = input->readLong(4);
if (ptr)
f << "ptr1=" << std::hex << ptr << std::dec << ";";
pict->m_line = static_cast<int>(input->readLong(2));
auto val = static_cast<int>(input->readLong(2)); // almost always equal to m_linePOs
if (val != pict->m_line)
f << "linePos2=" << std::hex << val << std::dec << ",";
int dim[4]; // pictbox
for (auto &d : dim) d = static_cast<int>(input->readLong(2));
pict->m_box = MWAWBox2f(MWAWVec2f(float(dim[1]), float(dim[0])), MWAWVec2f(float(dim[3]),float(dim[2])));
MWAWVec2i pictMin(pict->m_box.min()), pictSize(pict->m_box.size());
if (pictSize.x() < 0 || pictSize.y() < 0) return -1;
if (pictSize.x() > 3000 || pictSize.y() > 3000 ||
pictMin.x() < -200 || pictMin.y() < -200) return -1;
pict->m_dataPos = input->tell();
zone = pict->m_pos;
zone.setType("GraphEntry");
pict->m_extra = f.str();
if (!autoSend)
pict->m_doNotSend=true;
pict->m_fileId = int(m_state->m_zonesList.size());
m_state->m_zonesList.push_back(pict);
f.str("");
f << "Entries(GraphEntry):" << *pict;
ascFile.skipZone(pict->m_dataPos, pict->m_pos.end()-1);
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pict->m_pos.end(), librevenge::RVNG_SEEK_SET);
return pict->m_fileId;
}
// a list of picture
bool MsWksGraph::readRB(MWAWInputStreamPtr input, MWAWEntry const &entry, int kind)
{
libmwaw::DebugFile &ascFile = m_document.ascii();
libmwaw::DebugStream f;
long beginRB, endRB;
long pos=input->tell();
f << "Entries(" << entry.name() << "):";
const int vers=version();
switch (kind) {
case 0:
pos=beginRB=entry.begin();
endRB=entry.end();
break;
case 2:
if (input->readLong(1)!=3) return false;
f << "id=" << input->readLong(1) << ",";
MWAW_FALLTHROUGH;
case 1: {
unsigned long dSz=input->readULong(4);
beginRB=input->tell();
if (dSz&0x80000000) {
f << "flags[high],";
dSz &= 0x7FFFFFFF;
}
endRB=beginRB+long(dSz);
break;
}
default:
MWAW_DEBUG_MSG(("MsWksGraph::readRB: unknown kind\n"));
return false;
}
long const headerSize=vers<3 ? 0x150 : 0x164;
if (endRB-beginRB+2 < headerSize || !input->checkPosition(endRB)) return false;
MsWksGraphInternal::RBZone zone;
if (vers==4)
zone.m_isMain = entry.name()=="RBDR";
zone.m_id = entry.id();
input->seek(beginRB, librevenge::RVNG_SEEK_SET);
f << input->readLong(4) << ", ";
for (int i = 0; i < 4; i++) {
long val = input->readLong(4);
if (val) f << "#t" << i << "=" << val << ", ";
}
f << "type?=" << std::hex << input->readLong(2) << std::dec << ", ";
f << "numPage=" << input->readLong(2) << ", ";
for (int i = 0; i < 11; i++) {
long val = input->readLong(2);
if (!val) continue;
if (i >= 8 && (val < -100 || val > 100)) f << "###";
f << "f" << i << "=" << val << ", ";
}
f << ", unk=(";
for (int i = 0; i < 2; i++)
f << input->readLong(4) << ",";
f << "), ";
for (int i = 0; i < 9; i++) {
long val = input->readLong(2);
if (val) f << "#u" << i << "=" << val << ", ";
}
f << std::hex << "sz?=" << input->readLong(4) << std::dec << ", ";
for (int i = 0; i < 2; i++) {
long val = input->readLong(2);
if (val) f << "#v" << i << "=" << val << ", ";
}
f << "unk1=(";
for (int i = 0; i < 9; i++) {
long val = input->readLong(2);
if (val) f << val << ",";
else f << "_,";
}
f << "), ";
if (vers>=3) {
long aPos=input->tell();
std::string oleName;
while (input->tell() < beginRB+headerSize-2) {
auto val = char(input->readLong(1));
if (val == 0) break;
oleName+= val;
if (oleName.length() >= 10) break;
}
if (!oleName.empty()) {
zone.m_frame = oleName;
f << "ole='" << oleName << "', ";
}
ascFile.addDelimiter(input->tell(),'|');
input->seek(aPos+20, librevenge::RVNG_SEEK_SET);
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << entry.name() << "-II:";
for (int i=0; i<118; ++i) {
auto val = static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << std::hex << val << std::dec << ",";
}
// can happens at least with vers=2 and this means that the size is not set
if (endRB-beginRB+2 == headerSize)
endRB=-1;
auto N = static_cast<int>(input->readLong(2));
f << "N=" << N << ",";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
if (N == 0) return true;
if (endRB>0)
input->pushLimit(endRB);
size_t actId = m_state->m_zonesList.size();
for (int i = 0; i < N; i++) {
pos = input->tell();
MWAWEntry pictZone;
if (getEntryPicture(vers==4 ? 0 : entry.id(), pictZone)>=0)
continue;
MWAW_DEBUG_MSG(("MsWksDocument::readGroup: can not find the end of group\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
if (endRB>0)
input->popLimit();
for (size_t z = actId; z < m_state->m_zonesList.size(); z++) {
auto pictZone = m_state->m_zonesList[z];
if (!pictZone) continue;
zone.m_idList.push_back(int(z));
if (!zone.m_isMain)
pictZone->m_page = -2;
}
if (endRB>0 && input->tell() < endRB) {
f.str("");
f << entry.name() << "-end:###";
MWAW_DEBUG_MSG(("MsWksGraph::readRB: find some extra data\n"));
ascFile.addPos(input->tell());
ascFile.addNote(f.str().c_str());
}
if (endRB>0)
input->seek(endRB, librevenge::RVNG_SEEK_SET);
checkTextBoxLinks(zone);
int zId = zone.getId();
if (m_state->m_RBsMap.find(zId) != m_state->m_RBsMap.end()) {
MWAW_DEBUG_MSG(("MsWksGraph::readRB: zone %d is already filled\n", zId));
// ok, let's merge the two zone
auto &orig=m_state->m_RBsMap.find(zId)->second;
orig.m_idList.insert(orig.m_idList.end(), zone.m_idList.begin(), zone.m_idList.end());
if (orig.m_frame.empty()) orig.m_frame=zone.m_frame;
}
else
m_state->m_RBsMap[zId]=zone;
return true;
}
void MsWksGraph::checkTextBoxLinks(MsWksGraphInternal::RBZone &rbZone)
{
auto const &listIds = rbZone.m_idList;
std::string const &fName = rbZone.m_frame;
auto numZones = int(m_state->m_zonesList.size());
std::set<long> textIds;
std::map<long,long> prevLinks, nextLinks;
bool ok = true;
for (auto id : listIds) {
if (id < 0 || id >= numZones) continue;
auto zone = m_state->m_zonesList[size_t(id)];
if (zone->type() != MsWksGraphInternal::Zone::Textv4)
continue;
static_cast<MsWksGraphInternal::TextBoxv4 &>(*zone).m_frame = fName;
if (textIds.find(zone->m_ids[0]) != textIds.end()) {
MWAW_DEBUG_MSG(("MsWksGraph::checkTextBoxLinks: id %lX already exists\n", static_cast<long unsigned int>(zone->m_ids[0])));
ok = false;
break;
}
textIds.insert(zone->m_ids[0]);
if (zone->m_ids[1]>0)
prevLinks.insert(std::map<long,long>::value_type(zone->m_ids[0],zone->m_ids[1]));
if (zone->m_ids[2]>0)
nextLinks.insert(std::map<long,long>::value_type(zone->m_ids[0],zone->m_ids[2]));
}
size_t numLinks = nextLinks.size();
for (auto link : nextLinks) {
if (prevLinks.find(link.second)==prevLinks.end() ||
prevLinks.find(link.second)->second!=link.first) {
MWAW_DEBUG_MSG(("MsWksGraph::checkTextBoxLinks: can not find prevLinks: %lX<->%lX already exists\n", static_cast<long unsigned int>(link.first), static_cast<long unsigned int>(link.second)));
ok = false;
break;
}
// check loops
size_t w = 0;
long actText = link.second;
while (1) {
if (nextLinks.find(actText)==nextLinks.end())
break;
actText = nextLinks.find(actText)->second;
if (w++ > numLinks) {
MWAW_DEBUG_MSG(("MsWksGraph::checkTextBoxLinks:find a loop for id %lX\n", static_cast<long unsigned int>(link.first)));
ok = false;
break;
}
}
}
if (!ok) {
MWAW_DEBUG_MSG(("MsWksGraph::checkTextBoxLinks: problem find with text links\n"));
for (auto zone : m_state->m_zonesList) {
if (zone->type() != MsWksGraphInternal::Zone::Textv4)
continue;
zone->m_ids[1] = zone->m_ids[2] = 0;
}
}
}
bool MsWksGraph::readPictureV4(MWAWInputStreamPtr /*input*/, MWAWEntry const &entry)
{
if (!entry.hasType("PICT")) {
MWAW_DEBUG_MSG(("MsWksGraph::readPictureV4: unknown type='%s'\n", entry.type().c_str()));
return false;
}
entry.setParsed(true);
MsWksGraphInternal::Zone pict;
pict.m_pos = entry;
pict.m_dataPos = entry.begin();
pict.m_page = -2;
pict.m_zoneId = -1;
auto pct = std::make_shared<MsWksGraphInternal::DataPict>(pict);
std::shared_ptr<MsWksGraphInternal::Zone>res(pct);
m_document.ascii().skipZone(entry.begin(), entry.end()-1);
auto zId = int(m_state->m_zonesList.size());
res->m_fileId = zId;
m_state->m_zonesList.push_back(res);
return true;
}
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
// read/send a group
void MsWksGraph::sendGroup(int id, MWAWPosition const &pos)
{
if (id<0 || id>=int(m_state->m_zonesList.size()) || !m_state->m_zonesList[size_t(id)] ||
m_state->m_zonesList[size_t(id)]->type()!=MsWksGraphInternal::Zone::Group) {
MWAW_DEBUG_MSG(("MsWksGraph::sendGroup: can not find group %d\n", id));
return;
}
MWAWListenerPtr listener=m_parserState->getMainListener();
if (!listener) return;
auto &group=static_cast<MsWksGraphInternal::GroupZone &>(*m_state->m_zonesList[size_t(id)]);
group.m_isSent = true;
if (listener->getType()==MWAWListener::Graphic) {
sendGroup(group, m_parserState->m_graphicListener);
return;
}
if (!canCreateGraphic(group)) {
if (pos.m_anchorTo == MWAWPosition::Char || pos.m_anchorTo == MWAWPosition::CharBaseLine) {
std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
(new MsWksGraphInternal::SubDocument(*this, m_document.getInput(), MsWksGraphInternal::SubDocument::Group, id));
listener->insertTextBox(pos, subdoc);
return;
}
MWAWPosition childPos(pos);
childPos.setSize(MWAWVec2f(0,0));
sendGroupChild(id, childPos);
return;
}
MWAWGraphicEncoder graphicEncoder;
MWAWGraphicListenerPtr graphicListener(new MWAWGraphicListener(*m_parserState, group.m_box, &graphicEncoder));
graphicListener->startDocument();
sendGroup(group, graphicListener);
graphicListener->endDocument();
MWAWEmbeddedObject picture;
if (graphicEncoder.getBinaryResult(picture))
listener->insertPicture(pos, picture);
}
void MsWksGraph::sendGroupChild(int id, MWAWPosition const &pos)
{
if (id<0 || id>=int(m_state->m_zonesList.size()) || !m_state->m_zonesList[size_t(id)] ||
m_state->m_zonesList[size_t(id)]->type()!=MsWksGraphInternal::Zone::Group) {
MWAW_DEBUG_MSG(("MsWksGraph::sendGroupChild: can not find group %d\n", id));
return;
}
MWAWListenerPtr listener=m_parserState->getMainListener();
if (!listener) return;
auto &group=static_cast<MsWksGraphInternal::GroupZone &>(*m_state->m_zonesList[size_t(id)]);
group.m_isSent = true;
MWAWInputStreamPtr input=m_document.getInput();
size_t numZones=m_state->m_zonesList.size();
size_t numChild=group.m_childs.size(), childNotSent=0;
int numDataToMerge=0;
MWAWBox2f partialBdBox;
MWAWPosition partialPos(pos);
bool isDraw=listener->getType()==MWAWListener::Graphic;
for (size_t c=0; c < numChild; ++c) {
int cId = group.m_childs[c];
if (cId < 0 || cId >= int(numZones) || !m_state->m_zonesList[size_t(cId)])
continue;
auto const &child=*(m_state->m_zonesList[size_t(cId)]);
bool isLast=false;
bool canMerge=false;
if (isDraw)
canMerge=false;
else if (child.type()==MsWksGraphInternal::Zone::Shape || child.type()==MsWksGraphInternal::Zone::Text) {
MWAWBox2f origBdBox=child.getLocalBox();
MWAWVec2f decal = child.m_decal[0] + child.m_decal[1];
MWAWBox2f localBdBox(origBdBox[0]+decal, origBdBox[1]+decal);
if (numDataToMerge == 0)
partialBdBox=localBdBox;
else
partialBdBox=partialBdBox.getUnion(localBdBox);
canMerge=true;
}
else if (child.type()==MsWksGraphInternal::Zone::Group &&
canCreateGraphic(static_cast<MsWksGraphInternal::GroupZone const &>(child))) {
if (numDataToMerge == 0)
partialBdBox=child.getLocalBox();
else
partialBdBox=partialBdBox.getUnion(child.getLocalBox());
canMerge=true;
}
if (canMerge) {
++numDataToMerge;
if (c+1 < numChild)
continue;
isLast=true;
}
if (numDataToMerge>1) {
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) {
int localCId = group.m_childs[ch];
if (localCId < 0 || localCId >= int(numZones) || !m_state->m_zonesList[size_t(localCId)])
continue;
auto const &localChild=*(m_state->m_zonesList[size_t(localCId)]);
MWAWBox2f origBdBox=localChild.getLocalBox(false);
MWAWVec2f decal=localChild.m_decal[0]+localChild.m_decal[1];
MWAWPosition pictPos(origBdBox[0]+decal, origBdBox.size(), librevenge::RVNG_POINT);
pictPos.m_anchorTo=MWAWPosition::Page;
pictPos.m_wrapping = MWAWPosition::WBackground;
if (localChild.type()==MsWksGraphInternal::Zone::Group)
sendGroup(static_cast<MsWksGraphInternal::GroupZone const &>(localChild), graphicListener);
else if (localChild.type()==MsWksGraphInternal::Zone::Shape) {
auto const &shape=static_cast<MsWksGraphInternal::BasicShape const &>(localChild);
graphicListener->insertShape(pictPos, shape.m_shape, shape.getStyle());
}
else if (localChild.type()==MsWksGraphInternal::Zone::Text) {
std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
(new MsWksGraphInternal::SubDocument(const_cast<MsWksGraph &>(*this), input, MsWksGraphInternal::SubDocument::TextBox, localCId));
// a textbox can not have border
MWAWGraphicStyle style(localChild.m_style);
style.m_lineWidth=0;
graphicListener->insertTextBox(pictPos, subdoc, style);
}
}
graphicListener->endDocument();
MWAWEmbeddedObject picture;
if (graphicEncoder.getBinaryResult(picture)) {
partialPos.setOrigin(pos.origin()+partialBdBox[0]-group.m_box[0]);
partialPos.setSize(partialBdBox.size());
listener->insertPicture(partialPos, picture);
if (isLast)
break;
childNotSent=c;
}
}
// time to send back the data
for (; childNotSent <= c; ++childNotSent)
send(group.m_childs[childNotSent],pos);
numDataToMerge=0;
}
}
bool MsWksGraph::canCreateGraphic(MsWksGraphInternal::GroupZone const &group) const
{
if (m_parserState->getMainListener()->getType()==MWAWListener::Graphic) return false;
auto numZones = int(m_state->m_zonesList.size());
for (auto cId : group.m_childs) {
if (cId < 0 || cId >= numZones || !m_state->m_zonesList[size_t(cId)])
continue;
auto const &child=*(m_state->m_zonesList[size_t(cId)]);
if (child.m_page!=group.m_page)
return false;
switch (child.type()) {
case MsWksGraphInternal::Zone::Shape:
case MsWksGraphInternal::Zone::Text:
break;
case MsWksGraphInternal::Zone::Group:
if (!canCreateGraphic(static_cast<MsWksGraphInternal::GroupZone const &>(child)))
return false;
break;
case MsWksGraphInternal::Zone::Bitmap:
case MsWksGraphInternal::Zone::ChartZone:
case MsWksGraphInternal::Zone::OLE:
case MsWksGraphInternal::Zone::Pict:
case MsWksGraphInternal::Zone::TableZone:
case MsWksGraphInternal::Zone::Textv4:
case MsWksGraphInternal::Zone::Unknown:
#if !defined(__clang__)
default:
#endif
return false;
}
}
return true;
}
void MsWksGraph::sendGroup(MsWksGraphInternal::GroupZone const &group, MWAWGraphicListenerPtr &listener) const
{
if (!listener || !listener->isDocumentStarted()) {
MWAW_DEBUG_MSG(("MsWksGraph::sendGroup: the listener is bad\n"));
return;
}
auto numZones = int(m_state->m_zonesList.size());
MWAWInputStreamPtr input=m_document.getInput();
for (auto cId : group.m_childs) {
if (cId < 0 || cId >= numZones || !m_state->m_zonesList[size_t(cId)])
continue;
auto const &child=*(m_state->m_zonesList[size_t(cId)]);
MWAWVec2f decal=child.m_decal[0]+child.m_decal[1];
MWAWPosition pictPos(child.m_box[0]+decal, child.m_box.size(), librevenge::RVNG_POINT);
pictPos.m_anchorTo=MWAWPosition::Page;
pictPos.m_wrapping = MWAWPosition::WBackground;
if (child.type()==MsWksGraphInternal::Zone::Group)
sendGroup(static_cast<MsWksGraphInternal::GroupZone const &>(child), listener);
else if (child.type()==MsWksGraphInternal::Zone::Shape) {
auto const &shape=static_cast<MsWksGraphInternal::BasicShape const &>(child);
listener->insertShape(pictPos, shape.m_shape, shape.getStyle());
}
else if (child.type()==MsWksGraphInternal::Zone::Text) {
std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
(new MsWksGraphInternal::SubDocument(const_cast<MsWksGraph &>(*this), input, MsWksGraphInternal::SubDocument::TextBox, cId));
// a textbox can not have border
MWAWGraphicStyle style(child.m_style);
style.m_lineWidth=0;
listener->insertTextBox(pictPos, subdoc, style);
}
else {
MWAW_DEBUG_MSG(("MsWksGraph::sendGroup: find some unexpected child\n"));
}
}
}
std::shared_ptr<MsWksGraphInternal::GroupZone> MsWksGraph::readGroup(MsWksGraphInternal::Zone &header)
{
std::shared_ptr<MsWksGraphInternal::GroupZone> group(new MsWksGraphInternal::GroupZone(header));
libmwaw::DebugStream f;
MWAWInputStreamPtr input=m_document.getInput();
input->seek(header.m_dataPos, librevenge::RVNG_SEEK_SET);
float dim[4];
for (auto &d : dim) d = float(input->readLong(4));
group->m_box=MWAWBox2f(MWAWVec2f(dim[0],dim[1]), MWAWVec2f(dim[2],dim[3]));
group->m_finalDecal=MWAWVec2f(0,0);
long ptr[2];
for (auto &p : ptr) p = long(input->readULong(4));
f << "ptr0=" << std::hex << ptr[0] << std::dec << ",";
if (ptr[0] != ptr[1])
f << "ptr1=" << std::hex << ptr[1] << std::dec << ",";
if (version() >= 3) {
auto val = static_cast<int>(input->readULong(4));
if (val) f << "g1=" << val << ",";
}
input->seek(header.m_pos.end()-2, librevenge::RVNG_SEEK_SET);
auto N = static_cast<int>(input->readULong(2));
for (int i = 0; i < N; i++) {
long pos = input->tell();
MWAWEntry childZone;
int childId = getEntryPicture(header.m_zoneId, childZone, false);
if (childId < 0) {
MWAW_DEBUG_MSG(("MsWksGraph::readGroup: can not find child\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
f << "#child,";
break;
}
group->m_childs.push_back(childId);
}
group->m_extra += f.str();
group->m_pos.setEnd(input->tell());
return group;
}
// read/send a textbox zone
bool MsWksGraph::readText(MsWksGraphInternal::TextBox &textBox)
{
if (textBox.m_numPositions < 0) return false; // can an empty text exist
libmwaw::DebugFile &ascFile = m_document.ascii();
libmwaw::DebugStream f;
f << "Entries(SmallText):";
MWAWInputStreamPtr input=m_document.getInput();
long pos = input->tell();
if (!input->checkPosition(pos+4*(textBox.m_numPositions+1))) return false;
// first read the set of (positions, font)
f << "pos=[";
int nbFonts = 0;
for (int i = 0; i <= textBox.m_numPositions; i++) {
auto fPos = static_cast<int>(input->readLong(2));
auto form = static_cast<int>(input->readLong(2));
f << fPos << ":" << form << ", ";
if (fPos < 0 || form < -1) return false;
if ((form == -1 && i != textBox.m_numPositions) ||
(!textBox.m_positions.empty() && fPos < textBox.m_positions.back())) {
MWAW_DEBUG_MSG(("MsWksGraph::readText: find odd positions\n"));
f << "#";
continue;
}
textBox.m_positions.push_back(fPos);
textBox.m_formats.push_back(form);
if (form >= nbFonts) nbFonts = form+1;
}
f << "] ";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
pos = input->tell();
f.str("");
f << "SmallText:Fonts ";
// actualPos, -1, only exists if actualPos!= 0 ? We ignored it.
input->readLong(2);
if (input->readLong(2) != -1)
input->seek(pos,librevenge::RVNG_SEEK_SET);
else {
ascFile.addPos(pos);
ascFile.addNote("SmallText:char Pos");
pos = input->tell();
}
f.str("");
long endFontPos = input->tell();
auto sizeOfData = long(input->readULong(4));
int numFonts = (sizeOfData%0x12 == 0) ? int(sizeOfData/0x12) : 0;
if (numFonts >= nbFonts) {
endFontPos = input->tell()+4+sizeOfData;
ascFile.addPos(pos);
ascFile.addNote("SmallText: Fonts");
for (int i = 0; i < numFonts; i++) {
pos = input->tell();
MWAWFont font;
if (!readFont(font)) {
input->seek(endFontPos, librevenge::RVNG_SEEK_SET);
break;
}
textBox.m_fontsList.push_back(font);
f.str("");
f << "SmallText:Font"<< i
<< "(" << font.getDebugString(m_parserState->m_fontConverter) << "),";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
pos = input->tell();
}
}
int nChar = textBox.m_positions.back()-1;
if (nbFonts > int(textBox.m_fontsList.size())) {
MWAW_DEBUG_MSG(("MsWksGraph::readText: can not read the fonts\n"));
ascFile.addPos(pos);
ascFile.addNote("SmallText:###");
input->seek(endFontPos,librevenge::RVNG_SEEK_SET);
textBox.m_fontsList.resize(0);
textBox.m_positions.resize(0);
textBox.m_numPositions = 0;
}
// now, syntax is : long(size) + size char
// - 0x16 - 0 - 0 - Fonts (default fonts)
// - 0x08 followed by two long, maybe interesting to look
// - 0x0c (or 0x18) seems followed by small int
// - nbChar : the strings (final)
f.str("");
f << "SmallText:";
while (1) {
if (input->isEnd()) return false;
pos = input->tell();
sizeOfData = long(input->readULong(4));
if (sizeOfData == nChar) {
bool ok = true;
// ok we try to read the string
std::string chaine("");
for (int i = 0; i < sizeOfData; i++) {
auto c = static_cast<unsigned char>(input->readULong(1));
if (c == 0) {
ok = false;
break;
}
chaine += char(c);
}
if (!ok)
input->seek(pos+4,librevenge::RVNG_SEEK_SET);
else {
textBox.m_text = chaine;
f << "=" << chaine;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
return true;
}
}
if (sizeOfData <= 100+nChar && (sizeOfData%2==0)) {
if (input->seek(sizeOfData, librevenge::RVNG_SEEK_CUR) != 0) return false;
f << "#";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
f.str("");
f << "SmallText:Text";
continue;
}
// fixme: we can try to find the next string
MWAW_DEBUG_MSG(("MsWksGraph::readText: problem reading text\n"));
f << "#";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
break;
}
return false;
}
bool MsWksGraph::readFont(MWAWFont &font)
{
int vers = version();
MWAWInputStreamPtr input=m_document.getInput();
long pos = input->tell();
libmwaw::DebugStream f;
if (!input->checkPosition(pos+18))
return false;
font = MWAWFont();
for (int i = 0; i < 3; i++) {
auto val = static_cast<int>(input->readLong(2));
if (val)
f << "f" << i << "=" << val << ",";
}
font.setFont(static_cast<int>(input->readULong(2)));
auto flags = static_cast<int>(input->readULong(1));
uint32_t flag = 0;
if (flags & 0x1) flag |= MWAWFont::boldBit;
if (flags & 0x2) flag |= MWAWFont::italicBit;
if (flags & 0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
if (flags & 0x8) flag |= MWAWFont::embossBit;
if (flags & 0x10) flag |= MWAWFont::shadowBit;
if (flags & 0x20) {
if (vers==1)
font.set(MWAWFont::Script(20,librevenge::RVNG_PERCENT,80));
else
font.set(MWAWFont::Script::super100());
}
if (flags & 0x40) {
if (vers==1)
font.set(MWAWFont::Script(-20,librevenge::RVNG_PERCENT,80));
else
font.set(MWAWFont::Script::sub100());
}
if (flags & 0x80) f << "#smaller,";
font.setFlags(flag);
auto val = static_cast<int>(input->readULong(1));
if (val) f << "#flags2=" << val << ",";
font.setSize(float(input->readULong(2)));
unsigned char color[3];
for (auto &c : color) c = static_cast<unsigned char>(input->readULong(2)>>8);
font.setColor(MWAWColor(color[0],color[1],color[2]));
font.m_extra = f.str();
return true;
}
void MsWksGraph::sendTextBox(int zoneId, MWAWListenerPtr listener)
{
if (!listener || !listener->canWriteText()) {
MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: can not find get access to the listener\n"));
return;
}
if (zoneId < 0 || zoneId >= int(m_state->m_zonesList.size())) {
MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: can not find textbox %d\n", zoneId));
return;
}
auto zone = m_state->m_zonesList[size_t(zoneId)];
if (!zone) return;
auto &textBox = static_cast<MsWksGraphInternal::TextBox &>(*zone);
listener->setFont(MWAWFont(20,12));
MWAWParagraph para;
para.m_justify=textBox.m_justify;
listener->setParagraph(para);
auto numFonts = int(textBox.m_fontsList.size());
int actFormatPos = 0;
auto numFormats = int(textBox.m_formats.size());
if (numFormats != int(textBox.m_positions.size())) {
MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: positions and formats have different length\n"));
if (numFormats > int(textBox.m_positions.size()))
numFormats = int(textBox.m_positions.size());
}
for (size_t i = 0; i < textBox.m_text.length(); i++) {
if (actFormatPos < numFormats && textBox.m_positions[size_t(actFormatPos)]==int(i)) {
int id = textBox.m_formats[size_t(actFormatPos++)];
if (id < 0 || id >= numFonts) {
MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: can not find a font\n"));
}
else
listener->setFont(textBox.m_fontsList[size_t(id)]);
}
auto c = static_cast<unsigned char>(textBox.m_text[i]);
switch (c) {
case 0x9:
MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: find some tab\n"));
listener->insertChar(' ');
break;
case 0xd:
if (i+1 != textBox.m_text.length())
listener->insertEOL();
break;
case 0x19:
listener->insertField(MWAWField(MWAWField::Title));
break;
case 0x18:
listener->insertField(MWAWField(MWAWField::PageNumber));
break;
case 0x16:
MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: find some time\n"));
listener->insertField(MWAWField(MWAWField::Time));
break;
case 0x17:
MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: find some date\n"));
listener->insertField(MWAWField(MWAWField::Date));
break;
case 0x14: // fixme
MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: footnote are not implemented\n"));
break;
default:
listener->insertCharacter(c);
break;
}
}
}
void MsWksGraph::send(int id, MWAWPosition const &pos)
{
if (id < 0 || id >= int(m_state->m_zonesList.size())) {
MWAW_DEBUG_MSG(("MsWksGraph::send: can not find zone %d\n", id));
return;
}
MWAWListenerPtr listener=m_parserState->getMainListener();
if (!listener) return;
auto zone = m_state->m_zonesList[size_t(id)];
zone->m_isSent = true;
MWAWPosition pictPos(pos);
if (pos.size()[0]<=0 || pos.size()[1]<=0)
pictPos = zone->getPosition(pos.m_anchorTo);
if (pictPos.m_anchorTo == MWAWPosition::Page)
pictPos.setOrigin(pictPos.origin()+m_state->m_leftTopPos);
MWAWInputStreamPtr input=m_document.getInput();
bool isDraw=listener->getType()==MWAWListener::Graphic;
switch (zone->type()) {
case MsWksGraphInternal::Zone::Text: {
auto &textbox = static_cast<MsWksGraphInternal::TextBox &>(*zone);
MWAWBox2f box(MWAWVec2f(0,0),textbox.m_box.size());
std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
(new MsWksGraphInternal::SubDocument(*this, input, MsWksGraphInternal::SubDocument::TextBox, id));
// a textbox can not have border
MWAWGraphicStyle style(textbox.m_style);
style.m_lineWidth=0;
if (isDraw) {
listener->insertTextBox(pictPos, subdoc, style);
return;
}
MWAWPosition textPos(box[0], box.size(), librevenge::RVNG_POINT);
MWAWGraphicEncoder graphicEncoder;
MWAWGraphicListener graphicListener(*m_parserState, box, &graphicEncoder);
graphicListener.startDocument();
textPos.m_anchorTo=MWAWPosition::Page;
textPos.m_wrapping=pos.m_wrapping;
graphicListener.insertTextBox(textPos, subdoc, style);
graphicListener.endDocument();
MWAWEmbeddedObject picture;
if (graphicEncoder.getBinaryResult(picture))
listener->insertPicture(pictPos, picture);
return;
}
case MsWksGraphInternal::Zone::TableZone: {
auto &table = static_cast<MsWksGraphInternal::Table &>(*zone);
if (isDraw) {
listener->openFrame(pictPos, zone->m_style);
m_tableParser->sendTable(table.m_tableId);
listener->closeFrame();
return;
}
std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
(new MsWksGraphInternal::SubDocument(*this, input, MsWksGraphInternal::SubDocument::Table, table.m_tableId));
listener->insertTextBox(pictPos, subdoc, zone->m_style);
return;
}
case MsWksGraphInternal::Zone::ChartZone: {
auto &chart = static_cast<MsWksGraphInternal::Chart &>(*zone);
std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
(new MsWksGraphInternal::SubDocument(*this, input, MsWksGraphInternal::SubDocument::Chart, chart.m_chartId));
listener->insertTextBox(pictPos, subdoc, zone->m_style);
return;
}
case MsWksGraphInternal::Zone::Group:
sendGroup(id, pictPos);
return;
case MsWksGraphInternal::Zone::Bitmap: {
auto &bmap = static_cast<MsWksGraphInternal::DataBitmap &>(*zone);
MWAWEmbeddedObject picture;
if (!bmap.getPictureData(input, picture,m_document.getPalette(4)))
break;
m_document.ascii().skipZone(bmap.m_dataPos, bmap.m_pos.end()-1);
listener->insertPicture(pictPos, picture, zone->m_style);
return;
}
case MsWksGraphInternal::Zone::Shape: {
auto &shape = static_cast<MsWksGraphInternal::BasicShape &>(*zone);
listener->insertShape(pictPos, shape.m_shape, shape.getStyle());
return;
}
case MsWksGraphInternal::Zone::Pict: {
MWAWEmbeddedObject picture;
if (!zone->getBinaryData(input, picture))
break;
listener->insertPicture(pictPos, picture, zone->m_style);
return;
}
case MsWksGraphInternal::Zone::Textv4: {
auto &textbox = static_cast<MsWksGraphInternal::TextBoxv4 &>(*zone);
std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
(new MsWksGraphInternal::SubDocument(*this, input, MsWksGraphInternal::SubDocument::TextBoxv4, textbox.m_text, textbox.m_frame));
MWAWGraphicStyle style;
zone->fillFrame(style);
// a textbox can not have a border
style.m_lineWidth=0;
if (zone->m_ids[1] > 0) {
librevenge::RVNGString fName;
fName.sprintf("Frame%ld", zone->m_ids[0]);
style.m_frameName=fName.cstr();
}
if (zone->m_ids[2] > 0) {
librevenge::RVNGString fName;
fName.sprintf("Frame%ld", zone->m_ids[2]);
style.m_frameNextName=fName.cstr();
}
listener->insertTextBox(pictPos, subdoc, style);
return;
}
case MsWksGraphInternal::Zone::OLE: {
auto &ole = static_cast<MsWksGraphInternal::OLEZone &>(*zone);
m_document.sendOLE(ole.m_oleId, pictPos, zone->m_style);
return;
}
case MsWksGraphInternal::Zone::Unknown:
#if !defined(__clang__)
default:
#endif
break;
}
MWAW_DEBUG_MSG(("MsWksGraph::send: can not send zone %d\n", id));
}
void MsWksGraph::sendAll(int zoneId, bool mainZone)
{
MWAWPosition undefPos;
for (size_t i = 0; i < m_state->m_zonesList.size(); i++) {
auto zone = m_state->m_zonesList[i];
if (zoneId >= 0 && zoneId!=zone->m_zoneId)
continue;
if (zone->m_doNotSend || (zone->m_isSent && mainZone))
continue;
undefPos.m_anchorTo = mainZone ? MWAWPosition::Page : MWAWPosition::Paragraph;
send(int(i), undefPos);
}
}
void MsWksGraph::sendObjects(MsWksGraph::SendData const &what)
{
MWAWListenerPtr listener=m_parserState->getMainListener();
if (!listener) {
MWAW_DEBUG_MSG(("MsWksGraph::sendObjects: listener is not set\n"));
return;
}
bool first = true;
auto numZones = int(m_state->m_zonesList.size());
std::vector<int> listIds;
MsWksGraphInternal::RBZone *rbZone=nullptr;
switch (what.m_type) {
case MsWksGraph::SendData::ALL:
listIds.resize(size_t(numZones));
for (int i = 0; i < numZones; i++) listIds[size_t(i)]=i;
break;
case MsWksGraph::SendData::RBDR:
case MsWksGraph::SendData::RBIL: {
int zId = what.m_type==MsWksGraph::SendData::RBDR ? -1 : what.m_id;
if (m_state->m_RBsMap.find(zId)!=m_state->m_RBsMap.end())
rbZone = &m_state->m_RBsMap.find(zId)->second;
break;
}
#if !defined(__clang__)
default:
break;
#endif
}
if (rbZone)
listIds=rbZone->m_idList;
bool isText=m_parserState->m_type==MWAWParserState::Text;
if (isText && what.m_type==MsWksGraph::SendData::RBIL) {
if (!rbZone) {
MWAW_DEBUG_MSG(("MsWksGraph::sendObjects: can find RBIL zone %d\n", what.m_id));
return;
}
if (listIds.size() != 1) {
if (what.m_anchor == MWAWPosition::Char ||
what.m_anchor == MWAWPosition::CharBaseLine) {
std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
(new MsWksGraphInternal::SubDocument(*this, m_document.getInput(), MsWksGraphInternal::SubDocument::RBILZone, what.m_id));
MWAWPosition pictPos(MWAWVec2f(0,0), MWAWVec2f(what.m_size), librevenge::RVNG_POINT);
pictPos.setRelativePosition(MWAWPosition::Char,
MWAWPosition::XLeft, MWAWPosition::YTop);
pictPos.m_wrapping = MWAWPosition::WBackground;
listener->insertTextBox(pictPos, subdoc);
return;
}
}
}
MWAWPosition undefPos;
undefPos.m_anchorTo = what.m_anchor;
for (auto id : listIds) {
if (id < 0 || id >= numZones) continue;
auto zone = m_state->m_zonesList[size_t(id)];
if (!zone || zone->m_doNotSend) continue;
if (zone->m_isSent) {
if (what.m_type == MsWksGraph::SendData::ALL ||
(isText && what.m_anchor == MWAWPosition::Page)) continue;
}
if (what.m_anchor == MWAWPosition::Page) {
if (what.m_page > 0 && zone->m_page+1 != what.m_page) continue;
else if (what.m_page==0 && zone->m_page < 0) continue;
else if (what.m_page==-2 && zone->m_page >=0) continue;
undefPos=zone->getPosition(MWAWPosition::Page);
}
if (isText && first) {
first = false;
if (what.m_anchor == MWAWPosition::Page && !listener->isSectionOpened() && !listener->isParagraphOpened())
listener->insertChar(' ');
}
send(int(id), undefPos);
}
}
void MsWksGraph::flushExtra()
{
MWAWPosition undefPos;
undefPos.m_anchorTo=MWAWPosition::Char;
for (size_t i = 0; i < m_state->m_zonesList.size(); i++) {
auto zone = m_state->m_zonesList[i];
if (!zone || zone->m_isSent || zone->m_doNotSend) continue;
send(int(i), undefPos);
}
}
////////////////////////////////////////////////////////////
// style
////////////////////////////////////////////////////////////
MsWksGraph::Style::~Style()
{
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: