/* -*- 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 "MWAWFontConverter.hxx"
#include "MWAWGraphicListener.hxx"
#include "MWAWGraphicShape.hxx"
#include "MWAWGraphicStyle.hxx"
#include "MWAWHeader.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPictBitmap.hxx"
#include "MWAWPictData.hxx"
#include "MWAWPrinter.hxx"
#include "MWAWPosition.hxx"
#include "MWAWRSRCParser.hxx"
#include "MWAWSubDocument.hxx"
#include "MacDrawProStyleManager.hxx"
#include "MacDrawProParser.hxx"
/** Internal: the structures of a MacDrawProParser */
namespace MacDrawProParserInternal
{
// generic class used to defined a layer
struct Layer {
//! constructor
Layer()
: m_numShapes(0)
, m_firstShape(-1)
, m_isHidden(false)
, m_box()
, m_libraryToObjectMap()
, m_name("")
{
for (auto &n : m_N) n=0;
}
//! the number of shape
int m_numShapes;
//! the first shape
int m_firstShape;
//! true if the layer is hidden
bool m_isHidden;
//! the layer bounding box (if computed)
MWAWBox2f m_box;
//! map library to pos
std::map<int, int> m_libraryToObjectMap;
//! some unknown number find in the beginning of the header
long m_N[3];
//! the layer name
librevenge::RVNGString m_name;
};
// generic class used to defined a library
struct Library {
//! constructor
explicit Library(int id=-1)
: m_id(id)
, m_layerList()
, m_box()
, m_name("")
{
}
//! the library id
int m_id;
//! the list of layer id
std::vector<int> m_layerList;
//! the bounding box (if computed)
MWAWBox2f m_box;
//! the library name
librevenge::RVNGString m_name;
};
// generic class used to store shape in MWAWDrawParser
struct Shape {
//! the different shape
enum Type { Basic, Bitmap, Group, GroupEnd, Note, Text, Unknown };
//! constructor
Shape()
: m_type(Unknown)
, m_fileType(0)
, m_box()
, m_style()
, m_shape()
, m_id(-1)
, m_nextId(-1)
, m_flags(0)
, m_textZoneId(-1)
, m_numChars(0)
, m_fontMap()
, m_lineBreakSet()
, m_paragraphMap()
, m_paragraph()
, m_childList()
, m_measureBox()
, m_measureEntry()
, m_numBytesByRow(0)
, m_bitmapIsColor(false)
, m_bitmapDim()
, m_bitmapFileDim()
, m_bitmapEntry()
, m_bitmapClutId(0)
, m_isSent(false)
{
}
//! basic operator<<
friend std::ostream &operator<<(std::ostream &o, Shape const &shape);
//! return the shape bdbox
MWAWBox2f getBdBox() const
{
if (m_type==Basic)
return m_shape.getBdBox();
if (m_style.m_rotate>=0 && m_style.m_rotate<=0) return m_box;
return libmwaw::rotateBoxFromCenter(m_box,m_style.m_rotate);
}
//! returns true if the object is a line
bool isLine() const
{
return m_type==Basic && m_shape.m_type==MWAWGraphicShape::Line;
}
//! the graphic type
Type m_type;
//! the file type
int m_fileType;
//! the shape bdbox
MWAWBox2f m_box;
//! the graphic style
MWAWGraphicStyle m_style;
//! the graphic shape ( for basic geometric form )
MWAWGraphicShape m_shape;
//! the shape id
int m_id;
//! the following id (if set)
int m_nextId;
//! the main shape flag
int m_flags;
// text box or note
//! the text zone ( for a text box or a note)
int m_textZoneId;
//! the number of caracters ( for a text box or a note)
int m_numChars;
//! a map position to font id ( for a text box or a note)
std::map<int,int> m_fontMap;
//! the list of line break position ( for a text box or a note)
std::set<int> m_lineBreakSet;
//! the paragraphMap ( for a text box or a note) and a Pro file
std::map<int,int> m_paragraphMap;
//! the paragraph ( for a text box or a note) and a II file
MWAWParagraph m_paragraph;
// group
//! the child list ( for a group )
std::vector<size_t> m_childList;
// line
// the measure box ( for a line)
MWAWBox2f m_measureBox;
// the measure message ( for a line )
MWAWEntry m_measureEntry;
// bitmap
//! the number of bytes by row (for a bitmap)
int m_numBytesByRow;
//! true if the bitmap is a color bitmap
bool m_bitmapIsColor;
//! the bitmap dimension (in page)
MWAWBox2i m_bitmapDim;
//! the bitmap dimension (in the file)
MWAWBox2i m_bitmapFileDim;
//! the bitmap entry (data)
MWAWEntry m_bitmapEntry;
//! the bitmap clut rsrc id
int m_bitmapClutId;
//! a flag used to know if the object is sent to the listener or not
mutable bool m_isSent;
};
std::ostream &operator<<(std::ostream &o, Shape const &shape)
{
o << "O" << shape.m_id << "[";
switch (shape.m_type) {
case Shape::Basic:
switch (shape.m_shape.m_type) {
case MWAWGraphicShape::Line:
o << "line,";
break;
case MWAWGraphicShape::Measure:
o << "measure,";
break;
case MWAWGraphicShape::Rectangle:
o << "rect,";
break;
case MWAWGraphicShape::Circle:
o << "circle,";
break;
case MWAWGraphicShape::Arc:
o << "arc,";
break;
case MWAWGraphicShape::Pie:
o << "pie,";
break;
case MWAWGraphicShape::Polygon:
o << "polygon,";
break;
case MWAWGraphicShape::Polyline:
o << "polyline,";
break;
case MWAWGraphicShape::Path:
o << "spline,";
break;
case MWAWGraphicShape::ShapeUnknown:
#if !defined(__clang__)
default:
#endif
o << "###unknown[shape],";
break;
}
break;
case Shape::Bitmap:
o << "bitmap,";
break;
case Shape::Group:
o << "group,";
break;
case Shape::GroupEnd:
o << "group[end],";
break;
case Shape::Note:
o << "note,";
break;
case Shape::Text:
o << "text,";
break;
case Shape::Unknown:
#if !defined(__clang__)
default:
#endif
o << "unknown,";
break;
}
o << shape.m_box << ",";
if (shape.m_flags & 0x80) o << "rotation,";
if (shape.m_flags & 0x3f)
o << "fl=" << std::hex << (shape.m_flags&0x3f) << std::dec << ",";
o << "],";
return o;
}
////////////////////////////////////////
//! Internal: the state of a MacDrawProParser
struct State {
//! constructor
State()
: m_version(0)
, m_isStationery(false)
, m_numPages(1)
, m_actualLayer(1)
, m_numLayers(1)
, m_numHiddenLayers(0)
, m_numVisibleLayers(0)
, m_createMasterPage(false)
, m_sendAsLibraries(false)
, m_numLibraries(0)
, m_numShapes(0)
, m_libraryList()
, m_layerList()
, m_objectDataList()
, m_objectTextList()
, m_shapeList()
{
for (auto &size : m_sizeStyleZones) size=0;
for (auto &size : m_sizeLayerZones) size=0;
for (auto &size : m_sizeLibraryZones) size=0;
for (auto &size : m_sizeFZones) size=0;
}
//! the file version
int m_version;
//! flag to know if the file is a stationery document
bool m_isStationery;
//! the final number of pages
int m_numPages;
//! the actual layer
int m_actualLayer;
//! the number of layer
int m_numLayers;
//! the number of hidden layer
int m_numHiddenLayers;
//! the number of visible layer
int m_numVisibleLayers;
//! flag to know if we need or not to create a master
bool m_createMasterPage;
//! flag to know if we create a page by library or not
bool m_sendAsLibraries;
//! the number of library
int m_numLibraries;
//! the total number of shapes
int m_numShapes;
//! the size of the header zones
long m_sizeStyleZones[6];
//! the size of the layer zones
long m_sizeLayerZones[2];
//! the size of the library zones(checkme)
long m_sizeLibraryZones[2];
//! the size of the zoneF
long m_sizeFZones[4];
//! the library list
std::vector<Library> m_libraryList;
//! the layer list
std::vector<Layer> m_layerList;
//! the list of entries which stores the object's data
std::vector<MWAWEntry> m_objectDataList;
//! the list of entries which stores the object's text
std::vector<MWAWEntry> m_objectTextList;
//! the shape list
std::vector<Shape> m_shapeList;
};
////////////////////////////////////////
//! Internal: the subdocument of a MacDrawProParser
class SubDocument final : public MWAWSubDocument
{
public:
//! constructor given an zone id
SubDocument(MacDrawProParser &pars, MWAWInputStreamPtr const &input, int zoneId)
: MWAWSubDocument(&pars, input, MWAWEntry())
, m_id(zoneId)
, m_measureEntry()
{
}
//! constructor given a measure entry
SubDocument(MacDrawProParser &pars, MWAWInputStreamPtr const &input, MWAWEntry const &measureEntry)
: MWAWSubDocument(&pars, input, MWAWEntry())
, m_id(-1)
, m_measureEntry(measureEntry)
{
}
//! destructor
~SubDocument() final {}
//! operator!=
bool operator!=(MWAWSubDocument const &doc) const final
{
if (MWAWSubDocument::operator!=(doc)) return true;
auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
if (!sDoc) return true;
if (m_id != sDoc->m_id) return true;
if (m_measureEntry != sDoc->m_measureEntry) return true;
return false;
}
//! the parser function
void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
protected:
//! the subdocument id
int m_id;
//! the measure entry
MWAWEntry m_measureEntry;
private:
SubDocument(SubDocument const &orig) = delete;
SubDocument &operator=(SubDocument const &orig) = delete;
};
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType)
{
if (!listener || !listener->canWriteText()) {
MWAW_DEBUG_MSG(("MacDrawProParserInternal::SubDocument::parse: no listener\n"));
return;
}
auto *parser=dynamic_cast<MacDrawProParser *>(m_parser);
if (!parser) {
MWAW_DEBUG_MSG(("MacDrawProParserInternal::SubDocument::parse: no parser\n"));
return;
}
long pos = m_input->tell();
if (m_id >= 0)
parser->sendText(m_id);
else
parser->sendMeasure(m_measureEntry);
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
MacDrawProParser::MacDrawProParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
: MWAWGraphicParser(input, rsrcParser, header)
, m_state()
, m_styleManager()
{
init();
}
MacDrawProParser::~MacDrawProParser()
{
}
void MacDrawProParser::init()
{
resetGraphicListener();
setAsciiName("main-1");
m_state.reset(new MacDrawProParserInternal::State);
m_styleManager.reset(new MacDrawProStyleManager(*this));
getPageSpan().setMargins(0.1);
}
////////////////////////////////////////////////////////////
// the parser
////////////////////////////////////////////////////////////
void MacDrawProParser::parse(librevenge::RVNGDrawingInterface *docInterface)
{
if (!getInput().get() || !checkHeader(nullptr)) throw(libmwaw::ParseException());
bool ok = false;
try {
// create the asciiFile
ascii().setStream(getInput());
ascii().open(asciiName());
checkHeader(nullptr);
ok = createZones();
if (ok) {
createDocument(docInterface);
sendMasterPage();
for (int i=0; i<m_state->m_numPages; ++i)
sendPage(i);
#ifdef DEBUG
flushExtra();
#endif
}
ascii().reset();
}
catch (...) {
MWAW_DEBUG_MSG(("MacDrawProParser::parse: exception catched when parsing\n"));
ok = false;
}
resetGraphicListener();
if (!ok) throw(libmwaw::ParseException());
}
////////////////////////////////////////////////////////////
// create the document
////////////////////////////////////////////////////////////
void MacDrawProParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface)
{
if (!documentInterface) return;
if (getGraphicListener()) {
MWAW_DEBUG_MSG(("MacDrawProParser::createDocument: listener already exist\n"));
return;
}
// we need one page for the master page: one page by hidden layers
int numPages=m_state->m_sendAsLibraries ? static_cast<int>(m_state->m_libraryList.size()) :
m_state->m_numHiddenLayers;
if (numPages<=0) numPages=1;
m_state->m_numPages = numPages;
// create the list of page names
std::vector<librevenge::RVNGString> namesList;
if (m_state->m_sendAsLibraries) {
for (auto const &library : m_state->m_libraryList)
namesList.push_back(library.m_name);
}
else {
for (auto const &layer : m_state->m_layerList) {
if (!layer.m_isHidden) continue;
namesList.push_back(layer.m_name);
}
}
if (static_cast<int>(namesList.size()) < numPages)
namesList.resize(size_t(numPages));
// create the page list
MWAWPageSpan ps(getPageSpan());
m_state->m_createMasterPage=!m_state->m_sendAsLibraries &&
m_state->m_numHiddenLayers>1 && m_state->m_numVisibleLayers>0;
if (m_state->m_createMasterPage)
ps.setMasterPageName(librevenge::RVNGString("Master"));
std::vector<MWAWPageSpan> pageList;
int actUnamedPage=0;
for (auto const &name : namesList) {
if (name.empty()) {
++actUnamedPage;
continue;
}
if (actUnamedPage) {
ps.setPageSpan(actUnamedPage);
pageList.push_back(ps);
actUnamedPage=0;
}
MWAWPageSpan psNamed(ps);
psNamed.setPageName(name);
psNamed.setPageSpan(1);
pageList.push_back(psNamed);
}
if (actUnamedPage) {
ps.setPageSpan(actUnamedPage);
pageList.push_back(ps);
}
MWAWGraphicListenerPtr listen(new MWAWGraphicListener(*getParserState(), pageList, documentInterface));
setGraphicListener(listen);
listen->startDocument();
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool MacDrawProParser::createZones()
{
MWAWInputStreamPtr input = getInput();
int const vers=version();
if (getRSRCParser()) m_styleManager->readRSRCZones();
readHeaderInfo();
input->seek(vers==0 ? 0x1f4 : 0x1d4,librevenge::RVNG_SEEK_SET);
if (!m_styleManager->readStyles(m_state->m_sizeStyleZones) ||
!readLayersInfo() || !readLayerLibraryCorrespondance() || !readLibrariesInfo() ||
!findObjectPositions(true) || !findObjectPositions(false)) {
MWAW_DEBUG_MSG(("MacDrawProParser::createZones: something is bad, stop.\n"));
return false;
}
long pos;
libmwaw::DebugStream f;
if (m_state->m_sizeFZones[3]) {
/* checkme: I am not sure if this zone contains data or if it is
only an intermediary zone used to make possible to grow the
previous zones */
pos=input->tell();
long endPos=pos+m_state->m_sizeFZones[3];
f.str("");
f << "Entries(Free0):";
if (m_state->m_sizeFZones[3]<0 || !input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("MacDrawProParser::createZones: can not read Free0 size\n"));
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return false;
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
}
pos=input->tell();
if (m_state->m_numShapes<0 || !input->checkPosition(pos+32L*m_state->m_numShapes)) {
MWAW_DEBUG_MSG(("MacDrawProParser::createZones: can not read the object information\n"));
ascii().addPos(pos);
ascii().addNote("Entries(Object):###");
return false;
}
for (int i=0; i<m_state->m_numShapes; ++i) {
if (readObject()<0) break;
}
// probably a free zone (used to reserve space to make the file grow)
if (!input->isEnd()) {
pos=input->tell();
ascii().addPos(pos);
ascii().addNote("Entries(Free1):");
}
int n=0;
for (auto const &entry : m_state->m_objectDataList) {
++n;
if (!entry.valid() || entry.isParsed())
continue;
MWAW_DEBUG_MSG(("MacDrawProParser::createZones: find some unparsed data's object zones\n"));
f.str("");
f << "Entries(ObjData)[" << n-1 << "]:###unparsed";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
}
return computeLayersAndLibrariesBoundingBox();
}
////////////////////////////////////////////////////////////
// layer/library functions
////////////////////////////////////////////////////////////
bool MacDrawProParser::readLayersInfo()
{
int const vers=version();
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
long begNamePos=pos+m_state->m_sizeLayerZones[0];
long endPos=pos+m_state->m_sizeLayerZones[0]+m_state->m_sizeLayerZones[1];
if (input->isEnd() || m_state->m_numLayers*0x80>m_state->m_sizeLayerZones[0] || !input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayersInfo: problem with the layer dimensions\n"));
ascii().addPos(pos);
ascii().addNote("Entries(Layer):###");
return false;
}
libmwaw::DebugStream f;
int numShapes=0;
std::map<int,long> idToNamePosMap;
m_state->m_layerList.clear();
for (int i=0; i<m_state->m_numLayers; ++i) {
MacDrawProParserInternal::Layer layer;
layer.m_firstShape=numShapes;
if (i && m_state->m_actualLayer==i+1)
layer.m_isHidden=true;
pos=input->tell();
f.str("");
f << "Entries(Layer)[L" << i+1 << "]:";
auto val=long(input->readULong(2));
if (val&0x8000) {
layer.m_isHidden=true;
f << "hidden,";
}
val &=0x7FFF;
if (val) f << "fl=" << std::hex << val << std::dec << ",";
f << "id?=" << input->readULong(2) << ","; // a small number
auto delta=long(input->readULong(4));
if (delta) f << "name[pos]=" << std::hex << delta << std::dec << ",";
if (delta < 0 || delta >=m_state->m_sizeLayerZones[1]) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayersInfo: problem with the layer position dim\n"));
f << "###";
delta=-1;
}
idToNamePosMap[i]=delta;
layer.m_numShapes=static_cast<int>(input->readULong(4));
if (layer.m_numShapes < 0 || layer.m_numShapes > std::numeric_limits<int>::max() - numShapes) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayersInfo: found an absurd number of shapes\n"));
layer.m_numShapes = 0;
}
numShapes+=layer.m_numShapes;
if (layer.m_numShapes) f << "N[shapes]=" << layer.m_numShapes << ",";
m_state->m_layerList.push_back(layer);
val=long(input->readLong(4)); // always 0?
if (val) f << "f0=" << val << ",";
f << "N[unkn]=[";
for (auto &n : layer.m_N) { // small numbers always less than numShapes. Note: f2 can be equal to -1
n=long(input->readLong(4));
if (!n) {
f << "_,";
continue;
}
if (n>layer.m_numShapes) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayersInfo: find an old number in header\n"));
f << "#";
}
if (n==-1)
f << "*,";
else
f << n << "/" << layer.m_numShapes << ",";
}
f << "],";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
for (int j=0; j<2; ++j) {
/* very often Layer-A0=Layer-A1.
When they differ, Layer-A1 seems to contain more data than Layer-A0
*/
pos=input->tell();
f.str("");
f << "Layer-A" << j << "[L" << i+1 << "]:";
f << "N[unkn]=[";
for (int k=0; k<3; ++k) { // small numbers always less than numShapes, but they can be greater than m_N[k]
val=long(input->readLong(4));
if (!val) {
f << "_,";
continue;
}
if (val > layer.m_numShapes) f << "#";
f << val << "/" << layer.m_numShapes << ",";
}
f << "],";
for (int k=0; k<2; ++k) {
float dim[4];
for (auto &d : dim) d=float(input->readLong(4))/65536.f;
if (dim[0]>0||dim[1]>0||dim[2]>0||dim[3]>0)
f << "dim" << k << "=" << MWAWBox2f(MWAWVec2f(dim[0],dim[1]),MWAWVec2f(dim[2],dim[3])) << ",";
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
pos=input->tell();
f.str("");
f << "Layer-B[L" << i+1 << "]:";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(pos+(vers==0 ? 12 : 14), librevenge::RVNG_SEEK_SET);
}
m_state->m_numShapes=numShapes;
for (auto it : idToNamePosMap) {
if (it.second < 0) continue;
pos=begNamePos+it.second;
input->seek(pos, librevenge::RVNG_SEEK_SET);
f.str("");
f << "Layer[L" << it.first+1 << ",name]:";
auto fSz=static_cast<int>(input->readULong(1));
if (input->tell()+fSz>endPos) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayersInfo: oops the layer name size seems bad\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
continue;
}
librevenge::RVNGString name("");
for (int c=0; c<fSz; ++c) {
auto ch=char(input->readULong(1));
if (!ch) continue;
f << ch;
int unicode= getParserState()->m_fontConverter->unicode(3, static_cast<unsigned char>(ch));
if (unicode==-1)
name.append(ch);
else
libmwaw::appendUnicode(static_cast<uint32_t>(unicode), name);
}
f << ",";
m_state->m_layerList[size_t(it.first)].m_name=name;
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
bool MacDrawProParser::readLayerLibraryCorrespondance()
{
if (!m_state->m_sizeFZones[0]) return true;
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
MWAWEntry entry;
entry.setBegin(pos);
entry.setLength(m_state->m_sizeFZones[0]);
entry.setName("LayToLib");
if (entry.length()<0 || !input->checkPosition(entry.end())) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayerLibraryCorrespondance: the zone size seems bad\n"));
ascii().addPos(pos);
ascii().addNote("Entries(LayToLib):###");
return false;
}
std::map<int, long> idToDecal;
if (!readStructuredHeaderZone(entry, idToDecal)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayerLibraryCorrespondance: can not read the header\n"));
ascii().addPos(pos);
ascii().addNote("Entries(LayToLib):###");
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return true;
}
pos=input->tell();
libmwaw::DebugStream f;
f << "LayToLib[data]:";
auto sz=long(input->readULong(4));
if (sz<4 || pos+sz>entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayerLibraryCorrespondance: can not read the data size\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return true;
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
MacDrawProParserInternal::Layer falseLayer;
int actId=0;
for (auto it : idToDecal) {
long decal=it.second;
if (decal<0 || decal+8>sz) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayerLibraryCorrespondance: the zone %d's decal seems bad\n", it.first));
continue;
}
int id=actId++;
f.str("");
f << "LayToLib-L" << id+1 << ":";
if (id<0 || id>=static_cast<int>(m_state->m_layerList.size())) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayerLibraryCorrespondance: the layer's number:%d seems bad\n", id));
}
MacDrawProParserInternal::Layer &layer=(id>=0 && id<static_cast<int>(m_state->m_layerList.size())) ?
m_state->m_layerList[size_t(id)] : falseLayer;
long begPos=pos+decal;
input->seek(begPos, librevenge::RVNG_SEEK_SET);
auto val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(2)); // 1|3
if (val!=1) f << "type=" << val << ",";
auto dataSz=long(input->readULong(4));
if (dataSz<8 || begPos+dataSz>entry.end() || (dataSz%8)!=0) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayerLibraryCorrespondance: the zone %d data size seems bad\n", id));
f << "###";
ascii().addPos(begPos);
ascii().addNote(f.str().c_str());
continue;
}
auto N=int((dataSz-8)/8);
f << "libs=[";
for (int i=0; i<N; ++i) {
auto obj=static_cast<int>(input->readLong(4)); // find 1 or 2
auto val1=static_cast<int>(input->readLong(2));
auto library=static_cast<int>(input->readLong(2));
if (library<1 || library>m_state->m_numLibraries) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayerLibraryCorrespondance: a library number seems bad\n"));
f << "###";
}
else {
if (library>int(m_state->m_libraryList.size()))
m_state->m_libraryList.resize(size_t(library));
m_state->m_libraryList[size_t(library-1)].m_id=library;
m_state->m_libraryList[size_t(library-1)].m_layerList.push_back(id);
layer.m_libraryToObjectMap[library]=obj;
}
f << "Li" << library;
if (obj>1) f << ":O" << obj-1;
if (val1) f << ":" << val1;
f << ",";
}
f << "],";
ascii().addPos(begPos+dataSz);
ascii().addNote("_");
ascii().addPos(begPos);
ascii().addNote(f.str().c_str());
}
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return true;
}
bool MacDrawProParser::readLibrariesInfo()
{
if (!m_state->m_sizeLibraryZones[0] && !m_state->m_sizeLibraryZones[1])
return true;
int const vers=version();
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
long begNamePos=pos+m_state->m_sizeLibraryZones[0];
long endPos=pos+m_state->m_sizeLibraryZones[0]+m_state->m_sizeLibraryZones[1];
if (!input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLibrariesInfo: problem with the library dimensions\n"));
ascii().addPos(pos);
ascii().addNote("Entries(Library):###");
return false;
}
int const fieldSize=vers==0 ? 8 : 10;
if ((m_state->m_sizeLibraryZones[0]%fieldSize) || !m_state->m_sizeLibraryZones[0]||!m_state->m_sizeLibraryZones[1]) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLibrariesInfo: problem with the size zone(II)\n"));
ascii().addPos(pos);
ascii().addNote("Entries(Library):###");
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
libmwaw::DebugStream f;
f << "Entries(Library):";
std::vector<long> posList;
auto N=int(m_state->m_sizeLibraryZones[0]/fieldSize);
for (int i=0; i<N; ++i) {
auto cPos=long(input->readULong(4));
if (cPos<0 || cPos>m_state->m_sizeLibraryZones[1]) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayersInfo: oops the library name positions seems bad\n"));
f << "###";
posList.push_back(-1);
}
else
posList.push_back(cPos);
f << std::hex << cPos << std::dec;
auto val=static_cast<int>(input->readLong(4));
if (val) f << ":" << val;
f << ",";
if (vers>0) {
val=static_cast<int>(input->readLong(2));
if (val!=i+1) f << "id=" << val << ",";
}
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
for (size_t i=0; i<posList.size(); ++i) {
if (posList[i]<0) continue;
pos=begNamePos+posList[i];
f.str("");
f << "Library-name[" << i << "]:";
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto fSz=static_cast<int>(input->readULong(1));
if (input->tell()+fSz>endPos) {
MWAW_DEBUG_MSG(("MacDrawProParser::readLayersInfo: oops the library name size seems bad\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
continue;
}
librevenge::RVNGString name("");
for (int c=0; c<fSz; ++c) {
auto ch=char(input->readULong(1));
if (!ch) continue;
f << ch;
int unicode= getParserState()->m_fontConverter->unicode(3, static_cast<unsigned char>(ch));
if (unicode==-1)
name.append(ch);
else
libmwaw::appendUnicode(static_cast<uint32_t>(unicode), name);
}
f << ",";
if (int(i) < m_state->m_numLibraries) {
if (i>=m_state->m_libraryList.size()) {
m_state->m_libraryList.resize(i+1);
m_state->m_libraryList[i].m_id=int(i)+1;
}
m_state->m_libraryList[i].m_name=name;
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// generic function
////////////////////////////////////////////////////////////
bool MacDrawProParser::readStructuredHeaderZone(MWAWEntry const &entry, std::map<int, long> &idToDeltaPosMap)
{
idToDeltaPosMap.clear();
if (!entry.length())
return true;
MWAWInputStreamPtr input = getInput();
long pos=entry.begin();
libmwaw::DebugStream f;
f << "Entries(" << entry.name() << "):";
if (entry.length()<4+4 || !input->checkPosition(entry.end())) {
MWAW_DEBUG_MSG(("MacDrawProParser::readStructuredHeaderZone: can not read %s size\n", entry.name().c_str()));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return false;
}
input->seek(pos,librevenge::RVNG_SEEK_SET);
auto sz=long(input->readULong(4));
long endPos=pos+sz;
if (sz<8 || endPos>entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readStructuredHeaderZone: can not read %s-ptr size\n", entry.name().c_str()));
f << "ptr###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return true;
}
long fFree=static_cast<int>(input->readULong(4));
f << "fFree=" << fFree << ",";
if (fFree!=sz)
ascii().addDelimiter(pos+fFree,'|');
auto numDatas=int((sz-8)/4);
f << "ptrs=[";
for (int i=0; i<numDatas; ++i) {
auto ptr=long(input->readULong(4));
if (!ptr) continue;
idToDeltaPosMap[i]=ptr;
f << std::hex << ptr << std::dec << ":" << i << ",";
}
f << "],";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// object functions
////////////////////////////////////////////////////////////
bool MacDrawProParser::findObjectPositions(bool dataZone)
{
if (!m_state->m_sizeFZones[dataZone ? 1 : 2]) return true;
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
MWAWEntry entry;
entry.setBegin(pos);
entry.setLength(m_state->m_sizeFZones[dataZone ? 1 : 2]);
entry.setName(dataZone ? "ObjData" : "ObjText");
libmwaw::DebugStream f;
f << entry.name() << "[data]:";
if (entry.length()<0 || !input->checkPosition(entry.end())) {
MWAW_DEBUG_MSG(("MacDrawProParser::findObjectPositions: the zone size seems bad\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return false;
}
std::map<int, long> idToDecal;
if (!readStructuredHeaderZone(entry, idToDecal)) {
MWAW_DEBUG_MSG(("MacDrawProParser::findObjectPositions: can not read the header\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return true;
}
pos=input->tell();
auto sz=long(input->readULong(4));
if (sz<4 || pos+sz>entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::findObjectPositions: can not read the data size\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return true;
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
std::vector<MWAWEntry> &positionList=dataZone ? m_state->m_objectDataList : m_state->m_objectTextList;
positionList.clear();
for (auto it : idToDecal) {
int id=it.first;
long decal=it.second;
if (decal<4 || decal+8>sz) {
MWAW_DEBUG_MSG(("MacDrawProParser::findObjectPositions: the zone %d's decal seems bad\n", id));
continue;
}
f.str("");
f << entry.name() << "[" << id << "]:";
long begPos=pos+decal;
input->seek(begPos+4, librevenge::RVNG_SEEK_SET);
auto dataSz=long(input->readULong(4));
if (dataSz<8 || begPos+dataSz>entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::findObjectPositions: the zone %d data size seems bad\n", id));
f << "###";
ascii().addPos(begPos);
ascii().addNote(f.str().c_str());
continue;
}
MWAWEntry dEntry;
dEntry.setBegin(begPos);
dEntry.setLength(dataSz);
dEntry.setId(id);
if (id>=int(positionList.size()))
positionList.resize(size_t(id+1));
if (id>=0 && id < int(positionList.size()))
positionList[size_t(id)]=dEntry;
else {
MWAW_DEBUG_MSG(("MacDrawProParser::findObjectPositions: can not store entry %d\n", id));
f << "###";
ascii().addPos(begPos);
ascii().addNote(f.str().c_str());
}
ascii().addPos(dEntry.end());
ascii().addNote("_");
}
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return true;
}
bool MacDrawProParser::computeLayersAndLibrariesBoundingBox()
{
if (m_state->m_layerList.empty()) {
MWAW_DEBUG_MSG(("MacDrawProParser::computeLayersAndLibrariesBoundingBox: can not find any layer\n"));
return false;
}
int n=0;
for (auto &layer : m_state->m_layerList) {
++n;
if (layer.m_firstShape < 0 || layer.m_numShapes < 0 ||
(layer.m_numShapes && layer.m_firstShape >= int(m_state->m_shapeList.size()))) {
MWAW_DEBUG_MSG(("MacDrawProParser::computeLayersAndLibrariesBoundingBox: the layer %d seems bad\n", int(n-1)));
layer.m_numShapes=0;
layer.m_isHidden=false;
continue;
}
if (layer.m_isHidden)
++m_state->m_numHiddenLayers;
else
++m_state->m_numVisibleLayers;
MWAWBox2f box;
bool boxSet=false;
for (int j=layer.m_firstShape; j<layer.m_firstShape+layer.m_numShapes; ++j) {
if (j>=int(m_state->m_shapeList.size())) {
MWAW_DEBUG_MSG(("MacDrawProParser::computeLayersAndLibrariesBoundingBox: the layer %d seems to contain to much shapes\n", int(n-1)));
layer.m_numShapes=j-layer.m_firstShape;
break;
}
auto const &shape=m_state->m_shapeList[size_t(j)];
if (shape.m_type==MacDrawProParserInternal::Shape::Group ||
shape.m_type==MacDrawProParserInternal::Shape::GroupEnd ||
shape.m_type==MacDrawProParserInternal::Shape::Unknown)
continue;
if (!boxSet) {
box=shape.getBdBox();
boxSet=true;
}
else
box=box.getUnion(shape.getBdBox());
}
if (boxSet)
layer.m_box=box;
else if (layer.m_numShapes) {
MWAW_DEBUG_MSG(("MacDrawProParser::computeLayersAndLibrariesBoundingBox: can not compute the bdbox of the layer %d\n", int(n-1)));
}
}
size_t numLayers=m_state->m_layerList.size();
if (m_state->m_libraryList.empty()) {
// create a false library which contains all layer
MacDrawProParserInternal::Library library(1);
for (int i=0; i<int(numLayers); ++i)
library.m_layerList.push_back(i);
m_state->m_libraryList.push_back(library);
}
MWAWVec2f pageSize(float(72*getPageSpan().getFormWidth()), float(72*getPageSpan().getFormLength()));
MWAWVec2f leftTop(72.f*float(getPageSpan().getMarginLeft()),72.f*float(getPageSpan().getMarginTop()));
MWAWBox2f docBox;
bool docBoxSet=false;
int numLibraryWithGroup=0;
for (size_t i=0; i<m_state->m_libraryList.size(); ++i) {
auto &library=m_state->m_libraryList[i];
if (library.m_layerList.empty()) continue;
MWAWBox2f box;
bool boxSet=false;
for (auto &id : library.m_layerList) {
if (id<0 || id>=int(numLayers)) {
MWAW_DEBUG_MSG(("MacDrawProParser::computeLayersAndLibrariesBoundingBox: library %d contains bad layer\n", int(i)));
id=-1;
continue;
}
auto const &layer= m_state->m_layerList[size_t(id)];
if (layer.m_box.size()[0]<0 || layer.m_box.size()[1]<0)
continue;
MWAWBox2f newBox(layer.m_box);
if (layer.m_libraryToObjectMap.find(int(i+1))!=layer.m_libraryToObjectMap.end()) {
int objId=layer.m_firstShape+layer.m_libraryToObjectMap.find(int(i+1))->second;
if (objId<=0 || objId>int(m_state->m_shapeList.size())) {
MWAW_DEBUG_MSG(("MacDrawProParser::computeLayersAndLibrariesBoundingBox: can not find begin library group %d\n", int(objId)));
}
else {
auto const &shape=m_state->m_shapeList[size_t(objId-1)];
if (shape.m_type==MacDrawProParserInternal::Shape::Group) {
newBox=shape.m_box;
++numLibraryWithGroup;
}
// else seems to happen sometimes, maybe, this may be ok...
}
}
if (!boxSet) {
box=MWAWBox2f(newBox[0]+leftTop, newBox[1]+leftTop);
boxSet=true;
}
else
box=box.getUnion(MWAWBox2f(newBox[0]+leftTop, newBox[1]+leftTop));
}
if (boxSet)
library.m_box=box;
else if (m_state->m_numShapes>0) {
MWAW_DEBUG_MSG(("MacDrawProParser::computeLayersAndLibrariesBoundingBox: can not compute the bdbox of the library %d\n", int(i)));
}
// convert it to a multiple of page size
MWAWVec2f libRB=library.m_box[1];
for (int c=0; c<2; ++c) {
if (pageSize[c]<=0) continue;
if (libRB[c]<=0)
libRB[c]=pageSize[c];
else {
auto numPage=int(libRB[c]/pageSize[c]-0.01f);
if (numPage<0) numPage=0;
libRB[c]=float(numPage+1)*pageSize[c];
}
}
library.m_box.setMax(libRB);
if (!docBoxSet) {
docBox=library.m_box;
docBoxSet=true;
}
else
docBox=docBox.getUnion(library.m_box);
}
if (docBoxSet) {
getPageSpan().setFormWidth(double(docBox[1][0])/72);
getPageSpan().setFormLength(double(docBox[1][1])/72);
}
if (m_state->m_isStationery && numLayers==1 && numLibraryWithGroup>1 && version()>0 &&
numLibraryWithGroup==static_cast<int>(m_state->m_libraryList.size()))
m_state->m_sendAsLibraries=true;
return true;
}
int MacDrawProParser::readObject()
{
int const vers=version();
MWAWInputStreamPtr input = getInput();
if (input->isEnd()) return -1;
if (m_state->m_numShapes <= int(m_state->m_shapeList.size()))
return false;
long pos=input->tell();
libmwaw::DebugStream f;
size_t shapeId= m_state->m_shapeList.size();
f << "Entries(Object)[O" << shapeId << "]:";
int const expectedSize=vers==0 ? 32 : 34;
if (!input->checkPosition(pos+expectedSize)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: the zone seems to small\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return -1;
}
m_state->m_shapeList.push_back(MacDrawProParserInternal::Shape());
auto &shape=m_state->m_shapeList.back();
shape.m_id=int(shapeId);
shape.m_nextId=shape.m_id+1; // default value
float dim[4];
for (auto &d : dim) d=float(input->readLong(4))/65536.f;
shape.m_box=MWAWBox2f(MWAWVec2f(dim[1],dim[0]),MWAWVec2f(dim[3],dim[2]));
f << shape.m_box << ",";
auto flags=static_cast<int>(input->readULong(1));
shape.m_flags=flags;
if (flags & 0x8) f << "select,";
if (flags & 0x20) f << "locked,";
bool hasData=(flags & 0x40);
if (flags & 0x80) f << "rotation,";
flags &=0x17;
if (flags) f << "fl0=" << std::hex << flags << std::dec << ",";
auto val=static_cast<int>(input->readULong(1));
// checkme
if (val&0x20) {
shape.m_style.m_flip[0]=true;
f << "flipX,";
}
if (val&0x40) {
shape.m_style.m_flip[1]=true;
f << "flipY,";
}
if (val&0x90) f << "fl1=" << std::hex << ((val>>4)&9) << std::dec << ",";
shape.m_fileType = (val & 0xF);
switch (shape.m_fileType) {
case 1:
shape.m_type=MacDrawProParserInternal::Shape::Text;
f << "text,";
break;
case 2:
shape.m_type=MacDrawProParserInternal::Shape::Basic;
shape.m_shape.m_type=MWAWGraphicShape::Line;
f << "line,";
break;
case 3:
shape.m_type=MacDrawProParserInternal::Shape::Basic;
shape.m_shape.m_type=MWAWGraphicShape::Rectangle;
f << "rect,";
break;
case 4:
shape.m_type=MacDrawProParserInternal::Shape::Basic;
shape.m_shape.m_type=MWAWGraphicShape::Rectangle;
f << "rectOval,";
break;
case 5:
shape.m_type=MacDrawProParserInternal::Shape::Basic;
shape.m_shape.m_type=MWAWGraphicShape::Circle;
f << "circle,";
break;
case 6:
shape.m_type=MacDrawProParserInternal::Shape::Basic;
shape.m_shape.m_type=MWAWGraphicShape::Arc;
f << "arc,";
break;
case 7:
shape.m_type=MacDrawProParserInternal::Shape::Basic;
shape.m_shape.m_type=MWAWGraphicShape::Polygon;
f << "poly[smooth],";
break;
case 8:
shape.m_type=MacDrawProParserInternal::Shape::Basic;
shape.m_shape.m_type=MWAWGraphicShape::Polygon;
f << "poly,";
break;
case 9:
if (vers==0) {
shape.m_type=MacDrawProParserInternal::Shape::Bitmap;
f << "bitmap,";
}
else {
shape.m_type=MacDrawProParserInternal::Shape::Basic;
shape.m_shape.m_type=MWAWGraphicShape::Polygon;
f << "poly[9],";
}
break;
case 0xa:
if (hasData) {
shape.m_type=MacDrawProParserInternal::Shape::Note;
f << "note,";
}
else {
shape.m_type=MacDrawProParserInternal::Shape::Group;
f << "group,";
}
break;
case 0xb:
shape.m_type=MacDrawProParserInternal::Shape::GroupEnd;
f << "group[end],";
break;
case 0xc:
if (vers) {
shape.m_type=MacDrawProParserInternal::Shape::Bitmap;
f << "bitmap,";
break;
}
MWAW_FALLTHROUGH;
default:
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: find unknown type %d\n", val));
f << "###type=" << std::hex << val << std::dec << ",";
}
val=static_cast<int>(input->readLong(1)); // 0|1
if (val==0) f << "f0[no],";
else if (val!=1) f << "f0=" << val << ",";
// read the linewidth
val=static_cast<int>(input->readULong(1));
if (val) {
float penSize=1;
if (!m_styleManager->getPenSize(val, penSize))
f << "###P" << val << ",";
else {
shape.m_style.m_lineWidth=penSize;
if (penSize<1 || penSize>1)
f << "lineWidth=" << penSize << ",";
}
}
if (vers==1) {
val=static_cast<int>(input->readULong(2));
if (val) f << "f1=" << std::hex << val << std::dec << ",";
}
// reads the pattern
int patId[2]= {0,0};
static char const *wh[]= {"line", "surf"};
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readULong(2));
if (vers>0) {
patId[i]=val;
continue;
}
if (!val) { // no color
if (i==0) {
shape.m_style.m_lineWidth=0;
f << "line[no],";
}
continue;
}
if (val&0x4000) { // pattern created from colorMap
int cId=val&0x3FFF;
MWAWColor color;
if (!m_styleManager->getColor(cId, color)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: find unknown basic color pattern: %d\n", cId));
f << wh[i] << "[color]=###" << cId << ",";
}
else {
if (i==0) shape.m_style.m_lineColor=color;
else shape.m_style.setSurfaceColor(color);
if ((i==0&&!color.isBlack()) || (i==1 && !color.isWhite()))
f << wh[i] << "[color]=" << color << ",";
}
continue;
}
MWAWGraphicStyle::Pattern pattern;
if (!m_styleManager->getPattern(val, pattern)) {
f << wh[i] << "[pat]=###" << std::hex << val << std::dec << ",";
continue;
}
MWAWColor color;
if (pattern.getUniqueColor(color)) {
if (i==0) shape.m_style.m_lineColor=color;
else shape.m_style.setSurfaceColor(color);
if ((i==0&&!color.isBlack()) || (i==1 && !color.isWhite()))
f << wh[i] << "[color]=" << color << ",";
continue;
}
f << wh[i] << "[pat]=[" << pattern << "],";
if (i==0 && pattern.getAverageColor(color))
shape.m_style.m_lineColor=color;
else if (i==1)
shape.m_style.setPattern(pattern);
}
// read the dash
val=static_cast<int>(input->readULong(1));
if (val) {
if (!m_styleManager->getDash(val, shape.m_style.m_lineDashWidth))
f << "###";
f << "dash=D" << val << ",";
}
float cornerWidth=-1;
if (shape.m_fileType==4 && !hasData) {
val=static_cast<int>(input->readLong(4));
if (val) {
cornerWidth = float(val)/65536.f/36.f; // check the unit here
f << "round[dim]=" << cornerWidth << ",";
}
else
f << "round[def],";
}
else {
// unknown
val=static_cast<int>(input->readLong(1)); // a small number negative or positif between -4 and 3
if (val)
f << "f2=" << val << ",";
}
if (shape.m_type==MacDrawProParserInternal::Shape::Basic)
updateGeometryShape(shape, cornerWidth);
if (hasData) {
val=static_cast<int>(input->readULong(2));
if (val<8) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: can not find the data position\n"));
f << "###objData[pos],";
}
else {
long actPos=input->tell();
if (!readObjectData(shape, (val-8)/4))
f << "###";
input->seek(actPos, librevenge::RVNG_SEEK_SET);
f << "objData[id]=" << (val-8)/4 << ",";
}
if (val&3)
f << "objData[low]=" << (val&3) << ",";
}
else if (shape.m_type==MacDrawProParserInternal::Shape::Group) {
val=static_cast<int>(input->readULong(2));
int N=(val/0x20)-2;
size_t lastShapeId=shapeId+size_t(N);
// in MacDraw Pro, the number of elements seems frequently a majorant, so ...
if (vers>0 && val>0x40 && int(lastShapeId)>m_state->m_numShapes) {
f << "#number[object]=" << N << ",";
lastShapeId=size_t(m_state->m_numShapes);
}
if (val<0x40 || int(lastShapeId)>m_state->m_numShapes) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: can not find the group number\n"));
f << "###number[object]=" << N << ",";
}
else {
f << "N=" << N << ",";
if (val&0x1F)
f << "N[low]="<< (val&0x1F) << ",";
input->seek(pos+expectedSize, librevenge::RVNG_SEEK_SET);
bool ok=true, findEnd=false;
for (int i=0; i<N; ++i) {
if (m_state->m_shapeList.size()>lastShapeId)
break;
int cId=readObject();
if (cId<0) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: can not find a child\n"));
f << "###";
ok=false;
break;
}
// in MacDraw Pro, the number of elements seems frequently a majorant, so we must check
if (vers>0 && m_state->m_shapeList[size_t(cId)].m_type==MacDrawProParserInternal::Shape::GroupEnd) {
f << "#[" << lastShapeId-m_state->m_shapeList.size() << "],";
findEnd=true;
break;
}
// do not use shape's childList as the vector can have grown
m_state->m_shapeList[shapeId].m_childList.push_back(size_t(cId));
}
auto nextId=int(m_state->m_shapeList.size());
if (ok && !findEnd) {
int cId=readObject(); // read end group
if (cId<0 || m_state->m_shapeList[size_t(cId)].m_type!=MacDrawProParserInternal::Shape::GroupEnd) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: oops, can not find the end group data\n"));
f << "###";
}
else // ok skip end group
++nextId;
}
m_state->m_shapeList[shapeId].m_nextId=nextId;
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return int(shapeId);
}
}
if (vers==0) {
if (input->tell() != pos+expectedSize)
ascii().addDelimiter(input->tell(),'|');
input->seek(pos+expectedSize, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return int(shapeId);
}
input->seek(pos+expectedSize-4, librevenge::RVNG_SEEK_SET);
int colId[2]= {0,0};
for (auto &id : colId) id=static_cast<int>(input->readULong(2));
// time to set the color
for (int i=0; i<2; ++i) {
if (!patId[i]) { // no color
if (i==0) {
shape.m_style.m_lineWidth=0;
f << "line[no],";
}
continue;
}
MWAWColor color(MWAWColor::black());
if ((colId[i]>>14)==3) {
if (!i || !m_styleManager->updateGradient(colId[i]&0x3FFF, shape.m_style)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: find unknown gradient: %d\n", colId[i]));
f << wh[i] << "[grad]=###" << std::hex << colId[i] << std::dec << ",";
}
continue;
}
else if ((colId[i]>>14)==1) {
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: find some 4000 color\n"));
first=false;
}
f << wh[i] << "[color]=##" << std::hex << colId[i] << std::dec << ",";
color=MWAWColor::white();
}
else if (colId[i] && !m_styleManager->getColor(colId[i], color)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObject: find unknown basic color: %d\n", colId[i]));
f << wh[i] << "[color]=###" << std::hex << colId[i] << std::dec << ",";
continue;
}
MWAWGraphicStyle::Pattern pattern;
bool useBasicColor=false;
// normally, we only have BWpattern
MWAWColor patColor;
if (patId[i]==0xC006) // used for black pattern
useBasicColor=true;
else if (!m_styleManager->getPattern(patId[i], pattern)) {
f << wh[i] << "[pat]=###" << std::hex << patId[i] << std::dec << ",";
useBasicColor=true;
}
else if (pattern.getUniqueColor(patColor))
useBasicColor=true;
if (useBasicColor) { // only color
if (i==0) shape.m_style.m_lineColor=color;
else shape.m_style.setSurfaceColor(color);
if ((i==0&&!color.isBlack()) || (i==1 && !color.isWhite()))
f << wh[i] << "[color]=" << color << ",";
continue;
}
f << wh[i] << "[pat]=[" << pattern << "],";
if (i==0 && pattern.getAverageColor(patColor)) {
float alpha=1.0f-float(patColor.getBlue())/255.f;
shape.m_style.m_lineColor=MWAWColor::barycenter(alpha, color, 1.f-alpha, MWAWColor::white());
}
else if (i==1) {
pattern.m_colors[1]=color;
shape.m_style.setPattern(pattern);
}
}
if (input->tell() != pos+expectedSize)
ascii().addDelimiter(input->tell(),'|');
input->seek(pos+expectedSize, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return int(shapeId);
}
bool MacDrawProParser::readObjectData(MacDrawProParserInternal::Shape &shape, int zId)
{
if (zId<0 || zId>=static_cast<int>(m_state->m_objectDataList.size()) || !m_state->m_objectDataList[size_t(zId)].valid()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObjectData: can not find the data for zone %d\n", zId));
return false;
}
MWAWEntry const &entry=m_state->m_objectDataList[size_t(zId)];
entry.setParsed(true);
ascii().addPos(entry.end());
ascii().addNote("_");
MWAWInputStreamPtr input = getInput();
long savePos=input->tell();
if (shape.m_type==MacDrawProParserInternal::Shape::Basic) {
bool res=readGeometryShapeData(shape, entry);
input->seek(savePos, librevenge::RVNG_SEEK_SET);
return res;
}
else if (shape.m_type==MacDrawProParserInternal::Shape::Bitmap) {
bool res=readBitmap(shape, entry);
input->seek(savePos, librevenge::RVNG_SEEK_SET);
return res;
}
else if (shape.m_type==MacDrawProParserInternal::Shape::Text ||
shape.m_type==MacDrawProParserInternal::Shape::Note) {
bool res=version()==0 ? readTextII(shape, entry) : readTextPro(shape, entry);
input->seek(savePos, librevenge::RVNG_SEEK_SET);
return res;
}
libmwaw::DebugStream f;
f << "ObjData[" << shape << "]:";
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
auto val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val!=1) f << "numUsed=" << val << ",";
input->seek(4, librevenge::RVNG_SEEK_CUR); // skip the size field
val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f1=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val) {
MWAW_DEBUG_MSG(("MacDrawProParser::readObjectData: find unexpected f2 position\n"));
f << "#f2=" << val << ",";
}
std::string extra("");
if (!readRotationInObjectData(shape, entry.end(), extra)) {
f << "###rot,";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return false;
}
f << extra;
if (input->tell()!=entry.end()) {
ascii().addDelimiter(input->tell(),'|');
MWAW_DEBUG_MSG(("MacDrawProParser::readObjectData: find unexpected data\n"));
f << "###";
}
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
input->seek(savePos, librevenge::RVNG_SEEK_SET);
return true;
}
bool MacDrawProParser::readRotationInObjectData(MacDrawProParserInternal::Shape &shape, long endPos, std::string &extra)
{
if ((shape.m_flags & 0x80)==0)
return true;
MWAWInputStreamPtr input = getInput();
if (input->tell()+28>endPos) {
MWAW_DEBUG_MSG(("MacDrawProParser::readRotationInObjectData: can not find the rotation data\n"));
extra="###rot,";
return false;
}
libmwaw::DebugStream f;
float angle= float(input->readLong(4))/65536.f; // in radians
shape.m_style.m_rotate = float(180./M_PI*double(angle));
f << "angl[rot]=" << shape.m_style.m_rotate << ",";
float dim[4];
for (auto &d : dim) d=float(input->readLong(4))/65536.f;
MWAWBox2f rect(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
f << "prevDim[rot]=" << rect << ",";
f << "unkn[rot]=[";
for (int i=0; i<2; ++i) // another points ?
f << float(input->readLong(4))/65536.f << ",";
f << "],";
shape.m_box=MWAWBox2f(rect[0]+shape.m_box[0], rect[1]+shape.m_box[0]);
if (shape.m_type==MacDrawProParserInternal::Shape::Basic)
shape.m_shape.m_bdBox=shape.m_shape.m_formBox=shape.m_box;
extra=f.str();
return true;
}
bool MacDrawProParser::updateGeometryShape(MacDrawProParserInternal::Shape &shape, float cornerWidth)
{
if (shape.m_type!=MacDrawProParserInternal::Shape::Basic) {
MWAW_DEBUG_MSG(("MacDrawProParser::updateGeometryShape: called with unexpected shape\n"));
return false;
}
switch (shape.m_fileType) {
case 2: {
int flipX=shape.m_style.m_flip[0] ? 1 : 0;
int flipY=shape.m_style.m_flip[1] ? 1 : 0;
shape.m_shape=MWAWGraphicShape::line
(MWAWVec2f(shape.m_box[1-flipX][0],shape.m_box[1-flipY][1]),
MWAWVec2f(shape.m_box[flipX][0],shape.m_box[flipY][1]));
break;
}
case 3: // rect
case 4: { // rectOval
float cWidth=0;
if (shape.m_fileType==4)
cWidth=cornerWidth>0 ? cornerWidth : 25;
shape.m_shape=MWAWGraphicShape::rectangle(shape.m_box, MWAWVec2f(cWidth,cWidth));
break;
}
case 5: // circle
shape.m_shape=MWAWGraphicShape::circle(shape.m_box);
break;
case 6:
// we need the arc angle, shape will be created when reading data
break;
case 7: // polygon
case 8: // spline
case 9: // polygon smooth
shape.m_shape.m_bdBox=shape.m_box;
break;
default:
MWAW_DEBUG_MSG(("MacDrawProParser::updateGeometryShape: called with unexpected file type\n"));
break;
}
return true;
}
bool MacDrawProParser::readTextII(MacDrawProParserInternal::Shape &shape, MWAWEntry const &entry)
{
if ((shape.m_type!=MacDrawProParserInternal::Shape::Text &&
shape.m_type!=MacDrawProParserInternal::Shape::Note) || version()>0 || entry.length()<28) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextII: the entry seems bad\n"));
return false;
}
entry.setParsed(true);
MWAWInputStreamPtr input = getInput();
libmwaw::DebugStream f;
f << "ObjData[" << shape << "]:";
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
int val;
val=static_cast<int>(input->readLong(2));
if (val) f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val!=1) f << "numUsed=" << val << ",";
input->seek(4, librevenge::RVNG_SEEK_CUR); // skip the size field
val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f1=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val>=8) {
shape.m_textZoneId=(val-8)/4;
f << "objText[id]=" << (val-8)/4 << ",";
if (val&3)
f << "objText[low]=" << (val&3) << ",";
}
else {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextII: find unexpected text position\n"));
f << "###objText[pos]=" << val << ",";
}
std::string extra("");
if (!readRotationInObjectData(shape, entry.end(), extra)) {
f << "###rot,";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return false;
}
f << extra;
auto remain=int(entry.end()-input->tell());
bool isNote=shape.m_type==MacDrawProParserInternal::Shape::Note;
int headerSize=20;
if (isNote) headerSize+=68;
if (remain<headerSize) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextII: the text zone seems too short\n"));
return false;
}
val=static_cast<int>(input->readLong(1));
if (val) f << "f0=" << val << ","; // -1|0|1
val=static_cast<int>(input->readLong(1));
MWAWParagraph paragraph;
switch (val) {
case -1:
paragraph.setInterline(2, librevenge::RVNG_PERCENT);
f << "interline=200%,";
break;
case 0: // normal
break;
default:
if (val>0) {
paragraph.setInterline(val, librevenge::RVNG_POINT, MWAWParagraph::AtLeast);
f << "interline=" << val << "pt,";
}
else // maybe percent
f << "#interline=" << val << ",";
}
val=static_cast<int>(input->readLong(1));
if (val) f << "f1=" << val << ","; // 0|-1
val=static_cast<int>(input->readLong(1));
switch (val) {
case 0: // left
break;
case -1:
paragraph.m_justify = MWAWParagraph::JustificationRight;
f << "right,";
break;
case 1:
paragraph.m_justify = MWAWParagraph::JustificationCenter;
f << "center,";
break;
case 2:
paragraph.m_justify = MWAWParagraph::JustificationFull;
f << "justified,";
break;
default:
f << "#align=" << val << ",";
break;
}
shape.m_paragraph=paragraph;
shape.m_numChars=static_cast<int>(input->readULong(2));
if (shape.m_numChars) f << "nChar=" << shape.m_numChars << ",";
int N[2];
for (auto &n : N) n=static_cast<int>(input->readULong(2));
f << "N=[" << N[0] << "," << N[1] << "],";
if (remain<headerSize+6*N[0]+4*N[1]) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextII: can not read the number of data\n"));
f << "###,";
return false;
}
if (isNote) {
long debPos=input->tell();
f << "note=[";
for (int i=0; i<2; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
f << "flgs?=[";
for (int i=0; i<2; ++i) // two time the same big number ?
f << std::hex << input->readULong(4) << std::dec << ",";
f << "],";
for (int i=0; i<8; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+2 << "=" << val << ",";
}
auto sSz=static_cast<int>(input->readULong(1));
if (sSz>31) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextII: the note size seems to big\n"));
f << "#sSz=" << sSz << ",";
}
else {
std::string name("");
for (int i=0; i<sSz; ++i) name+=char(input->readULong(1));
f << name << ",";
}
input->seek(debPos+60, librevenge::RVNG_SEEK_SET);
for (int i=0; i<4; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
f << "],";
}
f << "lineBreak=[";
for (int i=0; i<=N[0]; ++i) {
val=static_cast<int>(input->readULong(2));
// do not store the first line: pos 0 and the last line numChars
if (val && val< shape.m_numChars)
shape.m_lineBreakSet.insert(val);
f << val << ",";
}
f << "],";
f << "font=[";
for (int i=0; i<=N[1]; ++i) {
auto cPos=static_cast<int>(input->readULong(2));
val=static_cast<int>(input->readLong(2));
shape.m_fontMap[cPos]=val;
f << cPos << ":F" << val+1 << ",";
}
f << "],";
f << "line[h,?]=[";
for (int i=0; i<=N[0]; ++i)
f << input->readULong(2) << ":" << input->readULong(2) << ",";
f << "],";
if (input->tell()+4<=entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextII: find unexpected data\n"));
f << "#remain,";
}
if (input->tell()!=entry.end()) {
ascii().addDelimiter(input->tell(),'|');
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
}
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return true;
}
bool MacDrawProParser::readTextPro(MacDrawProParserInternal::Shape &shape, MWAWEntry const &entry)
{
if ((shape.m_type!=MacDrawProParserInternal::Shape::Text &&
shape.m_type!=MacDrawProParserInternal::Shape::Note) || entry.length()<28 || version()==0) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextPro: the entry seems bad\n"));
return false;
}
entry.setParsed(true);
MWAWInputStreamPtr input = getInput();
libmwaw::DebugStream f;
f << "ObjData[" << shape << "]:";
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
int val;
val=static_cast<int>(input->readLong(2));
if (val) f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val!=1) f << "numUsed=" << val << ",";
input->seek(4, librevenge::RVNG_SEEK_CUR); // skip the size field
val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f1=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val>=8) {
shape.m_textZoneId=(val-8)/4;
f << "objText[id]=" << (val-8)/4 << ",";
if (val&3)
f << "objText[low]=" << (val&3) << ",";
}
else {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextPro: find unexpected text position\n"));
f << "###objText[pos]=" << val << ",";
}
std::string extra("");
if (!readRotationInObjectData(shape, entry.end(), extra)) {
f << "###rot,";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return false;
}
f << extra;
auto remain=int(entry.end()-input->tell());
bool isNote=shape.m_type==MacDrawProParserInternal::Shape::Note;
int headerSize= 16;
if (isNote) headerSize+=20;
if (remain<headerSize) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextPro: the text zone seems too short\n"));
return false;
}
shape.m_numChars=static_cast<int>(input->readULong(4));
if (shape.m_numChars) f << "nChar=" << shape.m_numChars << ",";
long N[2];
for (auto &n : N) n=long(input->readULong(4));
f << "N=[" << N[0] << "," << N[1] << "],";
if (remain<headerSize+26*(N[0]+1)+4*(N[1]+1) && N[0]==1)
N[0]=-1;
if (remain<headerSize+26*(N[0]+1)+4*(N[1]+1)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextPro: can not read the number of data\n"));
f << "###remain,";
return false;
}
int numVal=isNote ? 12 : 2;
for (int i=0; i<numVal; ++i) {
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
f << "mod=[";
for (int i=0; i<=N[1]; ++i) {
auto cPos=static_cast<int>(input->readULong(2));
f << cPos;
val=static_cast<int>(input->readULong(2));
if (val==0xFFFF) // end
f << ":_";
else if (val&0x8000) {
shape.m_paragraphMap[cPos]=(val&0x7FFF);
f << ":P" << (val&0x7FFF) << ",";
}
else {
shape.m_fontMap[cPos]=val;
f << ":F" << val+1 << ",";
}
}
f << "],";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
for (int i=0; i<=N[0]; ++i) {
long pos=input->tell();
auto cPos=static_cast<int>(input->readULong(2));
// do not store the first line: pos 0 and the last line numChars
if (cPos && cPos< shape.m_numChars)
shape.m_lineBreakSet.insert(cPos);
f.str("");
f << "ObjData[text-P" << i << "]:pos=" << cPos << ":";
for (int j=0; j<5; ++j) {
val=static_cast<int>(input->readULong(2));
if (val)
f << "f" << j << "=" << val << ",";
}
float dim[2];
for (auto &d : dim) d=float(input->readLong(4))/65536.f;
if (dim[0]>0 || dim[0]<0 || dim[1]>0 || dim[1]<0)
f << "dim?=" << MWAWVec2f(dim[0],dim[1]) << ",";
val=static_cast<int>(input->readULong(2));
if (val) f << "f5=" << val << ",";
f << "y=" << float(input->readLong(4))/65536.f << ",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
if (input->tell()+4<=entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readTextPro: find unexpected data\n"));
ascii().addPos(input->tell());
ascii().addNote("ObjData[text]:#remain");
}
else if (input->tell()!=entry.end()) {
ascii().addPos(input->tell());
ascii().addNote("ObjData[text]:_");
}
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
return true;
}
bool MacDrawProParser::readGeometryShapeData(MacDrawProParserInternal::Shape &shape, MWAWEntry const &entry)
{
int const vers=version();
if (shape.m_type!=MacDrawProParserInternal::Shape::Basic || entry.length()<8) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: the entry seems bad\n"));
return false;
}
entry.setParsed(true);
MWAWInputStreamPtr input = getInput();
libmwaw::DebugStream f;
f << "ObjData[" << shape << "]:";
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
int val;
val=static_cast<int>(input->readLong(2));
if (val) f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val!=1) f << "numUsed=" << val << ",";
input->seek(4, librevenge::RVNG_SEEK_CUR); // skip the size field
val=static_cast<int>(input->readLong(2)); // find 0|1|2 for line, other 0
if (val) f << "f1=" << val << ",";
auto fl=static_cast<int>(input->readLong(2));
std::string extra("");
if (!readRotationInObjectData(shape, entry.end(), extra)) {
f << "###rot,";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return false;
}
f << extra;
long remain=entry.end()-input->tell();
switch (shape.m_fileType) {
case 2: { // line
if (fl & 0xF8) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: find unknown line flag\n"));
f << "#line[flag]=" << (fl&0xF8) << ",";
}
if (input->tell()+20>entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not read the line header\n"));
f << "###";
break;
}
f << "angl=" << float(input->readLong(4))/65536.f << ","; // in radians
f << "dim=[";
for (int i=0; i<4; ++i)
f << float(input->readLong(4))/65536.f << ",";
f << "],";
bool ok=true;
for (int i=0; i<2; ++i) { // the arrow data
if ((fl&(i+1))==0) continue;
shape.m_style.m_arrows[1-i]=MWAWGraphicStyle::Arrow::plain();
if (input->tell()+24>entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not read the line arrow %d\n", i));
f << "###";
ok=false;
break;
}
f << "arrow[" << (i==1 ? "beg" : "end") << "]=[";
for (int j=0; j<6; ++j) { // arrow points
f << float(input->readLong(4))/65536.f;
if (j%2) f << ",";
else f << "x";
}
f << "],";
}
if (!ok) break;
if ((fl&4)==0) {
fl &= 0xFC;
break;
}
fl &= 0xF8;
long endMeasurePos=input->tell()+44;
f << "measure=[";
if (endMeasurePos>entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not read the line measure\n"));
f << "###";
break;
}
float dim[4];
for (auto &d : dim) d=float(input->readLong(4))/65536.f;
shape.m_measureBox=MWAWBox2f(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
f << "dim=" << shape.m_measureBox << ",";
val=static_cast<int>(input->readLong(2)); // 0 or 4
if (val) f<< "f0=" << val << ",";
auto sSz=static_cast<int>(input->readULong(1));
if (sSz>27) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not read the line measure size\n"));
f << "###";
break;
}
shape.m_measureEntry.setBegin(input->tell());
shape.m_measureEntry.setLength(sSz);
std::string measure("");
for (int i=0; i<sSz; ++i) measure+=char(input->readULong(1));
f << measure;
input->seek(endMeasurePos, librevenge::RVNG_SEEK_SET);
break;
}
case 3: // rect
case 4: { // rectOval
if (!remain)
break;
if (shape.m_fileType!=4 || remain!=128) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not read the round rect representation\n"));
f << "###";
break;
}
f << "pts=[";
for (int i=0; i<16; ++i) {
float pt[2];
for (auto &p : pt) p=float(input->readLong(4))/65536.f;
f << MWAWVec2f(pt[1],pt[0]) << ",";
}
f << "],";
break;
}
case 5: { // circle
if (!remain)
break;
if (remain!=64) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not read the circle representation\n"));
f << "###";
break;
}
f << "pts=[";
for (int i=0; i<8; ++i) {
float pt[2];
for (auto &p : pt) p=float(input->readLong(4))/65536.f;
f << MWAWVec2f(pt[1],pt[0]) << ",";
}
f << "],";
break;
}
case 6: { // arc
if (remain < 8 || (remain%8))
break;
float fileAngle[2];
for (auto &angle : fileAngle) angle=float(input->readLong(4))/65536.f;
f << "angle=" << fileAngle[0] << "->" << fileAngle[0]+fileAngle[1] << ",";
float angle[2] = { 90.f-fileAngle[0]-fileAngle[1], 90.f-fileAngle[0] };
if (fileAngle[1]<0) {
angle[0]=90-fileAngle[0];
angle[1]=90-fileAngle[0]-fileAngle[1];
}
while (angle[1] > 360) {
angle[0]-=360;
angle[1]-=360;
}
while (angle[0] < -360) {
angle[0]+=360;
angle[1]+=360;
}
MWAWBox2f box=shape.m_box;
// we must compute the real bd box
float minVal[2] = { 0, 0 }, maxVal[2] = { 0, 0 };
int limitAngle[2];
for (int i = 0; i < 2; i++)
limitAngle[i] = (angle[i] < 0) ? int(angle[i]/90)-1 : int(angle[i]/90);
for (int bord = limitAngle[0]; bord <= limitAngle[1]+1; bord++) {
float ang = (bord == limitAngle[0]) ? float(angle[0]) :
(bord == limitAngle[1]+1) ? float(angle[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];
}
MWAWBox2f circleBox=box;
// we have the shape box, we need to reconstruct the circle box
if (maxVal[0]>minVal[0] && maxVal[1]>minVal[1]) {
float scaling[2]= { (box[1][0]-box[0][0])/(maxVal[0]-minVal[0]),
(box[1][1]-box[0][1])/(maxVal[1]-minVal[1])
};
float constant[2]= { box[0][0]-minVal[0] *scaling[0], box[0][1]-minVal[1] *scaling[1]};
circleBox=MWAWBox2f(MWAWVec2f(constant[0]-scaling[0], constant[1]-scaling[1]),
MWAWVec2f(constant[0]+scaling[0], constant[1]+scaling[1]));
}
if (shape.m_style.hasSurface())
shape.m_shape = MWAWGraphicShape::pie(box, circleBox, MWAWVec2f(float(angle[0]),float(angle[1])));
else
shape.m_shape = MWAWGraphicShape::arc(box, circleBox, MWAWVec2f(float(angle[0]),float(angle[1])));
int N=int(remain/8)-1;
f << "N=" << N << ",pts?=["; // a smooth polygon
for (int i=0; i<N; ++i) {
float pt[2];
for (auto &p : pt) p=float(input->readLong(4))/65536.f;
f << MWAWVec2f(pt[1],pt[0]) << ",";
}
f << "],";
break;
}
case 7: // polygon[smooth]
case 8: // polygon
if (vers==0) {
if (remain<=0 || (remain%8)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not read compute the number of point in polygon\n"));
f << "###";
break;
}
shape.m_shape.m_bdBox=shape.m_box;
auto N=int(remain/8);
f << "N=" << N << ",pts=[";
std::vector<MWAWVec2f> listVertices;
MWAWVec2f origin=shape.m_box[0];
for (int i=0; i<N; ++i) {
float pt[2];
for (auto &p : pt) p=float(input->readLong(4))/65536.f;
MWAWVec2f point(pt[1],pt[0]);
listVertices.push_back(point+origin);
f << point << ",";
}
f << "],";
if (shape.m_fileType==8) {
shape.m_shape.m_vertices=listVertices;
break;
}
// try to smooth the curve
shape.m_shape.m_type=MWAWGraphicShape::Path;
shape.m_shape.m_path.push_back(MWAWGraphicShape::PathData('M', listVertices[0]));
for (size_t i=1; i+1<listVertices.size(); ++i) {
MWAWVec2f dir=listVertices[i+1]-listVertices[i-1];
shape.m_shape.m_path.push_back(MWAWGraphicShape::PathData('S', listVertices[i], listVertices[i]-0.1f*dir));
}
if (listVertices.size()>1)
shape.m_shape.m_path.push_back(MWAWGraphicShape::PathData('L', listVertices.back()));
break;
}
MWAW_FALLTHROUGH;
case 9: { // spline
if (remain<=2) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not read compute the number of point in spline\n"));
f << "###";
break;
}
auto N=static_cast<int>(input->readULong(2));
f << "N=" << N << ",";
f << "pts=[";
MWAWVec2f origin=shape.m_box[0], prevPoint;
bool hasPrevPoint=false, isSpline=false;
std::vector<MWAWVec2f> listVertices;
std::vector<MWAWGraphicShape::PathData> path;
for (int i=0; i<N; ++i) {
long pos=input->tell();
auto type=static_cast<int>(input->readULong(2));
int nCoord=0;
if (type>=0 && type<=9) {
static int const numCoord[]= {3, 3, 3, 3, 0/*4*/, 0/*5*/, 3, 3, 1, 1};
nCoord=numCoord[type];
}
if (nCoord<=0 || pos+2+8*nCoord>entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: can not determine the number of coordinate\n"));
f << "###type=" << type << ",";
break;
}
MWAWVec2f points[3];
for (int j=0; j<nCoord; ++j) {
float pPos[2];
for (auto &p : pPos) p=float(input->readLong(4))/65536;
points[j]=MWAWVec2f(pPos[1],pPos[0]);
f << points[j] << (j==nCoord-1 ? "," : ":");
points[j]+=origin;
}
if (nCoord==1)
listVertices.push_back(points[0]);
else
isSpline=true;
MWAWVec2f pt=nCoord==1 ? points[0] : points[1];
char pType = hasPrevPoint ? 'C' : i==0 ? 'M' : nCoord==1 ? 'L' : 'S';
path.push_back(MWAWGraphicShape::PathData(pType, pt, hasPrevPoint ? prevPoint : points[0], points[0]));
hasPrevPoint=nCoord==3;
if (hasPrevPoint) prevPoint=points[2];
}
f << "],";
if (isSpline) {
shape.m_shape.m_type=MWAWGraphicShape::Path;
shape.m_shape.m_path=path;
}
else
shape.m_shape.m_vertices=listVertices;
break;
}
default:
if (!remain) break;
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: called with unexpected file type\n"));
break;
}
if (fl) f << "fl=" << std::hex << fl << std::dec << ",";
if (input->tell()!=entry.end()) {
MWAW_DEBUG_MSG(("MacDrawProParser::readGeometryShapeData: find unexpected data\n"));
f << "###";
ascii().addDelimiter(input->tell(),'|');
}
// if there is a rotation, we need to recompute the rect, ..., arc shape
if (shape.m_fileType>=3 && shape.m_fileType<=6 && (shape.m_flags & 0x80))
shape.m_shape = shape.m_shape.rotate(shape.m_style.m_rotate, shape.m_box.center());
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return true;
}
bool MacDrawProParser::readBitmap(MacDrawProParserInternal::Shape &shape, MWAWEntry const &entry)
{
if (shape.m_type!=MacDrawProParserInternal::Shape::Bitmap || entry.length()<10) {
MWAW_DEBUG_MSG(("MacDrawProParser::readBitmap: the entry seems bad\n"));
return false;
}
int const vers=version();
entry.setParsed(true);
MWAWInputStreamPtr input = getInput();
libmwaw::DebugStream f;
f << "Entries(Bitmap)[" << shape << "]:";
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
int val;
val=static_cast<int>(input->readLong(2));
if (val) f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val!=1) f << "numUsed=" << val << ",";
input->seek(4, librevenge::RVNG_SEEK_CUR); // skip the size field
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+1 << "=" << val << ",";
}
std::string extra("");
if (!readRotationInObjectData(shape, entry.end(), extra)) {
f << "###rot,";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return false;
}
f << extra;
int dim[4];
for (auto &d : dim) d=static_cast<int>(input->readLong(2));
MWAWBox2i &bitmapBox=shape.m_bitmapDim;
bitmapBox=MWAWBox2i(MWAWVec2i(dim[1],dim[0]), MWAWVec2i(dim[3],dim[2]));
f << "dim=" << bitmapBox << ",";
f << "id?=" << std::hex << input->readULong(4) << std::dec << ",";
shape.m_numBytesByRow=static_cast<int>(input->readULong(2));
if (vers>0 && shape.m_numBytesByRow&0x8000) {
f << "color,";
shape.m_bitmapIsColor=true;
shape.m_numBytesByRow&=0x7FFF;
}
f << "rowSize=" << shape.m_numBytesByRow << ",";
MWAWBox2i &fileBox=shape.m_bitmapFileDim;
for (auto &d : dim) d=static_cast<int>(input->readLong(2));
fileBox=MWAWBox2i(MWAWVec2i(dim[1],dim[0]), MWAWVec2i(dim[3],dim[2]));
if (bitmapBox!=fileBox)
f << "bitmap[dimInFile]="<< fileBox << ",";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
if (vers) {
long pos=input->tell();
f.str("");
f << "Bitmap-A:";
for (int i=0; i<14; ++i) { // BW: all 0, color f4=f6=48, f9=f11=8, f10=1
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
val=static_cast<int>(input->readULong(4)); // big number in color : an unit ?
if (val) f << "f15=" << std::hex << val << std::dec << ",";
for (int i=0; i<2; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
shape.m_bitmapClutId=static_cast<int>(input->readLong(2));
if (shape.m_bitmapClutId) f << "clut[id]=" << shape.m_bitmapClutId << ",";
for (int i=0; i<2; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i+2 << "=" << val << ",";
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
shape.m_bitmapEntry.setBegin(input->tell());
shape.m_bitmapEntry.setLength(fileBox.size()[1]*shape.m_numBytesByRow);
if (fileBox.size()[1]<0 || shape.m_numBytesByRow<0 || !input->checkPosition(shape.m_bitmapEntry.end())) {
MWAW_DEBUG_MSG(("MacDrawProParser::readBitmap: can not compute the bitmap endPos\n"));
ascii().addPos(shape.m_bitmapEntry.begin());
ascii().addNote("Bitmap[data]:###");
return false;
}
int const numBytesByPixel=shape.m_bitmapIsColor ? 1 : 8;
if (shape.m_numBytesByRow*numBytesByPixel < fileBox.size()[0] ||
fileBox[0][0]>bitmapBox[0][0] || fileBox[0][1]>bitmapBox[0][1] ||
fileBox[1][0]<bitmapBox[1][0] || fileBox[1][1]<bitmapBox[1][1]) {
ascii().addPos(shape.m_bitmapEntry.begin());
ascii().addNote("Bitmap[data]:###");
MWAW_DEBUG_MSG(("MacDrawProParser::readBitmap: something look bad when reading a bitmap header\n"));
shape.m_bitmapEntry=MWAWEntry();
}
else
ascii().skipZone(shape.m_bitmapEntry.begin(), shape.m_bitmapEntry.end()-1);
if (shape.m_bitmapEntry.end()+4<entry.end()) {
ascii().addPos(shape.m_bitmapEntry.end());
ascii().addNote("Bitmap[end]:###");
MWAW_DEBUG_MSG(("MacDrawProParser::readBitmap: the bitmap data zone seems too big\n"));
}
return true;
}
////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool MacDrawProParser::checkHeader(MWAWHeader *header, bool strict)
{
*m_state = MacDrawProParserInternal::State();
MWAWInputStreamPtr input = getInput();
if (!input || !input->hasDataFork() || !input->checkPosition(512))
return false;
libmwaw::DebugStream f;
f << "FileHeader:";
input->seek(0, librevenge::RVNG_SEEK_SET);
auto val=static_cast<int>(input->readULong(2));
int vers=0;
if (val==0x4452) { // MacDraw II
if (input->readULong(2)!=0x5747) return false;
}
else if (val==0x5354) { // MacDraw II template
m_state->m_isStationery=true;
f << "stationery,";
if (input->readULong(2)!=0x4154) return false;
}
else if (val==0x6444) { // MacDraw Pro
if (input->readULong(2)!=0x6f63) return false;
f << "pro,";
vers=1;
}
else if (val==0x644c) { // MacDraw Pro template
m_state->m_isStationery=true;
if (input->readULong(2)!=0x6962) return false;
f << "pro,";
vers=1;
}
else return false;
#ifndef DEBUG
if (vers==1 && !input->hasResourceFork()) {
// as in MacDraw pro, the color, patterns, gradients are stored in the resource fork, ...
MWAW_DEBUG_MSG(("MacDrawProParser::checkHeader: impossible to read a MacDraw Pro files without resource fork\n"));
return false;
}
#endif
val=static_cast<int>(input->readULong(2));
if (val==0) {
f << "D2[not],";
vers=0;
}
else if (val!=0x4432) {
MWAW_DEBUG_MSG(("MacDrawProParser::checkHeader: find unexpected header\n"));
return false;
}
f << "version=" << vers << ",";
f << "subVersion=" << input->readLong(2) << ",";
ascii().addPos(0);
ascii().addNote(f.str().c_str());
if (strict) {
// try to begin the parsing
if (!readHeaderInfo() || !m_styleManager->readStyles(m_state->m_sizeStyleZones)) return false;
m_styleManager.reset(new MacDrawProStyleManager(*this));
// we must check that this is not a basic pict file
input->seek(512+2, librevenge::RVNG_SEEK_SET);
int dim[4];
for (auto &d : dim) d=static_cast<int>(input->readLong(2));
val=static_cast<int>(input->readLong(2));
if (dim[0]<dim[2] && dim[1]<dim[3] && (val==0x1101 || (val==0x11 && input->readLong(2)==0x2ff))) {
// posible
input->seek(512, librevenge::RVNG_SEEK_SET);
MWAWBox2f box;
if (MWAWPictData::check(input, static_cast<int>(input->size()-512), box) != MWAWPict::MWAW_R_BAD)
return false;
}
}
setVersion(vers);
m_state->m_version=vers;
if (header)
header->reset(MWAWDocument::MWAW_T_MACDRAWPRO, vers, MWAWDocument::MWAW_K_DRAW);
input->seek(512,librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// try to read the prefs zone
////////////////////////////////////////////////////////////
bool MacDrawProParser::readHeaderInfo()
{
int const vers=version();
MWAWInputStreamPtr input = getInput();
if (!input->checkPosition(512)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readHeaderInfo: the prefs zone seems too short\n"));
ascii().addPos(14);
ascii().addNote("Entries(HeaderInfo):#");
return false;
}
input->seek(8,librevenge::RVNG_SEEK_SET);
if (!readPrintInfo()) {
ascii().addPos(8);
ascii().addNote("Entries(PrintInfo):#");
}
input->seek(8+120, librevenge::RVNG_SEEK_SET);
// v2: cut in 128, 40, 3*40, remain
long pos=input->tell();
libmwaw::DebugStream f;
f << "Entries(HeaderInfo):";
for (int i=0; i<9; ++i) { // f0=1|2|7, f1=0|75|78|7c, f2=0|48, f4=0|48, f5=0|48, f6=0|48, f7=0|48
auto val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
ascii().addDelimiter(input->tell(),'|');
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(0x100, librevenge::RVNG_SEEK_SET);
pos=input->tell();
f.str("");
f << "HeaderInfo-A:";
auto val=static_cast<int>(input->readULong(2)); // 0 or 8
if (val) f << "f0=" << val << ",";
m_state->m_numLayers=static_cast<int>(input->readULong(2)); // 1|2|9
if (m_state->m_numLayers!=1) f << "num[layers]=" << m_state->m_numLayers << ",";
std::string extra("");
int const headerStyleSize=vers==0 ? 24 : 12;
if (!m_styleManager->readHeaderInfoStylePart(extra)) {
f << "###Style[ignored],";
input->seek(pos+4+headerStyleSize, librevenge::RVNG_SEEK_SET);
}
else
f << extra;
m_state->m_actualLayer=static_cast<int>(input->readULong(2));
if (m_state->m_actualLayer!=1) f << "layer[cur]=" << m_state->m_actualLayer << ",";
val=static_cast<int>(input->readULong(2)); // h1=1|2|40
if (val!=1) f << "h1=" << val << ",";
val=static_cast<int>(input->readULong(2)); // 0|45
if (val) f << "h2=" << val << ",";
val=static_cast<int>(input->readULong(2)); // actual shape ?
if (val!=1) f << "shape[actual]?=" << val << ",";
for (int i=0; i<2; ++i) { // h3=0, h4=2|3|7|a
val=static_cast<int>(input->readULong(2));
if (val) f << "h" << i+3 << "=" << val << ",";
}
m_state->m_numLibraries=static_cast<int>(input->readULong(2));
if (m_state->m_numLibraries) f << "num[libraries]=" << m_state->m_numLibraries << ",";
input->seek(pos+4+headerStyleSize+12, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
for (int i=0; i<3; ++i) {
pos=input->tell();
f.str("");
f << "HeaderInfo-B" << i << ":";
val=static_cast<int>(input->readULong(2)); // always 0 ?
if (val) f << "f0=" << val << ",";
val=static_cast<int>(input->readULong(2));
if (val && i==0) {
m_state->m_numLibraries=val;
f << "num[libraries]=" << m_state->m_numLibraries << ",";
}
else if (val)
f << "num[shape?]=" << val << ",";
ascii().addDelimiter(input->tell(),'|');
input->seek(pos+40, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
pos=input->tell();
f.str("");
f << "HeaderInfo-C:";
int const numSizeZones=vers==0 ? 5 : 6;
for (int i=0; i< numSizeZones; ++i) {
m_state->m_sizeStyleZones[i]=long(input->readULong(4));
if (!m_state->m_sizeStyleZones[i]) continue;
f << "sz[style" << i << "]=" << std::hex << m_state->m_sizeStyleZones[i] << std::dec << ",";
}
if (vers==0) {
for (int i=0; i< 5; ++i) {
auto lVal=long(input->readULong(4));
if (!lVal) continue;
MWAW_DEBUG_MSG(("MacDrawProParser::readHeaderInfo: find some unexpected value in C zone, we may have a problem\n"));
f << "##f" << i << "=" << std::hex << lVal << std::dec << ",";
}
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "HeaderInfo-C(II):";
for (auto &size : m_state->m_sizeLayerZones) size=long(input->readULong(4));
f << "sz[layers]=" << std::hex << m_state->m_sizeLayerZones[0] << "+" << m_state->m_sizeLayerZones[1] << std::dec << ",";
m_state->m_sizeFZones[0]=long(input->readULong(4));
if (m_state->m_sizeFZones[0])
f << "sz[ZoneF0]=" << std::hex << m_state->m_sizeFZones[0] << std::dec << ",";
for (auto &size : m_state->m_sizeLibraryZones) size=long(input->readULong(4));
if (m_state->m_sizeLibraryZones[0] || m_state->m_sizeLibraryZones[1])
f << "sz[libraries]=" << std::hex << m_state->m_sizeLibraryZones[0] << "+" << m_state->m_sizeLibraryZones[1] << std::dec << ",";
for (int i=1; i<4; ++i) {
m_state->m_sizeFZones[i]=long(input->readULong(4));
if (!m_state->m_sizeFZones[i]) continue;
f << "sz[ZoneF" << i << "]=" << std::hex << m_state->m_sizeFZones[i] << std::dec << ",";
}
if (vers==0) {
for (int i=0; i<3; ++i) { // always 0?
auto lVal=long(input->readULong(4));
if (!lVal) continue;
MWAW_DEBUG_MSG(("MacDrawProParser::readHeaderInfo: find some unexpected value in C(II) zone, we may have a problem\n"));
f << "##f" << i << "=" << lVal << ",";
}
}
else {
f << "id?=" << std::hex << input->readULong(4) << std::dec << ",";
for (int i=0; i<2; ++i) { // always 0, 1?
val=static_cast<int>(input->readLong(2));
if (val!=i)
f << "f" << i << "=" << val << ",";
}
}
input->seek(vers==1 ? 0x1d4 : 0x1f4, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// try to read the print info zone
////////////////////////////////////////////////////////////
bool MacDrawProParser::readPrintInfo()
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
long endPos=pos+120;
if (!input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readPrintInfo: file seems too short\n"));
return false;
}
libmwaw::DebugStream f;
f << "Entries(PrintInfo):";
libmwaw::PrinterInfo info;
if (!info.read(input)) {
MWAW_DEBUG_MSG(("MacDrawProParser::readPrintInfo: can not read print info\n"));
return false;
}
f << info;
MWAWVec2i paperSize = info.paper().size();
MWAWVec2i pageSize = info.page().size();
if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
paperSize.x() <= 0 || paperSize.y() <= 0) {
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
// define margin from print info
MWAWVec2i lTopMargin= -1 * info.paper().pos(0);
MWAWVec2i rBotMargin=info.paper().size() - info.page().size();
// move margin left | top
int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0;
int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0;
lTopMargin -= MWAWVec2i(decalX, decalY);
rBotMargin += MWAWVec2i(decalX, decalY);
// decrease right | bottom
int rightMarg = rBotMargin.x() -50;
if (rightMarg < 0) rightMarg=0;
int botMarg = rBotMargin.y() -50;
if (botMarg < 0) botMarg=0;
getPageSpan().setMarginTop(lTopMargin.y()/72.0);
getPageSpan().setMarginBottom(botMarg/72.0);
getPageSpan().setMarginLeft(lTopMargin.x()/72.0);
getPageSpan().setMarginRight(rightMarg/72.0);
getPageSpan().setFormLength(paperSize.y()/72.);
getPageSpan().setFormWidth(paperSize.x()/72.);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
//
// send data
//
////////////////////////////////////////////////////////////
bool MacDrawProParser::sendMasterPage()
{
if (!m_state->m_createMasterPage)
return true;
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendMasterPage: can not find the listener\n"));
m_state->m_createMasterPage=false;
return false;
}
MWAWPageSpan ps(getPageSpan());
ps.setMasterPageName(librevenge::RVNGString("Master"));
if (!listener->openMasterPage(ps)) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendMasterPage: can not create the master page\n"));
m_state->m_createMasterPage=false;
return false;
}
for (auto const &layer : m_state->m_layerList) {
if (layer.m_isHidden)
continue;
send(layer);
}
listener->closeMasterPage();
return true;
}
bool MacDrawProParser::sendPage(int page)
{
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendPage: can not find the listener\n"));
return false;
}
if (page>0)
listener->insertBreak(MWAWListener::PageBreak);
if (m_state->m_sendAsLibraries) {
if (page<0 || page>=static_cast<int>(m_state->m_libraryList.size())) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendPage: can not find a library\n"));
return false;
}
send(m_state->m_libraryList[size_t(page)]);
return true;
}
int actHidden=0;
for (auto const &layer : m_state->m_layerList) {
if (layer.m_isHidden && actHidden++!=page)
continue;
if (!layer.m_isHidden && m_state->m_createMasterPage)
continue;
send(layer);
}
return true;
}
bool MacDrawProParser::send(MacDrawProParserInternal::Library const &library)
{
auto numLayers=static_cast<int>(m_state->m_layerList.size());
MWAWVec2f leftTop(72.f*float(getPageSpan().getMarginLeft()),72.f*float(getPageSpan().getMarginTop()));
for (auto id : library.m_layerList) {
if (id<0 || id>=numLayers) continue;
auto const &layer=m_state->m_layerList[size_t(id)];
if (layer.m_libraryToObjectMap.find(library.m_id)!=layer.m_libraryToObjectMap.end()) {
int objId=layer.m_firstShape+layer.m_libraryToObjectMap.find(library.m_id)->second;
if (objId>0 && objId<int(m_state->m_shapeList.size())) {
MacDrawProParserInternal::Shape const &shape=m_state->m_shapeList[size_t(objId-1)];
if (shape.m_type==MacDrawProParserInternal::Shape::Group) {
send(shape, leftTop);
continue;
}
}
}
send(layer);
}
return true;
}
bool MacDrawProParser::send(MacDrawProParserInternal::Layer const &layer)
{
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDrawProParser::send[layer]: can not find the listener\n"));
return false;
}
if (layer.m_firstShape<0)
return true;
auto maxShape=static_cast<int>(m_state->m_shapeList.size());
if (layer.m_firstShape+layer.m_numShapes<maxShape)
maxShape=layer.m_firstShape+layer.m_numShapes;
if (maxShape<=layer.m_firstShape)
return false;
bool openLayer=false;
if (!layer.m_name.empty())
openLayer=listener->openLayer(layer.m_name);
MWAWVec2f leftTop(72.f*float(getPageSpan().getMarginLeft()),72.f*float(getPageSpan().getMarginTop()));
for (int i=layer.m_firstShape; i<maxShape; ++i) {
MacDrawProParserInternal::Shape const &shape=m_state->m_shapeList[size_t(i)];
send(shape, leftTop);
if (shape.m_nextId>i+1)
i+=shape.m_nextId-(i+1);
}
if (openLayer)
listener->closeLayer();
return true;
}
bool MacDrawProParser::send(MacDrawProParserInternal::Shape const &shape, MWAWVec2f const &orig)
{
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDrawProParser::send[shape]: can not find the listener\n"));
return false;
}
shape.m_isSent=true;
// for not basic shape, we need the box before the rotation, so compute the box by hand
MWAWBox2f box=(shape.m_type==MacDrawProParserInternal::Shape::Basic) ? shape.m_shape.getBdBox() : shape.m_box;
box=MWAWBox2f(box[0]+orig, box[1]+orig);
MWAWPosition pos(box[0], box.size(), librevenge::RVNG_POINT);
pos.m_anchorTo = MWAWPosition::Page;
switch (shape.m_type) {
case MacDrawProParserInternal::Shape::Basic:
listener->insertShape(pos, shape.m_shape, shape.m_style);
if (shape.m_shape.m_type==MWAWGraphicShape::Line && shape.m_measureEntry.valid()) {
MWAWGraphicStyle style;
style.m_lineWidth=0;
style.setSurfaceColor(MWAWColor::white());
std::shared_ptr<MWAWSubDocument> doc(new MacDrawProParserInternal::SubDocument(*this, getInput(), shape.m_measureEntry));
MWAWPosition measurePos(box[0]+shape.m_measureBox[0], shape.m_measureBox.size(), librevenge::RVNG_POINT);
measurePos.m_anchorTo = MWAWPosition::Page;
listener->insertTextBox(measurePos, doc, style);
}
break;
case MacDrawProParserInternal::Shape::Bitmap:
return sendBitmap(shape, pos);
case MacDrawProParserInternal::Shape::Group: {
size_t numShapes=m_state->m_shapeList.size();
if (!numShapes) break;
listener->openGroup(pos);
for (auto childId : shape.m_childList) {
if (childId>=numShapes) {
MWAW_DEBUG_MSG(("MacDrawProParser::send: can not find a child\n"));
continue;
}
auto const &child=m_state->m_shapeList[childId];
if (child.m_isSent) {
MWAW_DEBUG_MSG(("MacDrawProParser::send: the child is already sent\n"));
continue;
}
send(child, orig);
}
listener->closeGroup();
break;
}
case MacDrawProParserInternal::Shape::GroupEnd:
break;
case MacDrawProParserInternal::Shape::Note: {
MWAWGraphicStyle style=shape.m_style;
style.m_lineWidth=1;
MWAWBorder border;
border.m_color=MWAWColor::black();
border.m_width=1;
style.setBorders(libmwaw::LeftBit|libmwaw::BottomBit|libmwaw::RightBit, border);
border.m_color=MWAWColor(0x60,0x60,0); // normally pattern of yellow and black
border.m_width=20;
style.setBorders(libmwaw::TopBit, border);
style.setSurfaceColor(MWAWColor(0xff,0xff,0));
style.m_shadowOffset=MWAWVec2f(3,3);
style.setShadowColor(MWAWColor(0x80,0x80,0x80));
std::shared_ptr<MWAWSubDocument> doc(new MacDrawProParserInternal::SubDocument(*this, getInput(), shape.m_id));
listener->insertTextBox(pos, doc, style);
return true;
}
case MacDrawProParserInternal::Shape::Text: {
std::shared_ptr<MWAWSubDocument> doc(new MacDrawProParserInternal::SubDocument(*this, getInput(), shape.m_id));
listener->insertTextBox(pos, doc, shape.m_style);
return true;
}
case MacDrawProParserInternal::Shape::Unknown:
#if !defined(__clang__)
default:
#endif
return false;
}
return true;
}
bool MacDrawProParser::sendBitmap(MacDrawProParserInternal::Shape const &shape, MWAWPosition const &position)
{
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendBitmap: can not find the listener\n"));
return false;
}
if (!shape.m_bitmapEntry.valid()) return false;
int const numBytesByRow=shape.m_numBytesByRow;
int const numPixelByBytes=shape.m_bitmapIsColor ? 1 : 8;
MWAWVec2i pictDim=shape.m_bitmapDim.size();
if (shape.m_type!=MacDrawProParserInternal::Shape::Bitmap || numBytesByRow<=0 ||
pictDim[0]<0 || pictDim[1]<0 ||
numBytesByRow*shape.m_bitmapFileDim.size()[1]<shape.m_bitmapEntry.length() ||
shape.m_bitmapDim[0][0]<0 || shape.m_bitmapDim[0][1]<0 ||
shape.m_bitmapDim[0][0]<shape.m_bitmapFileDim[0][0] ||
shape.m_bitmapFileDim.size()[0]<=0 || shape.m_bitmapFileDim.size()[1]<=0 ||
numPixelByBytes*numBytesByRow<shape.m_bitmapFileDim.size()[0]) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendBitmap: the bitmap seems bad\n"));
return false;
}
std::shared_ptr<MWAWPictBitmap> pict;
if (shape.m_bitmapIsColor) {
MWAWRSRCParserPtr rsrcParser=getRSRCParser();
if (!rsrcParser) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendBitmap: need access to resource fork to read colormap\n"));
return false;
}
auto &entryMap = rsrcParser->getEntriesMap();
auto it=entryMap.find("clut");
MWAWEntry entry;
while (it!=entryMap.end() && it->first=="clut") {
if (it->second.id()==shape.m_bitmapClutId) {
entry=it->second;
break;
}
++it;
}
std::vector<MWAWColor> colList;
if (!entry.valid() || !rsrcParser->parseClut(entry, colList) || colList.empty()) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendBitmap: can not read the color list\n"));
return false;
}
auto pictIndexed = std::make_shared<MWAWPictBitmapIndexed>(pictDim);
pict = pictIndexed;
pictIndexed->setColors(colList);
std::vector<int> data;
data.resize(size_t(pictDim[0]), 0);
MWAWInputStreamPtr input=getInput();
input->seek(shape.m_bitmapEntry.begin(), librevenge::RVNG_SEEK_SET);
auto numCol=int(colList.size());
for (int r=shape.m_bitmapFileDim[0][1]; r<shape.m_bitmapFileDim[1][1]; ++r) {
long pos=input->tell();
if (r<shape.m_bitmapDim[0][1]||r>=shape.m_bitmapDim[1][1]) { // must not appear, but...
input->seek(pos+numBytesByRow, librevenge::RVNG_SEEK_SET);
continue;
}
int wPos=shape.m_bitmapDim[0][0]-shape.m_bitmapFileDim[0][0];
for (int col=shape.m_bitmapFileDim[0][0]; col<shape.m_bitmapFileDim[1][0]; ++col) {
auto c=static_cast<int>(input->readULong(1));
if (wPos>=pictDim[0]) break;
if (c>=numCol) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendBitmap: find some eroneous index, reset to 0\n"));
c=0;
}
data[size_t(wPos++)]=c;
}
pictIndexed->setRow(r-shape.m_bitmapDim[0][1], &data[0]);
input->seek(pos+numBytesByRow, librevenge::RVNG_SEEK_SET);
}
}
else {
// change: implement indexed transparent color, replaced this code
auto pictColor = std::make_shared<MWAWPictBitmapColor>(pictDim, true);
pict = pictColor;
MWAWColor transparent(255,255,255,0);
MWAWColor black(MWAWColor::black());
std::vector<MWAWColor> data;
data.resize(size_t(pictDim[0]), transparent);
// first set unseen row to zero (even if this must not appear)
for (int r=shape.m_bitmapDim[0][1]; r<shape.m_bitmapFileDim[0][1]; ++r) pictColor->setRow(r-shape.m_bitmapDim[0][1], &data[0]);
for (int r=shape.m_bitmapFileDim[1][1]; r<shape.m_bitmapDim[1][1]; ++r) pictColor->setRow(r-shape.m_bitmapDim[0][1], &data[0]);
MWAWInputStreamPtr input=getInput();
input->seek(shape.m_bitmapEntry.begin(), librevenge::RVNG_SEEK_SET);
for (int r=shape.m_bitmapFileDim[0][1]; r<shape.m_bitmapFileDim[1][1]; ++r) {
long pos=input->tell();
if (r<shape.m_bitmapDim[0][1]||r>=shape.m_bitmapDim[1][1]) { // must not appear, but...
input->seek(pos+numBytesByRow, librevenge::RVNG_SEEK_SET);
continue;
}
int wPos=shape.m_bitmapDim[0][0]-shape.m_bitmapFileDim[0][0];
for (int col=shape.m_bitmapFileDim[0][0]; col<shape.m_bitmapFileDim[1][0]; ++col) {
auto c=static_cast<unsigned char>(input->readULong(1));
for (int j=0, bit=0x80; j<8 ; ++j, bit>>=1) {
if (wPos>=pictDim[0]) break;
data[size_t(wPos++)]=(c&bit) ? black : transparent;
}
}
pictColor->setRow(r-shape.m_bitmapDim[0][1], &data[0]);
input->seek(pos+numBytesByRow, librevenge::RVNG_SEEK_SET);
}
}
MWAWEmbeddedObject picture;
if (!pict || !pict->getBinary(picture)) return false;
#ifdef DEBUG_WITH_FILES
if (!picture.m_dataList.empty()) {
static int volatile pictName = 0;
libmwaw::DebugStream f;
f << "PICT-" << ++pictName << ".bmp";
libmwaw::Debug::dumpFile(picture.m_dataList[0], f.str().c_str());
}
#endif
// bitmap have no border
MWAWGraphicStyle style=shape.m_style;
style.m_lineWidth=0;
listener->insertPicture(position, picture, style);
return true;
}
bool MacDrawProParser::sendText(int zId)
{
int const vers=version();
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendText: can not find the listener\n"));
return false;
}
if (zId<0||zId>=static_cast<int>(m_state->m_shapeList.size()) ||
(m_state->m_shapeList[size_t(zId)].m_type != MacDrawProParserInternal::Shape::Text &&
m_state->m_shapeList[size_t(zId)].m_type != MacDrawProParserInternal::Shape::Note)) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendText: can not find the text shape %d\n", zId));
return false;
}
auto const &shape=m_state->m_shapeList[size_t(zId)];
shape.m_isSent = true;
if (shape.m_textZoneId<0 || shape.m_textZoneId>=int(m_state->m_objectTextList.size())) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendText: can not find the text zone %d\n", shape.m_textZoneId));
return false;
}
MWAWEntry const &entry=m_state->m_objectTextList[size_t(shape.m_textZoneId)];
entry.setParsed(true);
MWAWInputStreamPtr input=getInput();
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Object[text]:shape=[" << shape << "],";
auto numUsed=static_cast<int>(input->readULong(4));
if (numUsed!=1) f << "numUsed=" << numUsed << ",";
// skip size
input->seek(4, librevenge::RVNG_SEEK_CUR);
int N=shape.m_numChars;
if (int(entry.length()-8)<N) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendText: the zone size seems short\n"));
N=int(entry.length()-8);
}
long endPos=input->tell()+N;
if (vers==0)
listener->setParagraph(shape.m_paragraph);
for (int i=0; i<N; ++i) {
if (input->isEnd())
break;
if (vers>=0 && shape.m_paragraphMap.find(i)!=shape.m_paragraphMap.end()) {
int id=shape.m_paragraphMap.find(i)->second;
f << "[P" << id+1 << "]";
MWAWParagraph para;
if (id>=0 && !m_styleManager->getParagraph(id, para))
f << "###";
listener->setParagraph(para);
}
/* note: it is also possible to add a soft linebreak here when the
previous character is not a EOL. Sometimes this is better but
other times not, ie. if the line slightly overlaps the right
border, we will end with some "empty" lines */
if (shape.m_lineBreakSet.find(i)!=shape.m_lineBreakSet.end())
f << "[L]";
if (shape.m_fontMap.find(i)!=shape.m_fontMap.end()) {
int id=shape.m_fontMap.find(i)->second;
f << "[F" << id+1 << "]";
MWAWFont font;
if (id>=0 && !m_styleManager->getFont(id+1, font))
f << "###";
listener->setFont(font);
}
auto c = char(input->readULong(1));
if (c==0) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendText: find char 0\n"));
f << "#[0]";
continue;
}
f << c;
switch (c) {
case 9:
listener->insertTab();
break;
case 0xd:
if (i+1!=N)
listener->insertEOL();
break;
default:
listener->insertCharacter(static_cast<unsigned char>(c), input, endPos);
break;
}
}
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return true;
}
bool MacDrawProParser::sendMeasure(MWAWEntry const &entry)
{
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendMeasure: can not find the listener\n"));
return false;
}
if (!entry.valid()) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendMeasure: can not find the measure entry\n"));
return false;
}
MWAWInputStreamPtr input=getInput();
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
long endPos=entry.end();
for (long i=0; i<entry.length(); ++i) {
if (input->isEnd()) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendMeasure: the zone seems too short\n"));
break;
}
auto c=char(input->readULong(1));
if (c==0) {
MWAW_DEBUG_MSG(("MacDrawProParser::sendText: find char 0\n"));
continue;
}
switch (c) {
case 9:
listener->insertTab();
break;
case 0xd:
listener->insertEOL();
break;
default:
listener->insertCharacter(static_cast<unsigned char>(c), input, endPos);
break;
}
}
return true;
}
void MacDrawProParser::flushExtra()
{
MWAWVec2f leftTop(72.f*float(getPageSpan().getMarginLeft()),72.f*float(getPageSpan().getMarginRight()));
for (auto const &shape : m_state->m_shapeList) {
if (shape.m_isSent || shape.m_type==MacDrawProParserInternal::Shape::GroupEnd)
continue;
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("MacDrawProParser::flushExtra: find some unsent zone\n"));
first=false;
}
send(shape, leftTop);
}
libmwaw::DebugStream f;
int n=0;
for (auto const &entry : m_state->m_objectTextList) {
++n;
if (!entry.valid() || entry.isParsed())
continue;
static bool first=false;
if (first) {
MWAW_DEBUG_MSG(("MacDrawProParser::flushExtra: find some unparsed text's object zones\n"));
first=false;
}
f.str("");
f << "Entries(ObjText)[" << n-1 << "]:###unparsed";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: