/* -*- 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 <sstream>
#include <librevenge/librevenge.h>
#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 "MWAWSubDocument.hxx"
#include "MacDraftParser.hxx"
/** Internal: the structures of a MacDraftParser */
namespace MacDraftParserInternal
{
//! low level: bitmap file position in a MacDraft file
struct BitmapFileData {
//! constructor
BitmapFileData()
: m_id(0)
, m_rowSize(0)
, m_dimension()
, m_entry()
{
}
//! check is the entry is coherent
bool ok() const
{
return m_dimension.size()[0]>=0 && m_dimension.size()[1]>=0 &&
m_rowSize>=0 && long(m_rowSize)*8>=m_dimension.size()[1] &&
long(m_rowSize)*m_dimension.size()[1]<=m_entry.length();
}
//! the bitmap id
unsigned long m_id;
//! the row size
int m_rowSize;
//! the bitmap dimension
MWAWBox2i m_dimension;
//! the data position
MWAWEntry m_entry;
};
//! generic class used to store shape in MWAWDraftParser
struct Shape {
//! the different shape
enum Type { Basic, Bitmap, Group, Label, Text, Unknown };
//! constructor
Shape()
: m_type(Unknown)
, m_box()
, m_origin()
, m_style()
, m_patternId(-1)
, m_shape()
, m_isLine(false)
, m_id(-1)
, m_nextId(-1)
, m_font()
, m_paragraph()
, m_textEntry()
, m_labelWidth(0)
, m_childList()
, m_bitmapIdList()
, m_bitmapDimensionList()
, m_isSent(false)
{
}
//! return the shape bdbox
MWAWBox2f getBdBox() const
{
return m_type==Basic ? m_shape.getBdBox() : m_box;
}
//! translate a shape
void translate(MWAWVec2f const &dir)
{
if (m_type==Basic)
m_shape.translate(dir);
m_box=MWAWBox2f(m_box[0]+dir, m_box[1]+dir);
m_origin=m_origin+dir;
}
//! transform a shape
void transform(float rotate, bool flipX, MWAWVec2f const ¢er)
{
if (rotate<0 || rotate>0) {
if (m_type==Basic) {
m_shape=m_shape.rotate(rotate, center);
m_box=m_shape.getBdBox();
}
else {
if (m_type==Text) // the textbox is the rotated textbox, so first compute the normal box
m_box=rotateBox(m_box, -m_style.m_rotate*float(M_PI/180.), m_box.center());
if (m_type!=Label) // the label seems always horizontal
m_style.m_rotate+=rotate;
float angle=rotate*float(M_PI/180.);
m_box=rotateBox(m_box, angle, center);
MWAWVec2f decal=center-MWAWVec2f(std::cos(angle)*center[0]-std::sin(angle)*center[1],
std::sin(angle)*center[0]+std::cos(angle)*center[1]);
m_origin=MWAWVec2f(std::cos(angle)*m_origin[0]-std::sin(angle)*m_origin[1],
std::sin(angle)*m_origin[0]+std::cos(angle)*m_origin[1])+decal;
if (m_type==Text) // now, we can compute the final rotated textbox
m_box=rotateBox(m_box, m_style.m_rotate*float(M_PI/180.), m_box.center());
}
}
if (flipX) {
if (m_type==Basic) {
m_shape.scale(MWAWVec2f(-1,1));
m_shape.translate(MWAWVec2f(2*center[0],0));
}
m_style.m_flip[0]=!m_style.m_flip[0];
m_box=MWAWBox2f(MWAWVec2f(2*center[0]-m_box[1][0],m_box[0][1]), MWAWVec2f(2*center[0]-m_box[0][0],m_box[1][1]));
m_origin=MWAWVec2f(2*center[0]-m_origin[0],m_origin[1]);
}
}
//! returns the rotation of a box
static MWAWBox2f rotateBox(MWAWBox2f const &box, float angle, MWAWVec2f const ¢er)
{
MWAWVec2f decal=center-MWAWVec2f(std::cos(angle)*center[0]-std::sin(angle)*center[1],
std::sin(angle)*center[0]+std::cos(angle)*center[1]);
MWAWBox2f fBox;
for (int i=0; i < 4; ++i) {
MWAWVec2f pt=MWAWVec2f(box[i%2][0],box[i/2][1]);
pt = MWAWVec2f(std::cos(angle)*pt[0]-std::sin(angle)*pt[1],
std::sin(angle)*pt[0]+std::cos(angle)*pt[1])+decal;
if (i==0) fBox=MWAWBox2f(pt,pt);
else fBox=fBox.getUnion(MWAWBox2f(pt,pt));
}
return fBox;
}
//! the graphic type
Type m_type;
//! the shape bdbox
MWAWBox2f m_box;
//! the shape origin
MWAWVec2f m_origin;
//! the graphic style
MWAWGraphicStyle m_style;
//! the pattern id
int m_patternId;
//! the graphic shape ( for basic geometric form )
MWAWGraphicShape m_shape;
//! flag to know if the shape is a line
bool m_isLine;
//! the shape id
int m_id;
//! the following id (if set)
int m_nextId;
//! the font ( for a text box)
MWAWFont m_font;
//! the paragraph ( for a text box)
MWAWParagraph m_paragraph;
//! the textbox entry (main text)
MWAWEntry m_textEntry;
//! the 1D label width in point
float m_labelWidth;
//! the child list ( for a group )
std::vector<size_t> m_childList;
//! the list of bitmap id ( for a bitmap)
std::vector<unsigned long> m_bitmapIdList;
//! the list of bitmap dimension ( for a bitmap)
std::vector<MWAWBox2i> m_bitmapDimensionList;
//! a flag used to know if the object is sent to the listener or not
mutable bool m_isSent;
};
////////////////////////////////////////
//! Internal: the state of a MacDraftParser
struct State {
//! constructor
State()
: m_version(0)
, m_patternList()
, m_shapeList()
, m_idToBitmapMap()
{
}
//! returns a pattern if posible
bool getPattern(int id, MWAWGraphicStyle::Pattern &pat)
{
if (m_patternList.empty()) initPatterns();
if (id<=0 || id>=int(m_patternList.size())) {
MWAW_DEBUG_MSG(("MacDraftParserInternal::getPattern: can not find pattern %d\n", id));
return false;
}
pat=m_patternList[size_t(id)];
return true;
}
//! init the patterns list
void initPatterns();
//! the file version
int m_version;
//! the patterns list
std::vector<MWAWGraphicStyle::Pattern> m_patternList;
//! the shapes list
std::vector<Shape> m_shapeList;
//! map id to bitmap file data
std::map<unsigned long, BitmapFileData> m_idToBitmapMap;
};
void State::initPatterns()
{
if (!m_patternList.empty()) return;
for (int i=0; i<64; ++i) {
static uint16_t const patterns[] = {
0x0, 0x0, 0x0, 0x0, 0x40, 0x400, 0x10, 0x100, 0x8040, 0x2010, 0x804, 0x201, 0x102, 0x408, 0x1020, 0x4080,
0x0, 0x0, 0x0, 0x0, 0x842, 0x90, 0x440, 0x1001, 0xe070, 0x381c, 0xe07, 0x83c1, 0x8307, 0xe1c, 0x3870, 0xe0c1,
0x8000, 0x0, 0x800, 0x0, 0x42a, 0x4025, 0x251, 0x2442, 0x4422, 0x88, 0x4422, 0x88, 0x1122, 0x4400, 0x1122, 0x4400,
0x8000, 0x800, 0x8000, 0x800, 0x4aa4, 0x8852, 0x843a, 0x4411, 0x8844, 0x2211, 0x8844, 0x2211, 0x1122, 0x4488, 0x1122, 0x4488,
0x8800, 0x2200, 0x8800, 0x2200, 0x4cd2, 0x532d, 0x9659, 0x46b3, 0x99cc, 0x6633, 0x99cc, 0x6633, 0x3366, 0xcc99, 0x3366, 0xcc99,
0x8822, 0x8822, 0x8822, 0x8822, 0xdbbe, 0xedbb, 0xfeab, 0xbeeb, 0xcc00, 0x0, 0x3300, 0x0, 0x101, 0x1010, 0x101, 0x1010,
0xaa55, 0xaa55, 0xaa55, 0xaa55, 0xf7bd, 0xff6f, 0xfbbf, 0xeffe, 0x2040, 0x8000, 0x804, 0x200, 0x40a0, 0x0, 0x40a, 0x0,
0x77dd, 0x77dd, 0x77dd, 0x77dd, 0x8244, 0x3944, 0x8201, 0x101, 0xff00, 0x0, 0xff00, 0x0, 0x8888, 0x8888, 0x8888, 0x8888,
0xffff, 0xffff, 0xffff, 0xffff, 0x8142, 0x3c18, 0x183c, 0x4281, 0xb130, 0x31b, 0xb8c0, 0xc8d, 0x6c92, 0x8282, 0x4428, 0x1000,
0xff80, 0x8080, 0xff80, 0x8080, 0x8142, 0x2418, 0x1020, 0x4080, 0xff80, 0x8080, 0xff08, 0x808, 0x8080, 0x413e, 0x808, 0x14e3,
0xff88, 0x8888, 0xff88, 0x8888, 0xff80, 0x8080, 0x8080, 0x8080, 0xbf00, 0xbfbf, 0xb0b0, 0xb0b0, 0xaa00, 0x8000, 0x8800, 0x8000,
0xaa44, 0xaa11, 0xaa44, 0xaa11, 0x8244, 0x2810, 0x2844, 0x8201, 0x8, 0x142a, 0x552a, 0x1408, 0x1038, 0x7cfe, 0x7c38, 0x1000,
0x1020, 0x54aa, 0xff02, 0x408, 0x8080, 0x8080, 0x8094, 0xaa55, 0x804, 0x2a55, 0xff40, 0x2010, 0x7789, 0x8f8f, 0x7798, 0xf8f8,
0x8814, 0x2241, 0x8800, 0xaa00, 0x77eb, 0xddbe, 0x77ff, 0x55ff, 0x1022, 0x408a, 0x4022, 0x108a, 0xefdd, 0xbf75, 0xbfdd, 0xef75,
0x9f90, 0x909f, 0xf909, 0x9f9, 0xf078, 0x2442, 0x870f, 0x1221, 0xfe82, 0xfeee, 0xef28, 0xefee, 0xf9fc, 0x664f, 0x9f3f, 0x66f3,
0xaf5f, 0xaf5f, 0xd0b, 0xd0b, 0xa011, 0xa1c, 0x2844, 0x82c1, 0xf0f0, 0xf0f0, 0xf0f, 0xf0f, 0xc864, 0x3219, 0x9923, 0x468c
};
MWAWGraphicStyle::Pattern pat;
pat.m_dim=MWAWVec2i(8,8);
pat.m_data.resize(8);
pat.m_colors[0]=MWAWColor::white();
pat.m_colors[1]=MWAWColor::black();
uint16_t const *patPtr=&patterns[4*i];
for (size_t j=0; j<8; j+=2, ++patPtr) {
pat.m_data[j]=uint8_t((*patPtr)>>8);
pat.m_data[j+1]=uint8_t((*patPtr)&0xFF);
}
m_patternList.push_back(pat);
}
}
////////////////////////////////////////
//! Internal: the subdocument of a MacDraftParser
class SubDocument final : public MWAWSubDocument
{
public:
SubDocument(MacDraftParser &pars, MWAWInputStreamPtr const &input, int zoneId)
: MWAWSubDocument(&pars, input, MWAWEntry())
, m_id(zoneId)
{
}
//! 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;
return false;
}
//! the parser function
void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
protected:
//! the subdocument id
int m_id;
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(("MacDraftParserInternal::SubDocument::parse: no listener\n"));
return;
}
auto *parser=dynamic_cast<MacDraftParser *>(m_parser);
if (!parser) {
MWAW_DEBUG_MSG(("MacDraftParserInternal::SubDocument::parse: no parser\n"));
return;
}
long pos = m_input->tell();
parser->sendText(m_id);
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
MacDraftParser::MacDraftParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
: MWAWGraphicParser(input, rsrcParser, header)
, m_state()
{
init();
}
MacDraftParser::~MacDraftParser()
{
}
void MacDraftParser::init()
{
resetGraphicListener();
setAsciiName("main-1");
m_state.reset(new MacDraftParserInternal::State);
getPageSpan().setMargins(0.1);
}
////////////////////////////////////////////////////////////
// the parser
////////////////////////////////////////////////////////////
void MacDraftParser::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);
// reimplement me, ie. we may loose some shape if the chain is breaked
for (size_t i=0; i<m_state->m_shapeList.size(); ++i) {
MacDraftParserInternal::Shape const &shape=m_state->m_shapeList[i];
if (shape.m_isSent) continue;
send(shape);
if (shape.m_nextId>0 && shape.m_nextId>int(i))
i=size_t(shape.m_nextId-1);
}
}
ascii().reset();
}
catch (...) {
MWAW_DEBUG_MSG(("MacDraftParser::parse: exception catched when parsing\n"));
ok = false;
}
resetGraphicListener();
if (!ok) throw(libmwaw::ParseException());
}
////////////////////////////////////////////////////////////
// create the document
////////////////////////////////////////////////////////////
void MacDraftParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface)
{
if (!documentInterface) return;
if (getGraphicListener()) {
MWAW_DEBUG_MSG(("MacDraftParser::createDocument: listener already exist\n"));
return;
}
// create the page list
MWAWPageSpan ps(getPageSpan());
ps.setPageSpan(1);
std::vector<MWAWPageSpan> pageList(1,ps);
MWAWGraphicListenerPtr listen(new MWAWGraphicListener(*getParserState(), pageList, documentInterface));
setGraphicListener(listen);
listen->startDocument();
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool MacDraftParser::createZones()
{
if (!readDocHeader())
return false;
MWAWInputStreamPtr input = getInput();
// first the list of patterns (often empty)
while (readPattern()) {
}
long pos=input->tell();
auto fSz=long(input->readULong(2));
if (fSz==0) {
ascii().addPos(pos);
ascii().addNote("Patterns[end]:");
}
else {
MWAW_DEBUG_MSG(("MacDraftParser::createZones: can not find end patterns zone\n"));
ascii().addPos(pos);
ascii().addNote("###");
input->seek(pos,librevenge::RVNG_SEEK_SET);
}
// bitmap data
if (m_state->m_version==1) {
while (readBitmapData()) {
}
pos=input->tell();
fSz=long(input->readULong(2));
if (fSz==0) {
ascii().addPos(pos);
ascii().addNote("BitmapData[end]:");
}
else {
MWAW_DEBUG_MSG(("MacDraftParser::createZones: can not find end bitmap data zone\n"));
ascii().addPos(pos);
ascii().addNote("###");
input->seek(pos,librevenge::RVNG_SEEK_SET);
}
}
// second the list of objects (can be empty)
while (readObject()) {
}
pos=input->tell();
fSz=long(input->readULong(2));
if (fSz==0) {
ascii().addPos(pos);
ascii().addNote("Object[end]:");
}
else {
MWAW_DEBUG_MSG(("MacDraftParser::createZones: can not find end object zone\n"));
ascii().addPos(pos);
ascii().addNote("###");
input->seek(pos,librevenge::RVNG_SEEK_SET);
}
// never seems
for (int i=0; i<2; ++i) {
while (!input->isEnd()) {
pos=input->tell();
fSz=long(input->readULong(2));
if (!input->checkPosition(pos+2+fSz)) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
break;
}
if (fSz==0) {
ascii().addPos(pos);
ascii().addNote("_");
break;
}
else {
MWAW_DEBUG_MSG(("MacDraftParser::createZones: find some unknown zone\n"));
ascii().addPos(pos);
ascii().addNote(i==0 ? "Entries(ZoneA):" : "Entries(ZoneB):");
input->seek(pos+2+fSz,librevenge::RVNG_SEEK_SET);
}
}
}
if (m_state->m_version==1) {
if (input->isEnd())
return true;
MWAW_DEBUG_MSG(("MacDraftParser::createZones: find some extra zone\n"));
ascii().addPos(pos);
ascii().addNote("Entries(Extra):###");
return !m_state->m_shapeList.empty();
}
// bitmap data
while (readBitmapData()) {
}
pos=input->tell();
fSz=long(input->readULong(2));
if (fSz==0) {
ascii().addPos(pos);
ascii().addNote("BitmapData[end]:");
}
else {
MWAW_DEBUG_MSG(("MacDraftParser::createZones: can not find end bitmap data zone\n"));
ascii().addPos(pos);
ascii().addNote("###");
input->seek(pos,librevenge::RVNG_SEEK_SET);
}
// modified patterns list
while (readPattern()) {
}
pos=input->tell();
fSz=long(input->readULong(2));
if (fSz==0) {
ascii().addPos(pos);
ascii().addNote("Patterns[end]:");
}
else {
MWAW_DEBUG_MSG(("MacDraftParser::createZones: can not find end of pattern zone\n"));
ascii().addPos(pos);
ascii().addNote("Entries(Extra):###");
return !m_state->m_shapeList.empty();
}
if (input->isEnd())
return !m_state->m_shapeList.empty();
pos=input->tell();
fSz=long(input->readULong(2));
input->seek(pos,librevenge::RVNG_SEEK_SET);
if (fSz==0) {
ascii().addPos(pos);
ascii().addNote("_");
return true;
}
else if (fSz==0x78) {
if (!readPrintInfo()) {
ascii().addPos(pos);
ascii().addNote("Entries(PrintInfo):#");
}
input->seek(pos+2+fSz,librevenge::RVNG_SEEK_SET);
}
if (!input->isEnd()) {
MWAW_DEBUG_MSG(("MacDraftParser::createZones: find some extra zone\n"));
ascii().addPos(input->tell());
ascii().addNote("Entries(Extra):###");
}
return true;
}
bool MacDraftParser::readZone()
{
MWAWInputStreamPtr input = getInput();
if (input->isEnd())
return false;
long pos=input->tell();
auto fSz=long(input->readULong(2));
if (fSz==0) {
ascii().addPos(pos);
ascii().addNote("_");
return true;
}
input->seek(pos,librevenge::RVNG_SEEK_SET);
if (fSz==0x1e)
return readPattern();
if (fSz==0x78 && readPrintInfo())
return true;
if (!input->checkPosition(pos+2+fSz)) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
return false;
}
ascii().addPos(pos);
ascii().addNote("Entries(UnknZone):");
input->seek(pos+2+fSz,librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// read an object
////////////////////////////////////////////////////////////
bool MacDraftParser::readObject()
{
MWAWInputStreamPtr input = getInput();
if (input->isEnd())
return false;
long pos=input->tell();
auto fSz=long(input->readULong(2));
if (fSz==0x1a) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
return readLabel();
}
long endPos=pos+2+fSz;
if (fSz<0x20 || !input->checkPosition(endPos)) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
return false;
}
libmwaw::DebugStream f;
f << "Entries(Object):";
int val;
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readULong(4));
if (val) f << "id" << i << "=" << std::hex << val << std::dec << ",";
}
size_t shapeId= m_state->m_shapeList.size();
MacDraftParserInternal::Shape shape;
shape.m_id=static_cast<int>(shapeId);
shape.m_nextId=shape.m_id+1; // default value
unsigned long flag=input->readULong(4);
switch ((flag>>3)&7) {
case 0:
break;
case 1:
shape.m_style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
f << "->,";
break;
case 2:
shape.m_style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain();
f << "<-,";
break;
case 3:
shape.m_style.m_arrows[0]=shape.m_style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
f << "<->,";
break;
case 4:
shape.m_style.m_arrows[0]=shape.m_style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
f << "<->+length,";
break;
default:
f << "##arrow=" << ((flag>>3)&7) << ",";
}
switch ((flag>>9)&0x3) {
case 0:
f << "line[inside],";
break;
case 1: // centered
break;
case 2:
f << "line[outside],";
break;
default:
f << "line[pos]=##3,";
}
shape.m_patternId=(flag>>11)&0x3f;
if (shape.m_patternId)
f << "pat=" << shape.m_patternId << ",";
else
shape.m_style.m_surfaceOpacity=0;
if (flag&0x40) {
shape.m_style.m_lineWidth=0.; // or maybe dotted
f << "hairline,";
}
else {
shape.m_style.m_lineWidth=float((flag>>23)&7)+1;
f << "width=" << ((flag>>23)&7)+1 << ",";
}
if (flag&0x80000000)
f << "select,";
flag &= 0x7c7e0187;
if (flag>>16) f << "fl0[h]=" << std::hex << (flag>>16) << std::dec << ",";
if (flag&0xFFFF) f << "fl0[l]=" << std::hex << (flag&0xFFFF) << std::dec << ",";
val=static_cast<int>(input->readULong(2));
if (val&0x8000) f << "lock,";
val &= 0x7fff;
if (val) f << "fl1=" << std::hex << val << std::dec << ",";
val=static_cast<int>(input->readULong(2));
bool flipX=false;
float rotate=0;
if (val&0xfc00)
f << "rot[h]=" << std::hex << (val>>10) << std::dec << ",";
if (val&0x200) {
flipX=true;
f << "flipX,";
}
val &=0x1ff;
if (val) {
rotate=float(val);
f << "rot=" << val << ",";
}
float dim[4];
for (auto &d : dim) d=float(input->readLong(2))/8.f;
shape.m_box=MWAWBox2f(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
f << "dim=" << shape.m_box << ",";
for (int i=0; i<2; ++i) dim[i]=float(input->readLong(2))/8.f;
MWAWVec2f origin=MWAWVec2f(dim[1],dim[0]);
f << "orig=" << origin << ",";
switch (fSz) {
case 0x28:
case 0x2c:
case 0x30:
case 0x38: {
shape.m_type=MacDraftParserInternal::Shape::Basic;
val=static_cast<int>(input->readULong(4));
if (val) f << "id?=" << std::hex << val << std::dec << ",";
for (auto &d : dim) d=float(input->readLong(2))/8.f;
MWAWBox2f box(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
f << "dim1=" << box << ",";
if (fSz==0x28) {
if (flag&0x8000000) {
shape.m_shape=MWAWGraphicShape::circle(box);
f << "ellipse,";
}
else {
shape.m_shape=MWAWGraphicShape::rectangle(box);
f << "rect,";
}
break;
}
else if (fSz==0x2c) {
shape.m_shape=MWAWGraphicShape::rectangle(box);
f << "rectOval,";
}
else if (fSz==0x30) {
if ((flag&0x28000000)==0x28000000) {
shape.m_shape=MWAWGraphicShape::circle(box);
f << "circle,";
}
else if ((flag&0x8000000)==0x8000000) {
shape.m_type=MacDraftParserInternal::Shape::Bitmap;
f << "bitmap,";
}
else {
shape.m_type=MacDraftParserInternal::Shape::Group;
f << "group,";
}
}
else if (fSz==0x38)
f << "pie,";
else {
// must not appear
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("MacDraftParser::readObject: find unexpected data\n"));
first=false;
}
f << "Bitmap###,";
}
if (fSz==0x30) {
val=static_cast<int>(input->readULong(4));
if (val)
f << "id2=" << std::hex << val << std::dec << ",";
}
else if (fSz==0x38) {
int fileAngle[2];
for (auto &angle : fileAngle) angle=static_cast<int>(input->readLong(2));
f << "angle=" << fileAngle[0] << "x" << fileAngle[0]+fileAngle[1] << ",";
if (fileAngle[1]<0) {
fileAngle[0]+=fileAngle[1];
fileAngle[1]*=-1;
}
int angle[2] = { 90-fileAngle[0]-fileAngle[1], 90-fileAngle[0] };
if (angle[1]>360) {
int numLoop=int(angle[1]/360)-1;
angle[0]-=numLoop*360;
angle[1]-=numLoop*360;
while (angle[1] > 360) {
angle[0]-=360;
angle[1]-=360;
}
}
if (angle[0] < -360) {
int numLoop=int(angle[0]/360)+1;
angle[0]-=numLoop*360;
angle[1]-=numLoop*360;
while (angle[0] < -360) {
angle[0]+=360;
angle[1]+=360;
}
}
MWAWVec2f axis = 0.5f*box.size();
// 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] = { axis[0] *std::cos(ang), -axis[1] *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];
}
MWAWVec2f center = box.center();
MWAWBox2f realBox(MWAWVec2f(center[0]+minVal[0],center[1]+minVal[1]),
MWAWVec2f(center[0]+maxVal[0],center[1]+maxVal[1]));
shape.m_box=MWAWBox2f(MWAWVec2f(shape.m_box[0])+realBox[0],MWAWVec2f(shape.m_box[0])+realBox[1]);
if (shape.m_patternId!=0)
shape.m_shape = MWAWGraphicShape::pie(realBox, box, MWAWVec2f(float(angle[0]),float(angle[1])));
else
shape.m_shape = MWAWGraphicShape::arc(realBox, box, MWAWVec2f(float(angle[0]),float(angle[1])));
for (int i=0; i<2; ++i) { // always 0
val=static_cast<int>(input->readULong(2));
if (val) f << "f" << i+2 << "=" << std::hex << val << std::dec << ",";
}
for (int i=0; i<2; ++i) dim[i]=float(input->readLong(2))/8.f;
f << "sz=" << MWAWVec2f(dim[1],dim[0]) << ",";
}
for (int i=0; i<2; ++i) dim[i]=float(input->readLong(2))/8.f;
if (fSz==0x2c)
shape.m_shape.m_cornerWidth=MWAWVec2f(dim[1]/2.f,dim[0]/2.f);
f << "corner=" << MWAWVec2f(dim[1],dim[0]) << ",";
if (shape.m_type!=MacDraftParserInternal::Shape::Group && shape.m_type!=MacDraftParserInternal::Shape::Bitmap)
break;
if (input->tell()!=endPos)
ascii().addDelimiter(input->tell(),'|');
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos,librevenge::RVNG_SEEK_SET);
if (shape.m_type==MacDraftParserInternal::Shape::Bitmap) {
while (!input->isEnd()) {
pos=input->tell();
if (input->readULong(2)==0) {
ascii().addPos(pos);
ascii().addNote("Bitmap[end]");
break;
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
if (!readBitmapDefinition(shape)) {
MWAW_DEBUG_MSG(("MacDraftParser::readObject: can not find end of bitmap\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote("Bitmap###");
break;
}
}
m_state->m_shapeList.push_back(shape);
return true;
}
m_state->m_shapeList.push_back(shape);
while (!input->isEnd()) {
pos=input->tell();
if (input->readULong(2)==0) {
ascii().addPos(pos);
ascii().addNote("Object[endGroup]");
break;
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
size_t cId=m_state->m_shapeList.size();
if (!readObject()) {
MWAW_DEBUG_MSG(("MacDraftParser::readObject: can not find end of group\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
m_state->m_shapeList[shapeId].m_childList.push_back(size_t(cId));
}
m_state->m_shapeList[shapeId].m_nextId=static_cast<int>(m_state->m_shapeList.size());
// times to apply transformation on child
if (origin!=MWAWVec2f(0,0)) {
for (int i=m_state->m_shapeList[shapeId].m_id+1; i<m_state->m_shapeList[shapeId].m_nextId; ++i)
m_state->m_shapeList[size_t(i)].translate(origin);
}
if (rotate<0 || rotate>0 || flipX) {
MWAWVec2f center=origin+box.center();
for (int i=m_state->m_shapeList[shapeId].m_id+1; i<m_state->m_shapeList[shapeId].m_nextId; ++i)
m_state->m_shapeList[size_t(i)].transform(rotate,flipX,center);
}
return true;
}
case 0x32: {
f << "line,";
val=static_cast<int>(input->readULong(4));
if (val) f << "pat[id]=" << std::hex << val << ",";
for (int i=0; i<2; ++i) { // 0
val=static_cast<int>(input->readULong(2));
if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
}
for (int i=0; i<2; ++i) dim[i]=float(input->readLong(2))/8.f;
if (dim[0]<0 || dim[0]>0 || dim[1]<0 || dim[1]>0)
f << "center=" << MWAWVec2f(dim[1],dim[0]) << ",";
val=static_cast<int>(input->readLong(2)); // always 2
if (val != 1)
f << "f2=" << val << ",";
for (auto &d : dim) d=float(input->readLong(2))/8.f;
MWAWBox2f box(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
f << "line=" << box << ",";
shape.m_type=MacDraftParserInternal::Shape::Basic;
shape.m_shape=MWAWGraphicShape::line(box[0], box[1]);
shape.m_isLine=true;
break;
}
default: {
int type=(flag>>26)&0x1f;
if (type==3) {
f << "textbox,";
shape.m_type=MacDraftParserInternal::Shape::Text;
shape.m_style.m_lineWidth=0;
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readLong(2));
if (val)
f << "f" << i << "=" << val << ",";
}
for (auto &d : dim) d=float(input->readLong(2))/8.f;
MWAWBox2f box(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
shape.m_box=box;
f << "box=" << box << ",";
f << "unkn=[";
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readULong(2));
if (val)
f << std::hex << val << std::dec << ",";
else
f << "_,";
}
f << "],";
MWAWFont &font=shape.m_font;
val=static_cast<int>(input->readULong(1));
uint32_t flags=0;
if (val&0x1) flags |= MWAWFont::boldBit;
if (val&0x2) flags |= MWAWFont::italicBit;
if (val&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
if (val&0x8) flags |= MWAWFont::embossBit;
if (val&0x10) flags |= MWAWFont::shadowBit;
if (val>>5) f << "#flag[font]=" << (val>>5) << ",";
val=static_cast<int>(input->readULong(1));
if (val) f << "flag1[font]=" << val << ",";
font.setId(static_cast<int>(input->readULong(1)));
val=static_cast<int>(input->readULong(1));
if (val)
font.setSize(float(val));
font.setFlags(flags);
f << "font=[" << font.getDebugString(getParserState()->m_fontConverter) << "],";
val=static_cast<int>(input->readLong(1));
switch (val) {
case -1:
shape.m_paragraph.m_justify = MWAWParagraph::JustificationRight;
f << "right,";
break;
case 0:
break;
case 1:
shape.m_paragraph.m_justify = MWAWParagraph::JustificationCenter;
f << "center,";
break;
default:
MWAW_DEBUG_MSG(("MacDraftParser::readObject: find unknown align\n"));
f << "##align=" << val << ",";
break;
}
val=static_cast<int>(input->readLong(1)); // find 2
if (val) f << "f4=" << val << ",";
val=static_cast<int>(input->readLong(1));
switch (val) {
case 0:
break;
case 1:
shape.m_paragraph.setInterline(1.5, librevenge::RVNG_PERCENT);
f << "interline=150%,";
break;
case 2:
shape.m_paragraph.setInterline(2, librevenge::RVNG_PERCENT);
f << "interline=200%,";
break;
default:
MWAW_DEBUG_MSG(("MacDraftParser::readObject: find unknown interline\n"));
f << "##interline=" << val << ",";
break;
}
val=static_cast<int>(input->readLong(1));
switch (val) { // the string must already be in lowercase, ..., so
case 0:
break;
case 1:
f << "upper,";
break;
case 2:
f << "lower,";
break;
case 3:
f << "title,";
break;
default:
MWAW_DEBUG_MSG(("MacDraftParser::readObject: find unknown position\n"));
f << "##position=" << val << ",";
break;
}
val=static_cast<int>(input->readLong(2));
if (val!=1) // find 1 or 3
f << "f5=" << val << ",";
auto N=static_cast<int>(input->readULong(2));
if (input->tell()+4+N>endPos) {
MWAW_DEBUG_MSG(("MacDraftParser::readObject: can not read the number of char \n"));
f << "##N=" << N << ",";
break;
}
val=static_cast<int>(input->readLong(2));
if (val!=1) // find always 1
f << "f6=" << val << ",";
std::string text("");
shape.m_textEntry.setBegin(input->tell());
shape.m_textEntry.setLength(N);
for (int i=0; i<N; ++i) text+=char(input->readULong(1));
f << text << ",";
val=static_cast<int>(input->readULong(2));
if (val)
f << "fl3=" << std::hex << val << std::dec << ",";
break;
}
val=static_cast<int>(input->readULong(4));
if (val) f << "pat[id]=" << std::hex << val << ",";
for (auto &d: dim) d=float(input->readLong(2))/8.f;
auto N=static_cast<int>(input->readULong(2));
if (N*4+46==fSz) {
f << "poly, box=" << MWAWBox2f(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2])) << ",";
shape.m_type=MacDraftParserInternal::Shape::Basic;
shape.m_shape.m_type = MWAWGraphicShape::Polygon;
std::vector<MWAWVec2f> &vertices=shape.m_shape.m_vertices;
MWAWBox2f box;
f << "pts=[";
for (int i=0; i<N; ++i) {
float coord[2];
for (auto &c : coord) c=float(input->readLong(2))/8.f;
MWAWVec2f point=MWAWVec2f(coord[1],coord[0]);
vertices.push_back(point);
if (i==0)
box=MWAWBox2f(point, point);
else
box=box.getUnion(MWAWBox2f(point, point));
f << point << ",";
}
shape.m_shape.m_bdBox=shape.m_shape.m_formBox=box;
f << "],";
break;
}
input->seek(-10, librevenge::RVNG_SEEK_CUR);
// happens in a converted file: a quite horrible file
MWAW_DEBUG_MSG(("MacDraftParser::readObject: find unknown data\n"));
f << "UNKN###,";
break;
}
}
shape.transform(rotate, flipX, shape.getBdBox().center());
shape.translate(origin);
m_state->m_shapeList.push_back(shape);
if (input->tell()!=endPos)
ascii().addDelimiter(input->tell(),'|');
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos,librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// read a bitmap
////////////////////////////////////////////////////////////
bool MacDraftParser::readBitmapData()
{
MWAWInputStreamPtr input = getInput();
if (input->isEnd())
return false;
long pos=input->tell();
auto fSz=long(input->readULong(2));
long endPos=pos+2+fSz;
if (fSz<28 || !input->checkPosition(endPos)) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
return false;
}
MacDraftParserInternal::BitmapFileData data;
libmwaw::DebugStream f;
f << "Entries(BitmapData):";
auto val=long(input->readULong(4));
if (val)
f << "id[next]=" << std::hex << val << std::dec << ",";
val=long(input->readULong(2));
if (val) f << "fl=" << std::hex << val << std::dec << ",";
data.m_id=input->readULong(4);
f << "id=" << std::hex << data.m_id << std::dec << ",";
val=input->readLong(2); // 1 or 2
if (val) f << "f0=" << val << ",";
val=input->readLong(2); // 0
if (val) f << "f1=" << val << ",";
f << "id2=" << std::hex << input->readULong(4) << std::dec << ","; // 80000000+id
data.m_rowSize=static_cast<int>(input->readULong(2)); // 2|4|72 : number of bytes by row
if (data.m_rowSize) f << "row[size]=" << data.m_rowSize << ",";
int dim[4];
for (auto &d : dim) d=static_cast<int>(input->readLong(2));
data.m_dimension=MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2]));
f << "dim=" << data.m_dimension << ",";
data.m_entry.setBegin(input->tell());
data.m_entry.setEnd(endPos);
if (!data.ok()) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
return false;
}
if (data.m_rowSize*data.m_dimension.size()[1]+28!=fSz) {
MWAW_DEBUG_MSG(("MacDraftParser::readBitmapData: the bitmap size seems odd\n"));
f << "###sz,";
}
if (m_state->m_idToBitmapMap.find(data.m_id)!=m_state->m_idToBitmapMap.end()) {
MWAW_DEBUG_MSG(("MacDraftParser::readBitmapData: the id is already defined\n"));
f << "###id,";
}
else
m_state->m_idToBitmapMap[data.m_id]=data;
if (data.m_entry.length())
ascii().skipZone(data.m_entry.begin(), data.m_entry.end()-1);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos,librevenge::RVNG_SEEK_SET);
return true;
}
bool MacDraftParser::readBitmapDefinition(MacDraftParserInternal::Shape &bitmap)
{
MWAWInputStreamPtr input = getInput();
if (input->isEnd())
return false;
long pos=input->tell();
auto fSz=long(input->readULong(2));
long endPos=pos+2+fSz;
if (fSz!=0x3e || !input->checkPosition(endPos)) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
return false;
}
libmwaw::DebugStream f;
f << "Entries(BitmapDef):";
int val;
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readULong(4));
if (val) f << "id" << i << "=" << std::hex << val << std::dec << ",";
}
unsigned long flag=input->readULong(4);
if (flag>>16) f << "fl0[h]=" << std::hex << (flag>>16) << std::dec << ",";
if (flag&0xFFFF) f << "fl0[l]=" << std::hex << (flag&0xFFFF) << std::dec << ",";
val=static_cast<int>(input->readULong(2));
if (val&0x8000) f << "lock,";
val &= 0x7fff;
if (val) f << "fl1=" << std::hex << val << std::dec << ",";
val=static_cast<int>(input->readULong(2));
if (val&0xfc00)
f << "rot[h]=" << std::hex << (val>>10) << std::dec << ",";
if (val&0x200) {
f << "flipX,";
}
val &=0x1ff;
if (val)
f << "rot=" << float(val) << ",";
float dim[4];
for (auto &d : dim) d=float(input->readLong(2))/8.f;
f << "dim=" << MWAWBox2f(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2])) << ",";
float coord[2];
for (auto &c : coord) c=float(input->readLong(2))/8.f;
f << "orig=" << MWAWVec2f(coord[1],coord[0]) << ",";
val=static_cast<int>(input->readULong(4));
if (val) f << "id?=" << std::hex << val << std::dec << ",";
for (auto &d : dim) d=float(input->readLong(2))/8.f;
f << "dim1=" << MWAWBox2f(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2])) << ",";
unsigned long id=input->readULong(4);
bitmap.m_bitmapIdList.push_back(id);
f << "id[data]=" << std::hex << id << std::dec << ",";
val=static_cast<int>(input->readLong(2)); // 0|1
if (val) f << "g0=" << val << ",";
int iDim[4];
for (auto &d : iDim) d=int(input->readLong(2));
MWAWBox2i box(MWAWVec2i(iDim[1],iDim[0]), MWAWVec2i(iDim[3],iDim[2]));
bitmap.m_bitmapDimensionList.push_back(box);
f << "dim2=" << box << ",";
for (auto &d : dim) d=float(input->readLong(2));
f << "dim3=" << MWAWBox2f(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2])) << ",";
if (input->tell()!=endPos)
ascii().addDelimiter(input->tell(),'|');
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos,librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// read a pattern
////////////////////////////////////////////////////////////
bool MacDraftParser::readPattern()
{
MWAWInputStreamPtr input = getInput();
if (input->isEnd())
return false;
long pos=input->tell();
auto fSz=long(input->readULong(2));
long endPos=pos+2+fSz;
if (fSz!=0x1e || !input->checkPosition(endPos)) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
return false;
}
libmwaw::DebugStream f;
f << "Entries(Pattern):";
auto val=long(input->readULong(4));
if (val) f << "id=" << std::hex << val << std::dec << ",";
auto id=static_cast<int>(input->readULong(2));
if (id==0xf0)
f << "default,";
else if (id<64)
f << "id=" << id << ",";
else {
MWAW_DEBUG_MSG(("MacDraftParser::readPattern: find odd id\n"));
f << "##id=" << id << ",";
}
val=long(input->readULong(4));
if (val) f << "id[prev?]=" << std::hex << val << std::dec << ",";
for (int i=0; i<2; ++i) { //3,3 | 4,4 | 5,5
val=input->readLong(2);
if (val) f << "f" << i << "=" << val << ",";
}
MWAWGraphicStyle::Pattern pat;
pat.m_dim=MWAWVec2i(8,8);
pat.m_data.resize(8);
pat.m_colors[0]=MWAWColor::white();
pat.m_colors[1]=MWAWColor::black();
for (auto &data : pat.m_data) data=uint8_t(input->readULong(1));
f << "orig=" << pat << ",";
for (auto &data : pat.m_data) data=uint8_t(input->readULong(1));
f << "final=" << pat << ",";
if (id>=0 && id<64) {
m_state->initPatterns();
if (id<static_cast<int>(m_state->m_patternList.size()))
m_state->m_patternList[size_t(id)]=pat;
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos,librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// read an label
////////////////////////////////////////////////////////////
bool MacDraftParser::readLabel()
{
MWAWInputStreamPtr input = getInput();
if (input->isEnd())
return false;
long pos=input->tell();
auto fSz=long(input->readULong(2));
long endPos=pos+2+fSz;
if (fSz!=0x1a || !input->checkPosition(endPos)) {
input->seek(pos,librevenge::RVNG_SEEK_SET);
return false;
}
size_t shapeId= m_state->m_shapeList.size();
MacDraftParserInternal::Shape shape;
shape.m_id=static_cast<int>(shapeId);
shape.m_nextId=shape.m_id+1;
shape.m_type=MacDraftParserInternal::Shape::Label;
shape.m_style.m_lineWidth=0;
shape.m_style.setSurfaceColor(MWAWColor::white());
libmwaw::DebugStream f;
f << "Entries(Label):";
int val;
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readULong(4));
if (val) f << "id" << i << "=" << std::hex << val << std::dec << ",";
}
unsigned long flag=input->readULong(4);
MWAWFont &font=shape.m_font;
if ((flag & 0x7F))
font.setSize(float(flag & 0x7F));
font.setId((flag>>8) & 0xFF);
uint32_t flags=0;
if (flag&0x1000000) flags |= MWAWFont::boldBit;
if (flag&0x2000000) flags |= MWAWFont::italicBit;
if (flag&0x4000000) font.setUnderlineStyle(MWAWFont::Line::Simple);
if (flag&0x8000000) flags |= MWAWFont::embossBit;
if (flag&0x10000000) flags |= MWAWFont::shadowBit;
if (flag&static_cast<unsigned long>(0x80000000)) f << "select,";
flag &= static_cast<unsigned long>(0x60FF0080);
if (flag>>16) f << "fl0[h]=" << std::hex << (flag>>16) << std::dec << ",";
if (flag&0xFFFF) f << "fl0[l]=" << std::hex << (flag&0xFFFF) << std::dec << ",";
flag=input->readULong(4);
switch (flag&3) {
case 1:
flags |= MWAWFont::uppercaseBit;
break;
case 2:
flags |= MWAWFont::lowercaseBit;
break;
case 3:
f << "title,";
flags |= MWAWFont::smallCapsBit;
break;
case 0:
default:
break;
}
font.setFlags(flags);
f << "font=[" << font.getDebugString(getParserState()->m_fontConverter) << "],";
switch ((flag>>8)&3) {
case 0:
break;
case 1:
shape.m_paragraph.setInterline(1.5, librevenge::RVNG_PERCENT);
f << "interline=150%,";
break;
case 2:
shape.m_paragraph.setInterline(200, librevenge::RVNG_PERCENT);
f << "interline=200%,";
break;
default:
MWAW_DEBUG_MSG(("MacDraftParser::readLabel: find unknown interline\n"));
f << "##interline=3,";
break;
}
switch ((flag>>24)) {
case 0:
break;
case 0xff:
shape.m_paragraph.m_justify = MWAWParagraph::JustificationRight;
f << "right,";
break;
case 1:
shape.m_paragraph.m_justify = MWAWParagraph::JustificationCenter;
f << "center,";
break;
default:
MWAW_DEBUG_MSG(("MacDraftParser::readLabel: find unknown align\n"));
f << "##align=" << (flag>>24) << ",";
break;
}
flag &= 0xFFFCFC;
if (flag>>16) f << "fl1[h]=" << std::hex << (flag>>16) << std::dec << ",";
if (flag&0xFFFF) f << "fl1[l]=" << std::hex << (flag&0xFFFF) << std::dec << ",";
float dim[4];
for (auto &d : dim) d=float(input->readLong(2))/8.f;
shape.m_box=MWAWBox2f(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
f << shape.m_box << ",";
val=static_cast<int>(input->readULong(2));
if (val!=0x204)
f << "fl2=" << std::hex << val << std::dec << ",";
m_state->m_shapeList.push_back(shape);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos,librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool MacDraftParser::checkHeader(MWAWHeader *header, bool strict)
{
*m_state = MacDraftParserInternal::State();
MWAWInputStreamPtr input = getInput();
if (!input || !input->hasDataFork() || !input->checkPosition(0x270))
return false;
libmwaw::DebugStream f;
f << "FileHeader:";
input->seek(0, librevenge::RVNG_SEEK_SET);
if (input->readULong(2)!=2 || input->readULong(2)!=0 || input->readULong(2)!=2 ||
input->readULong(2)!=0x262 || input->readULong(2)!=0x262)
return false;
int const vers=1;
auto val=static_cast<int>(input->readULong(2));
if (val)
f << "f0=" << val << ",";
f << "id=" << std::hex << input->readULong(4) << std::dec << ",";
for (int i=0; i<2; ++i) {// f0=fc[4c]0, f1=[09]200
val=static_cast<int>(input->readULong(2));
if (val) f << "fl" << i << "=" << std::hex << val << std::dec << ",";
}
f << "id1=" << std::hex << input->readULong(4) << std::dec << ",";
val=static_cast<int>(input->readLong(2)); // always 0
if (val)
f << "f1=" << val << ",";
f << "ids=[";
for (int i=0; i<5; ++i) { // maybe junk
val=static_cast<int>(input->readULong(4));
if (val)
f << std::hex << val << std::dec << ",";
else
f << "_,";
}
f << "],";
val=static_cast<int>(input->readLong(2)); // 0|1
if (val)
f << "f2=" << val << ",";
auto sSz=static_cast<int>(input->readULong(1));
if (sSz>31) {
if (strict)
return false;
MWAW_DEBUG_MSG(("MacDraftParser::checkHeader: string size seems bad\n"));
f << "##sSz=" << sSz << ",";
}
else {
std::string name("");
for (int i=0; i<sSz; ++i) name+=char(input->readULong(1));
f << name << ",";
}
input->seek(80, librevenge::RVNG_SEEK_SET);
val=static_cast<int>(input->readULong(4));
if (val)
f << "id2=" << std::hex << val << std::dec << ",";
ascii().addPos(0);
ascii().addNote(f.str().c_str());
if (strict) {
input->seek(0x270,librevenge::RVNG_SEEK_SET);
for (int i=0; i<10; ++i) {
if (input->isEnd())
break;
long pos=input->tell();
long fSz=static_cast<int>(input->readULong(2));
if (!input->checkPosition(pos+2+fSz))
return false;
input->seek(pos+2+fSz, librevenge::RVNG_SEEK_SET);
}
input->seek(84, librevenge::RVNG_SEEK_SET);
}
setVersion(vers);
m_state->m_version=vers;
if (header)
header->reset(MWAWDocument::MWAW_T_MACDRAFT, vers, MWAWDocument::MWAW_K_DRAW);
return true;
}
////////////////////////////////////////////////////////////
// read the document header
////////////////////////////////////////////////////////////
bool MacDraftParser::readDocHeader()
{
MWAWInputStreamPtr input = getInput();
if (!input->checkPosition(0x262)) {
MWAW_DEBUG_MSG(("MacDraftParser::readDocHeader: the file seems too short\n"));
return false;
}
input->seek(84,librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(DocHeader):";
int val;
for (int i=0; i<16; ++i) {
val=static_cast<int>(input->readULong(2));
static int const expected[]= {0,1,0,0,0xdfff,0xffff,0,0, 0xeed7, 0xffff, 0, 0,
0x9dd5, 0xbfff,0x4088,0x8000
};
if (val!=expected[i])
f << "f" << i << "=" << std::hex << val << std::dec << ",";
}
for (int i=0; i<7; ++i) {
f << "unkn" << i << "=[";
for (int j=0; j<4; ++j) {
val=static_cast<int>(input->readULong(2));
if (val)
f << std::hex << val << std::dec << ",";
else
f << "_,";
}
f << "],";
}
for (int i=0; i<14; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val)
f << "g" << i << "=" << val << ",";
}
ascii().addPos(84);
ascii().addNote(f.str().c_str());
long pos=input->tell();
f.str("");
f << "DocHeader-1:";
auto N=static_cast<int>(input->readULong(2));
if (N>=20) {
MWAW_DEBUG_MSG(("MacDraftParser::readDocHeader: N seems bad\n"));
f << "###";
N=0;
}
// FIXME: find a better way to determine v1.0 and/or v1.2 file
if (N==4)
m_state->m_version=1;
else
m_state->m_version=2;
f << "N=" << N << ",unk=[";
for (int i=0; i<=N; ++i) {
val=static_cast<int>(input->readULong(2));
f << input->readULong(2) << ":" << std::hex << val << std::dec << ",";
}
f << "],";
input->seek(pos+42, librevenge::RVNG_SEEK_SET);
for (int i=0; i<6; ++i) {
val=static_cast<int>(input->readULong(2));
static int const expected[]= {0x144, 0xa, 0x152, 0xc, 0, 0 };
if (val!=expected[i])
f << "f" << i << "=" << std::hex << val << std::dec << ",";
}
f << "unkn1=[";
for (int i=0; i<2; ++i) { // 0,0 or 99cc6633,99cc6633
auto lVal=long(input->readULong(4));
if (lVal)
f << std::hex << lVal << std::dec << ",";
else
f << "_,";
}
f << "],";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "DocHeader-2:";
input->seek(pos+132,librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "DocHeader-3:";
input->seek(pos+66,librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
for (int st=0; st<2; ++st) {
pos=input->tell();
f.str("");
f << "DocHeader-4" << (st==0 ? "a" : "b") << ":";
input->seek(pos+40,librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
pos=input->tell();
f.str("");
f << "DocHeader-5:";
for (int i=0; i<2; ++i) { // fl0=0|1, fl1=1
val=static_cast<int>(input->readLong(1));
if (val==1)
f << "fl" << i << ",";
else if (val)
f << "fl" << i << "=" << val << ",";
}
for (int i=0; i<2; ++i) { // f0=3, f1=32|100
val=static_cast<int>(input->readLong(2));
if (val)
f << "f" << i << "=" << val << ",";
}
for (int i=0; i<4; ++i) { // fl2=1, fl4=0|1
val=static_cast<int>(input->readLong(1));
if (val==1)
f << "fl" << i+2 << ",";
else if (val)
f << "fl" << i+2 << "=" << val << ",";
}
f << "unkn=[";
for (int i=0; i<6; ++i) { // small number
val=static_cast<int>(input->readLong(2));
if (val)
f << val << ",";
else
f << "_,";
}
f << "],";
float coord[2];
for (auto &c : coord) c=float(input->readLong(2))/8.f;
f << "dim?=" << MWAWVec2f(coord[1],coord[0]) << ",";
for (int i=0; i<7; ++i) { // f2=0|256, f5=256
val=static_cast<int>(input->readLong(2));
if (val)
f << "f" << i+2 << "=" << val << ",";
}
int numPages[2];
for (auto &p : numPages) p=static_cast<int>(input->readLong(2));
if (numPages[0]!=1 || numPages[1]!=1)
f << "numPages=" << MWAWVec2i(numPages[0], numPages[1]) << ",";
if (numPages[0]<=0 || numPages[0]>50) {
MWAW_DEBUG_MSG(("MacDraftParser::readDocHeader: num horizontal pages seems bad, reset it to 1\n"));
numPages[0]=1;
f << "###hori,";
}
if (numPages[1]<=0 || numPages[1]>100) {
MWAW_DEBUG_MSG(("MacDraftParser::readDocHeader: num vertical pages seems bad, reset it to 1\n"));
numPages[1]=1;
f << "###verti,";
}
int pageDim[2];
for (auto &d : pageDim) d=static_cast<int>(input->readLong(2));
f << "page[dim]=" << MWAWVec2i(pageDim[1],pageDim[0]) << ",";
if (pageDim[0]>=50 && pageDim[0]<=2000)
getPageSpan().setFormLength(double(pageDim[0]*numPages[1])/72.);
else {
MWAW_DEBUG_MSG(("MacDraftParser::readDocHeader: page height seems bad, use default\n"));
}
if (pageDim[1]>=50 && pageDim[1]<=2000)
getPageSpan().setFormWidth(double(pageDim[1]*numPages[0])/72.);
else {
MWAW_DEBUG_MSG(("MacDraftParser::readDocHeader: page width seems bad, use default\n"));
}
ascii().addDelimiter(input->tell(),'|');
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(0x270,librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
// try to read the print info zone
////////////////////////////////////////////////////////////
bool MacDraftParser::readPrintInfo()
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
auto fSz=long(input->readULong(2));
long endPos=pos+2+120;
if (fSz!=120 || !input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("MacDraftParser::readPrintInfo: file seems too short\n"));
return false;
}
libmwaw::DebugStream f;
f << "Entries(PrintInfo):";
libmwaw::PrinterInfo info;
if (!info.read(input)) {
MWAW_DEBUG_MSG(("MacDraftParser::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);
// do not set page height/width as this is done by readDocHeader
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
////////////////////////////////////////////////////////////
//
// send data
//
////////////////////////////////////////////////////////////
bool MacDraftParser::send(MacDraftParserInternal::Shape const &shape)
{
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDraftParser::send: can not find the listener\n"));
return false;
}
shape.m_isSent=true;
MWAWBox2f box=shape.getBdBox();
MWAWPosition pos(box[0], box.size(), librevenge::RVNG_POINT);
pos.m_anchorTo = MWAWPosition::Page;
MWAWGraphicStyle style=shape.m_style;
if (shape.m_patternId>0) {
MWAWGraphicStyle::Pattern pat;
if (m_state->getPattern(shape.m_patternId, pat)) {
MWAWColor col;
if (pat.getUniqueColor(col))
style.setSurfaceColor(col);
else
style.setPattern(pat);
}
else {
MWAW_DEBUG_MSG(("MacDraftParser::send: can not find pattern %d\n", shape.m_patternId));
}
}
switch (shape.m_type) {
case MacDraftParserInternal::Shape::Basic:
listener->insertShape(pos, shape.m_shape, style);
break;
case MacDraftParserInternal::Shape::Group: {
size_t numShapes=m_state->m_shapeList.size();
if (!numShapes) break;
listener->openGroup(pos);
for (auto id : shape.m_childList) {
if (id>=numShapes) {
MWAW_DEBUG_MSG(("MacDraftParser::send: can not find a child\n"));
continue;
}
MacDraftParserInternal::Shape const &child=m_state->m_shapeList[id];
if (child.m_isSent) {
MWAW_DEBUG_MSG(("MacDraftParser::send: the child is already sent\n"));
continue;
}
send(child);
}
listener->closeGroup();
break;
}
case MacDraftParserInternal::Shape::Label: {
/* either a 1d label or a 2d label, so check if we can print it,
ie. if the previous object is a line
*/
if (shape.m_id<=0 || shape.m_id>=static_cast<int>(m_state->m_shapeList.size())) {
MWAW_DEBUG_MSG(("MacDraftParser::send: problem finding the shape\n"));
break;
}
auto const &obj=m_state->m_shapeList[size_t(shape.m_id-1)];
if (obj.m_type!=MacDraftParserInternal::Shape::Basic || !obj.m_isLine) {
MWAW_DEBUG_MSG(("MacDraftParser::send: can only retrieve line label\n"));
break;
}
// update the label width and recenter the label box
MWAWVec2f lineSz=obj.m_shape.getBdBox().size();
auto &label=m_state->m_shapeList[size_t(shape.m_id)];
label.m_labelWidth=std::sqrt(lineSz[0]*lineSz[0]+lineSz[1]*lineSz[1]);
MWAWVec2f decal=obj.m_shape.getBdBox().center()-box.center();
pos.setOrigin(box[0]+decal);
std::shared_ptr<MWAWSubDocument> doc(new MacDraftParserInternal::SubDocument(*this, getInput(), shape.m_id));
listener->insertTextBox(pos, doc, style);
return true;
}
case MacDraftParserInternal::Shape::Text: {
std::shared_ptr<MWAWSubDocument> doc(new MacDraftParserInternal::SubDocument(*this, getInput(), shape.m_id));
listener->insertTextBox(pos, doc, style);
return true;
}
case MacDraftParserInternal::Shape::Bitmap:
sendBitmap(shape, pos);
break;
case MacDraftParserInternal::Shape::Unknown:
#if !defined(__clang__)
default:
#endif
return false;
}
return true;
}
bool MacDraftParser::sendBitmap(MacDraftParserInternal::Shape const &bitmap, MWAWPosition const &position)
{
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDraftParser::sendBitmap: can not find the listener\n"));
return false;
}
bitmap.m_isSent=true;
if (bitmap.m_bitmapIdList.empty() || bitmap.m_bitmapDimensionList.size()!=bitmap.m_bitmapIdList.size()) {
MWAW_DEBUG_MSG(("MacDraftParser::sendBitmap: the bitmap seems bad\n"));
return false;
}
MWAWBox2i bitmapBox=bitmap.m_bitmapDimensionList[0];
for (size_t i=1; i<bitmap.m_bitmapDimensionList.size(); ++i)
bitmapBox=bitmapBox.getUnion(bitmap.m_bitmapDimensionList[i]);
MWAWVec2i bitmapDim(bitmapBox.size());
if (bitmapDim[0]<=0||bitmapDim[1]<=0||bitmapDim[0]*bitmapDim[1]>10000000) {
MWAW_DEBUG_MSG(("MacDraftParser::sendBitmap: can not compute the bitmap bdbox\n"));
return false;
}
MWAWInputStreamPtr input = getInput();
MWAWPictBitmapColor pict(bitmapDim, true);
MWAWColor transparent(255,255,255,0);
MWAWColor black(MWAWColor::black());
std::vector<MWAWColor> colors;
colors.resize(size_t(bitmapDim[0]), transparent);
for (int r=0; r<bitmapDim[1]; ++r) pict.setRow(r, &colors[0]);
for (size_t i=0; i<bitmap.m_bitmapIdList.size(); ++i) {
auto id=bitmap.m_bitmapIdList[i];
if (m_state->m_idToBitmapMap.find(id)==m_state->m_idToBitmapMap.end()) {
MWAW_DEBUG_MSG(("MacDraftParser::sendBitmap: can not find bitmap data %lx\n", id));
continue;
}
MWAWBox2i dataBox(bitmap.m_bitmapDimensionList[i]);
MacDraftParserInternal::BitmapFileData const &data=m_state->m_idToBitmapMap.find(id)->second;
if (!data.ok() || data.m_dimension[0]!=dataBox[0] || data.m_dimension.size()[0]<dataBox.size()[0] ||
data.m_dimension.size()[1]<dataBox.size()[1]) {
MWAW_DEBUG_MSG(("MacDraftParser::sendBitmap: bitmap data %lx seems bad\n", id));
continue;
}
long pos=input->tell();
input->seek(data.m_entry.begin(), librevenge::RVNG_SEEK_SET);
for (int r=dataBox[0][1]; r<dataBox[1][1]; ++r) {
long debRow=input->tell();
if (r>=bitmapBox[1][1])
break;
int row=r-bitmapBox[0][1];
int wPos=dataBox[0][0]-bitmapBox[0][0];
for (int col=dataBox[0][0]; col<dataBox[1][0];) {
auto c=static_cast<unsigned char>(input->readULong(1));
for (int j=0, bit=0x80; j<8 ; ++j, bit>>=1) {
if (wPos>=bitmapDim[0] || ++col>dataBox[1][0]) break;
pict.set(wPos++, row, (c&bit) ? black : transparent);
}
}
input->seek(debRow+data.m_rowSize, librevenge::RVNG_SEEK_SET);
}
input->seek(pos, librevenge::RVNG_SEEK_SET);
}
MWAWEmbeddedObject picture;
if (!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
listener->insertPicture(position, picture);
return true;
}
bool MacDraftParser::sendText(int zId)
{
MWAWGraphicListenerPtr listener=getGraphicListener();
if (!listener) {
MWAW_DEBUG_MSG(("MacDraftParser::sendText: can not find the listener\n"));
return false;
}
if (zId<0||zId>=static_cast<int>(m_state->m_shapeList.size())) {
MWAW_DEBUG_MSG(("MacDraftParser::sendText: can not find the text shape\n"));
return false;
}
auto const &shape=m_state->m_shapeList[size_t(zId)];
shape.m_isSent = true;
listener->setParagraph(shape.m_paragraph);
listener->setFont(shape.m_font);
if (shape.m_type == MacDraftParserInternal::Shape::Label) {
std::stringstream s;
s << std::setprecision(0) << std::fixed << shape.m_labelWidth << " pt";
listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str()));
return true;
}
if (shape.m_type != MacDraftParserInternal::Shape::Text) {
MWAW_DEBUG_MSG(("MacDraftParser::sendText: can not find the text shape\n"));
return false;
}
if (!shape.m_textEntry.valid())
return true;
MWAWInputStreamPtr input=getInput();
input->seek(shape.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Object[text]:";
long endPos=shape.m_textEntry.end();
while (!input->isEnd()) {
if (input->tell()>=shape.m_textEntry.end())
break;
auto c = char(input->readULong(1));
if (c==0) {
MWAW_DEBUG_MSG(("MacDraftParser::sendText: find char 0\n"));
f << "#[0]";
continue;
}
f << c;
switch (c) {
case 9:
listener->insertTab();
break;
case 0xd:
listener->insertEOL();
break;
default:
listener->insertCharacter(static_cast<unsigned char>(c), input, endPos);
break;
}
}
ascii().addPos(shape.m_textEntry.begin());
ascii().addNote(f.str().c_str());
return true;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: