You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/filters/karbon/msod/msod.cc

1341 lines
41 KiB

/*
Copyright (C) 2000, S.R.Haque <shaheedhaque@hotmail.com>.
This file is part of the KDE project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
DESCRIPTION
*/
#include <kdebug.h>
#include <tqdatastream.h>
#include <tqfile.h>
#include <tqptrlist.h>
#include <tqpointarray.h>
#include <tqrect.h>
#include <tqsize.h>
#include <msod.h>
#include <zlib.h>
const int Msod::s_area = 30505;
Msod::Msod(
unsigned dpi) :
KWmf(dpi)
{
m_dpi = dpi;
m_images.setAutoDelete(true);
m_opt = new Options(*this);
m_shape.data = 0L;
m_shape.length = 0;
}
Msod::~Msod()
{
delete [] m_shape.data;
delete m_opt;
}
void Msod::drawShape(
unsigned shapeType,
TQ_UINT32 bytes,
TQDataStream &operands)
{
static const char *funcTab[] =
{
"UNKNOWN", // Unknown
"RECTANGLE", // Rectangle
"ROUNDRECTANGLE", // Roundrectangle
"ELLIPSE", // Ellipse
"DIAMOND", // Diamond
"ISOCELESTRIANGLE", // Isocelestriangle
"RIGHTTRIANGLE", // Righttriangle
"PARALLELOGRAM", // Parallelogram
"TRAPEZOID", // Trapezoid
"HEXAGON", // Hexagon
"OCTAGON", // Octagon
"PLUS", // Plus
"STAR", // Star
"ARROW", // Arrow
"THICKARROW", // Thickarrow
"HOMEPLATE", // Homeplate
"CUBE", // Cube
"BALLOON", // Balloon
"SEAL", // Seal
"ARC", // Arc
"LINE", // Line
"PLAQUE", // Plaque
"CAN", // Can
"DONUT", // Donut
"TEXTSIMPLE", // Textsimple
"TEXTOCTAGON", // Textoctagon
"TEXTHEXAGON", // Texthexagon
"TEXTCURVE", // Textcurve
"TEXTWAVE", // Textwave
"TEXTRING", // Textring
"TEXTONCURVE", // Textoncurve
"TEXTONRING", // Textonring
"STRAIGHTCONNECTOR1", // Straightconnector1
"BENTCONNECTOR2", // Bentconnector2
"BENTCONNECTOR3", // Bentconnector3
"BENTCONNECTOR4", // Bentconnector4
"BENTCONNECTOR5", // Bentconnector5
"CURVEDCONNECTOR2", // Curvedconnector2
"CURVEDCONNECTOR3", // Curvedconnector3
"CURVEDCONNECTOR4", // Curvedconnector4
"CURVEDCONNECTOR5", // Curvedconnector5
"CALLOUT1", // Callout1
"CALLOUT2", // Callout2
"CALLOUT3", // Callout3
"ACCENTCALLOUT1", // Accentcallout1
"ACCENTCALLOUT2", // Accentcallout2
"ACCENTCALLOUT3", // Accentcallout3
"BORDERCALLOUT1", // bordercallout1
"BORDERCALLOUT2", // Bordercallout2
"BORDERCALLOUT3", // Bordercallout3
"ACCENTBORDERCALLOUT1", // Accentbordercallout1
"ACCENTBORDERCALLOUT2", // Accentbordercallout2
"ACCENTBORDERCALLOUT3", // Accentbordercallout3
"RIBBON", // Ribbon
"RIBBON2", // Ribbon2
"CHEVRON", // Chevron
"PENTAGON", // Pentagon
"NOSMOKING", // Nosmoking
"SEAL8", // Seal8
"SEAL16", // Seal16
"SEAL32", // Seal32
"WEDGERECTCALLOUT", // Wedgerectcallout
"WEDGERRECTCALLOUT", // Wedgerrectcallout
"WEDGEELLIPSECALLOUT", // Wedgeellipsecallout
"WAVE", // Wave
"FOLDEDCORNER", // Foldedcorner
"LEFTARROW", // Leftarrow
"DOWNARROW", // Downarrow
"UPARROW", // Uparrow
"LEFTRIGHTARROW", // Leftrightarrow
"UPDOWNARROW", // Updownarrow
"IRREGULARSEAL1", // Irregularseal1
"IRREGULARSEAL2", // Irregularseal2
"LIGHTNINGBOLT", // Lightningbolt
"HEART", // Heart
"PICTUREFRAME", // PictureFrame
"QUADARROW", // Quadarrow
"LEFTARROWCALLOUT", // Leftarrowcallout
"RIGHTARROWCALLOUT", // Rightarrowcallout
"UPARROWCALLOUT", // Uparrowcallout
"DOWNARROWCALLOUT", // Downarrowcallout
"LEFTRIGHTARROWCALLOUT", // Leftrightarrowcallout
"UPDOWNARROWCALLOUT", // Updownarrowcallout
"QUADARROWCALLOUT", // Quadarrowcallout
"BEVEL", // Bevel
"LEFTBRACKET", // Leftbracket
"RIGHTBRACKET", // Rightbracket
"LEFTBRACE", // Leftbrace
"RIGHTBRACE", // Rightbrace
"LEFTUPARROW", // Leftuparrow
"BENTUPARROW", // Bentuparrow
"BENTARROW", // Bentarrow
"SEAL24", // Seal24
"STRIPEDRIGHTARROW", // Stripedrightarrow
"NOTCHEDRIGHTARROW", // Notchedrightarrow
"BLOCKARC", // Blockarc
"SMILEYFACE", // Smileyface
"VERTICALSCROLL", //Qt::Verticalscroll
"HORIZONTALSCROLL", //Qt::Horizontalscroll
"CIRCULARARROW", // Circulararrow
"NOTCHEDCIRCULARARROW", // Notchedcirculararrow
"UTURNARROW", // Uturnarrow
"CURVEDRIGHTARROW", // Curvedrightarrow
"CURVEDLEFTARROW", // Curvedleftarrow
"CURVEDUPARROW", // Curveduparrow
"CURVEDDOWNARROW", // Curveddownarrow
"CLOUDCALLOUT", // Cloudcallout
"ELLIPSERIBBON", // Ellipseribbon
"ELLIPSERIBBON2", // Ellipseribbon2
"FLOWCHARTPROCESS", // Flowchartprocess
"FLOWCHARTDECISION", // Flowchartdecision
"FLOWCHARTINPUTOUTPUT", // Flowchartinputoutput
"FLOWCHARTPREDEFINEDPROCESS", // Flowchartpredefinedprocess
"FLOWCHARTINTERNALSTORAGE", // Flowchartinternalstorage
"FLOWCHARTDOCUMENT", // Flowchartdocument
"FLOWCHARTMULTIDOCUMENT", // Flowchartmultidocument
"FLOWCHARTTERMINATOR", // Flowchartterminator
"FLOWCHARTPREPARATION", // Flowchartpreparation
"FLOWCHARTMANUALINPUT", // Flowchartmanualinput
"FLOWCHARTMANUALOPERATION", // Flowchartmanualoperation
"FLOWCHARTCONNECTOR", // Flowchartconnector
"FLOWCHARTPUNCHEDCARD", // Flowchartpunchedcard
"FLOWCHARTPUNCHEDTAPE", // Flowchartpunchedtape
"FLOWCHARTSUMMINGJUNCTION", // Flowchartsummingjunction
"FLOWCHARTOR", // Flowchartor
"FLOWCHARTCOLLATE", // Flowchartcollate
"FLOWCHARTSORT", // Flowchartsort
"FLOWCHARTEXTRACT", // Flowchartextract
"FLOWCHARTMERGE", // Flowchartmerge
"FLOWCHARTOFFLINESTORAGE", // Flowchartofflinestorage
"FLOWCHARTONLINESTORAGE", // Flowchartonlinestorage
"FLOWCHARTMAGNETICTAPE", // Flowchartmagnetictape
"FLOWCHARTMAGNETICDISK", // Flowchartmagneticdisk
"FLOWCHARTMAGNETICDRUM", // Flowchartmagneticdrum
"FLOWCHARTDISPLAY", // Flowchartdisplay
"FLOWCHARTDELAY", // Flowchartdelay
"TEXTPLAINTEXT", // Textplaintext
"TEXTSTOP", // Textstop
"TEXTTRIANGLE", // Texttriangle
"TEXTTRIANGLEINVERTED", // Texttriangleinverted
"TEXTCHEVRON", // Textchevron
"TEXTCHEVRONINVERTED", // Textchevroninverted
"TEXTRINGINSIDE", // Textringinside
"TEXTRINGOUTSIDE", // Textringoutside
"TEXTARCHUPCURVE", // Textarchupcurve
"TEXTARCHDOWNCURVE", // Textarchdowncurve
"TEXTCIRCLECURVE", // Textcirclecurve
"TEXTBUTTONCURVE", // Textbuttoncurve
"TEXTARCHUPPOUR", // Textarchuppour
"TEXTARCHDOWNPOUR", // Textarchdownpour
"TEXTCIRCLEPOUR", // Textcirclepour
"TEXTBUTTONPOUR", // Textbuttonpour
"TEXTCURVEUP", // Textcurveup
"TEXTCURVEDOWN", // Textcurvedown
"TEXTCASCADEUP", // Textcascadeup
"TEXTCASCADEDOWN", // Textcascadedown
"TEXTWAVE1", // Textwave1
"TEXTWAVE2", // Textwave2
"TEXTWAVE3", // Textwave3
"TEXTWAVE4", // Textwave4
"TEXTINFLATE", // Textinflate
"TEXTDEFLATE", // Textdeflate
"TEXTINFLATEBOTTOM", // Textinflatebottom
"TEXTDEFLATEBOTTOM", // Textdeflatebottom
"TEXTINFLATETOP", // Textinflatetop
"TEXTDEFLATETOP", // Textdeflatetop
"TEXTDEFLATEINFLATE", // Textdeflateinflate
"TEXTDEFLATEINFLATEDEFLATE", // Textdeflateinflatedeflate
"TEXTFADERIGHT", // Textfaderight
"TEXTFADELEFT", // Textfadeleft
"TEXTFADEUP", // Textfadeup
"TEXTFADEDOWN", // Textfadedown
"TEXTSLANTUP", // Textslantup
"TEXTSLANTDOWN", // Textslantdown
"TEXTCANUP", // Textcanup
"TEXTCANDOWN", // Textcandown
"FLOWCHARTALTERNATEPROCESS", // Flowchartalternateprocess
"FLOWCHARTOFFPAGECONNECTOR", // Flowchartoffpageconnector
"CALLOUT90", // Callout90
"ACCENTCALLOUT90", // Accentcallout90
"BORDERCALLOUT90", // Bordercallout90
"ACCENTBORDERCALLOUT90", // Accentbordercallout90
"LEFTRIGHTUPARROW", // Leftrightuparrow
"SUN", // Sun
"MOON", // Moon
"BRACKETPAIR", // Bracketpair
"BRACEPAIR", // Bracepair
"SEAL4", // Seal4
"DOUBLEWAVE", // Doublewave
"ACTIONBUTTONBLANK", // Actionbuttonblank
"ACTIONBUTTONHOME", // Actionbuttonhome
"ACTIONBUTTONHELP", // Actionbuttonhelp
"ACTIONBUTTONINFORMATION", // Actionbuttoninformation
"ACTIONBUTTONFORWARDNEXT", // Actionbuttonforwardnext
"ACTIONBUTTONBACKPREVIOUS", // Actionbuttonbackprevious
"ACTIONBUTTONEND", // Actionbuttonend
"ACTIONBUTTONBEGINNING", // Actionbuttonbeginning
"ACTIONBUTTONRETURN", // Actionbuttonreturn
"ACTIONBUTTONDOCUMENT", // Actionbuttondocument
"ACTIONBUTTONSOUND", // Actionbuttonsound
"ACTIONBUTTONMOVIE", // Actionbuttonmovie
"HOSTCONTROL", // Hostcontrol
"TEXTBOX", // Textbox
};
struct
{
TQ_UINT32 spid; // The shape id
union
{
TQ_UINT32 info;
struct
{
TQ_UINT32 fGroup : 1; // This shape is a group shape
TQ_UINT32 fChild : 1; // Not a top-level shape
TQ_UINT32 fPatriarch : 1; // This is the topmost group shape.
// Exactly one of these per drawing.
TQ_UINT32 fDeleted : 1; // The shape.has been deleted
TQ_UINT32 fOleShape : 1; // The shape is an OLE object
TQ_UINT32 fHaveMaster : 1; // Shape has a hspMaster property
TQ_UINT32 fFlipH : 1; // Shape is flipped horizontally
TQ_UINT32 fFlipV : 1; // Shape is flipped vertically
TQ_UINT32 fConnector : 1; // Connector type of shape
TQ_UINT32 fHaveAnchor : 1; // Shape has an anchor of some kind
TQ_UINT32 fBackground : 1; // Background shape
TQ_UINT32 fHaveSpt : 1; // Shape has a shape type property
TQ_UINT32 reserved : 20; // Not yet used
} fields;
} grfPersistent;
} data;
// Scan lookup table for operation.
operands >> data.spid;
operands >> data.grfPersistent.info;
bytes -= 8;
kdDebug(s_area) << "shape-id: " << data.spid << " type: " << funcTab[shapeType] << " (" << shapeType << ")" <<
(data.grfPersistent.fields.fGroup ? " group" : "") <<
(data.grfPersistent.fields.fChild ? " child" : "") <<
(data.grfPersistent.fields.fPatriarch ? " patriarch" : "") <<
(data.grfPersistent.fields.fDeleted ? " deleted" : "") <<
(data.grfPersistent.fields.fOleShape ? " oleshape" : "") <<
(data.grfPersistent.fields.fHaveMaster ? " master" : "") <<
(data.grfPersistent.fields.fFlipH ? " flipv" : "") <<
(data.grfPersistent.fields.fConnector ? " connector" : "") <<
(data.grfPersistent.fields.fHaveAnchor ? " anchor" : "") <<
(data.grfPersistent.fields.fBackground ? " background" : "") <<
(data.grfPersistent.fields.fHaveSpt ? " spt" : "") <<
" operands: " << bytes << endl;
if (data.grfPersistent.fields.fDeleted)
return;
if ((!m_isRequiredDrawing) && (m_requestedShapeId != data.spid))
return;
// An active shape! Let's draw it...
switch (shapeType)
{
case 0:
if (m_opt->m_pVertices)
{
gotPolyline(m_dc, *m_opt->m_pVertices);
}
break;
case 1:
if (bytes > 7)
{
TQPoint topLeft;
TQSize size;
topLeft = normalisePoint(operands);
size = normaliseSize(operands);
TQRect rect(topLeft, size);
TQPointArray points(4);
points.setPoint(0, topLeft);
points.setPoint(1, rect.topRight());
points.setPoint(2, rect.bottomRight());
points.setPoint(3, rect.bottomLeft());
gotRectangle(m_dc, points);
}
case 20:
if (bytes > 7)
{
TQPoint lineFrom;
TQPoint lineTo;
lineTo = normalisePoint(operands);
TQPointArray points(2);
points.setPoint(0, lineFrom);
points.setPoint(1, lineTo);
gotPolyline(m_dc, points);
}
break;
default:
break;
}
}
void Msod::invokeHandler(
Header &op,
TQ_UINT32 bytes,
TQDataStream &operands)
{
typedef void (Msod::*method)(Header &op, TQ_UINT32 bytes, TQDataStream &operands);
typedef struct
{
const char *name;
TQ_UINT16 opcode;
method handler;
} opcodeEntry;
static const opcodeEntry funcTab[] =
{
{ "ALIGNRULE", 0xF013, &Msod::opAlignrule },
{ "ANCHOR", 0xF00E, &Msod::opAnchor },
{ "ARCRULE", 0xF014, &Msod::opArcrule },
{ "BSE", 0xF007, &Msod::opBse },
{ "BSTORECONTAINER", 0xF001, &Msod::opBstorecontainer },
{ "CALLOUTRULE", 0xF017, &Msod::opCalloutrule },
{ "CHILDANCHOR", 0xF00F, &Msod::opChildanchor },
{ "CLIENTANCHOR", 0xF010, &Msod::opClientanchor },
{ "CLIENTDATA", 0xF011, &Msod::opClientdata },
{ "CLIENTRULE", 0xF015, &Msod::opClientrule },
{ "CLIENTTEXTBOX", 0xF00D, &Msod::opClienttextbox },
{ "CLSID", 0xF016, &Msod::opClsid },
{ "COLORMRU", 0xF11A, &Msod::opColormru },
{ "CONNECTORRULE", 0xF012, &Msod::opConnectorrule },
{ "DELETEDPSPL", 0xF11D, &Msod::opDeletedpspl },
{ "DG", 0xF008, &Msod::opDg },
{ "DGCONTAINER", 0xF002, &Msod::opDgcontainer },
{ "DGG", 0xF006, &Msod::opDgg },
{ "DGGCONTAINER", 0xF000, &Msod::opDggcontainer },
{ "OLEOBJECT", 0xF11F, &Msod::opOleobject },
{ "OPT", 0xF00B, &Msod::opOpt },
{ "REGROUPITEMS", 0xF118, &Msod::opRegroupitems },
{ "SELECTION", 0xF119, &Msod::opSelection },
{ "SOLVERCONTAINER", 0xF005, &Msod::opSolvercontainer },
{ "SP", 0xF00A, &Msod::opSp },
{ "SPCONTAINER", 0xF004, &Msod::opSpcontainer },
{ "SPGR", 0xF009, &Msod::opSpgr },
{ "SPGRCONTAINER", 0xF003, &Msod::opSpgrcontainer },
{ "SPLITMENUCOLORS", 0xF11E, &Msod::opSplitmenucolors },
{ "TEXTBOX", 0xF00C, &Msod::opTextbox },
{ NULL, 0, 0 },
{ "BLIP", 0, &Msod::opBlip }
};
unsigned i;
method result;
// Scan lookup table for operation.
for (i = 0; funcTab[i].name; i++)
{
if (funcTab[i].opcode == op.opcode.fields.fbt)
{
break;
}
}
// Invoke handler.
result = funcTab[i].handler;
if (!result && (op.opcode.fields.fbt >= 0xF018) && (0xF117 >= op.opcode.fields.fbt))
result = funcTab[++i].handler;
if (!result)
{
if (funcTab[i].name)
kdWarning(s_area) << "invokeHandler: unsupported opcode: " <<
funcTab[i].name <<
" operands: " << bytes << endl;
else
kdWarning(s_area) << "invokeHandler: unsupported opcode: 0x" <<
TQString::number(op.opcode.fields.fbt, 16) <<
" operands: " << bytes << endl;
// Skip data we cannot use.
skip(bytes, operands);
}
else
{
kdDebug(s_area) << "invokeHandler: opcode: " << funcTab[i].name <<
" operands: " << bytes << endl;
// We don't invoke the handler directly on the incoming operands, but
// via a temporary datastream. This adds overhead, but eliminates the
// need for the individual handlers to read *exactly* the right amount
// of data (thus speeding development, and possibly adding some
// future-proofing).
if (bytes)
{
TQByteArray *record = new TQByteArray(bytes);
TQDataStream *body;
operands.readRawBytes(record->data(), bytes);
body = new TQDataStream(*record, IO_ReadOnly);
body->setByteOrder(TQDataStream::LittleEndian);
(this->*result)(op, bytes, *body);
delete body;
delete record;
}
else
{
TQDataStream *body = new TQDataStream();
(this->*result)(op, bytes, *body);
delete body;
}
}
}
TQPoint Msod::normalisePoint(
TQDataStream &operands)
{
TQ_UINT16 x;
TQ_UINT16 y;
operands >> x >> y;
return TQPoint(x / m_dpi, y / m_dpi);
}
TQSize Msod::normaliseSize(
TQDataStream &operands)
{
TQ_UINT16 width;
TQ_UINT16 height;
operands >> width >> height;
return TQSize(width / m_dpi, height / m_dpi);
}
bool Msod::parse(
unsigned shapeId,
const TQString &file,
const char *delayStream)
{
TQFile in(file);
if (!in.open(IO_ReadOnly))
{
kdError(s_area) << "Unable to open input file!" << endl;
in.close();
return false;
}
TQDataStream stream(&in);
bool result = parse(shapeId, stream, in.size(), delayStream);
in.close();
return result;
}
bool Msod::parse(
unsigned shapeId,
TQDataStream &stream,
unsigned size,
const char *delayStream)
{
stream.setByteOrder(TQDataStream::LittleEndian); // Great, I love TQt !
m_requestedShapeId = shapeId;
m_isRequiredDrawing = false;
m_delayStream = delayStream;
// Read bits.
walk(size, stream);
return true;
}
void Msod::opAlignrule(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opAnchor(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opArcrule(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opBlip(Header &, TQ_UINT32 bytes, TQDataStream &operands)
{
typedef enum
{
msobiWMF = 0x216, // Metafile header then compressed WMF.
msobiEMF = 0x3D4, // Metafile header then compressed EMF.
msobiPICT = 0x542, // Metafile header then compressed PICT.
msobiPNG = 0x6E0, // One byte tag then PNG data.
msobiJPEG = 0x46A, // One byte tag then JFIF data.
msobiDIB = 0x7A8, // One byte tag then DIB data.
msobiClient = 0x800 // Clients should set this bit.
} MSOBI;
typedef enum
{
msocompressionDeflate,
msocompressionNone = 254,
msocompressionTest
} MSOBLIPCOMPRESSION;
bool hasPrimaryId;
TQ_UINT32 length = 0;
struct
{
TQ_UINT32 cb;
struct
{
TQ_UINT32 x;
TQ_UINT32 y;
TQ_UINT32 w;
TQ_UINT32 h;
} bounds;
struct
{
TQ_UINT32 w;
TQ_UINT32 h;
} ptSize;
TQ_UINT32 cbSave;
TQ_UINT8 compression;
TQ_UINT8 filter;
} data;
// Skip any explicit primary header (m_rgbUidprimary).
switch (m_blipType)
{
case msoblipEMF:
hasPrimaryId = (m_blipType ^ msobiEMF) != 0;
break;
case msoblipWMF:
hasPrimaryId = (m_blipType ^ msobiWMF) != 0;
break;
case msoblipPICT:
hasPrimaryId = (m_blipType ^ msobiPICT) != 0;
break;
case msoblipJPEG:
hasPrimaryId = (m_blipType ^ msobiJPEG) != 0;
break;
case msoblipPNG:
hasPrimaryId = (m_blipType ^ msobiPNG) != 0;
break;
case msoblipDIB:
hasPrimaryId = (m_blipType ^ msobiDIB) != 0;
break;
default:
hasPrimaryId = (m_blipType ^ msobiClient) != 0;
break;
}
if (hasPrimaryId)
{
length += 16;
skip(16, operands);
}
// Process the rest of the header.
data.compression = msocompressionNone;
switch (m_blipType)
{
case msoblipEMF:
case msoblipWMF:
case msoblipPICT:
length += 34;
operands >> data.cb;
operands >> data.bounds.x >> data.bounds.y >> data.bounds.w >> data.bounds.h;
operands >> data.ptSize.w >> data.ptSize.h;
operands >> data.cbSave;
operands >> data.compression >> data.filter;
break;
case msoblipJPEG:
case msoblipPNG:
case msoblipDIB:
// Skip the "tag".
length += 1;
skip(1, operands);
break;
default:
break;
}
// Work out the file type.
Image *image = new Image();
switch (m_blipType)
{
case msoblipEMF:
image->extension = "emf";
break;
case msoblipWMF:
image->extension = "wmf";
break;
case msoblipPICT:
image->extension = "pic";
break;
case msoblipJPEG:
image->extension = "jpg";
break;
case msoblipPNG:
image->extension = "png";
break;
case msoblipDIB:
image->extension = "dib";
break;
default:
image->extension = "img";
break;
}
image->length = bytes - length;
image->data = new char[image->length];
operands.readRawBytes((char *)image->data, image->length);
if (data.compression == msocompressionDeflate)
{
const char *tmp;
uLongf destLen = data.cb;
int result;
tmp = new char[data.cb];
result = uncompress((TQ_UINT8 *)tmp, &destLen, (TQ_UINT8 *)image->data, image->length);
if (result != Z_OK)
{
kdError(s_area) << "opBlip: uncompress failed: " << result << endl;
}
if (destLen != data.cb)
{
kdError(s_area) << "opBlip: uncompressed " << destLen << " instead of " << data.cb << endl;
}
delete [] image->data;
image->data = tmp;
image->length = destLen;
}
m_images.resize(m_images.size() + 1);
m_images.insert(m_images.size() - 1, image);
}
// FBSE - File Blip Store Entry
void Msod::opBse(Header &op, TQ_UINT32, TQDataStream &operands)
{
struct
{
TQ_UINT8 btWin32; // Required type on Win32.
TQ_UINT8 btMacOS; // Required type on Mac.
TQ_UINT8 rgbUid[16]; // Identifier of blip.
TQ_UINT16 tag; // currently unused.
TQ_UINT32 size; // Blip size in stream.
TQ_UINT32 cRef; // Reference count on the blip.
TQ_UINT32 foDelay; // File offset in the delay stream.
TQ_UINT8 usage; // How this blip is used (MSOBLIPUSAGE).
TQ_UINT8 cbName; // length of the blip name.
TQ_UINT8 unused2; // for the future.
TQ_UINT8 unused3; // for the future.
} data;
unsigned i;
// Work out the type of the BLIP.
m_blipType = static_cast<MSOBLIPTYPE>(op.opcode.fields.inst);
operands >> data.btWin32;
operands >> data.btMacOS;
for (i = 0; i < 16; i++)
operands >> data.rgbUid[i];
operands >> data.tag >> data.size;
operands >> data.cRef >> data.foDelay;
operands >> data.usage >> data.cbName;
operands >> data.unused2 >> data.unused2;
// If the Blip is not in this drawing file, process it "manually".
if (m_delayStream)
{
// The m_pib refers to images by number, which includes images
// that are no longer here. Thus, we fake these out so that any
// references to non-deleted images are still valid (!!!).
if (data.size && data.cRef)
{
TQByteArray bytes;
bytes.setRawData(m_delayStream + data.foDelay, data.size);
TQDataStream stream(bytes, IO_ReadOnly);
stream.setByteOrder(TQDataStream::LittleEndian);
walk(data.size, stream);
bytes.resetRawData(m_delayStream + data.foDelay, data.size);
}
else
{
m_images.resize(m_images.size() + 1);
m_images.insert(m_images.size() - 1, 0L);
}
}
}
void Msod::opBstorecontainer(Header &, TQ_UINT32 bytes, TQDataStream &operands)
{
walk(bytes, operands);
}
void Msod::opCalloutrule(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opChildanchor(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opClientanchor(Header &, TQ_UINT32, TQDataStream &operands)
{
struct
{
TQ_UINT32 unknown;
} data;
operands >> data.unknown;
kdDebug(s_area) << "client anchor: " << data.unknown << endl;
}
void Msod::opClientdata(Header &, TQ_UINT32, TQDataStream &operands)
{
struct
{
TQ_UINT32 unknown;
} data;
operands >> data.unknown;
kdDebug(s_area) << "client data: " << data.unknown << endl;
}
void Msod::opClientrule(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opClienttextbox(
Header &,
TQ_UINT32,
TQDataStream &operands)
{
struct
{
TQ_UINT32 unknown;
} data;
operands >> data.unknown;
kdDebug(s_area) << "client textbox: 0x" << TQString::number(data.unknown,16) << endl;
}
void Msod::opClsid(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opColormru(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opConnectorrule(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opDeletedpspl(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
// FDG - File DG
void Msod::opDg(Header &, TQ_UINT32, TQDataStream &operands)
{
struct
{
TQ_UINT32 csp; // The number of shapes in this drawing.
TQ_UINT32 spidCur; // The last shape ID given to an SP in this DG.
} data;
operands >> data.csp >> data.spidCur;
kdDebug(s_area) << "drawing id: " << data.spidCur << endl;
m_isRequiredDrawing = (m_requestedShapeId == data.spidCur);
if (m_isRequiredDrawing)
{
kdDebug(s_area) << "found requested drawing" << endl;
}
}
void Msod::opDgcontainer(Header &, TQ_UINT32 bytes, TQDataStream &operands)
{
walk(bytes, operands);
}
// FDGG - File DGG
void Msod::opDgg(Header &, TQ_UINT32, TQDataStream &operands)
{
struct
{
TQ_UINT32 spidMax; // The current maximum shape ID.
TQ_UINT32 cidcl; // The number of ID clusters (FIDCLs).
TQ_UINT32 cspSaved; // The total number of shapes saved.
// (including deleted shapes, if undo
// information was saved).
TQ_UINT32 cdgSaved; // The total number of drawings saved.
} data;
// File ID Cluster - used to save IDCLs
struct
{
TQ_UINT32 dgid; // DG owning the SPIDs in this cluster
TQ_UINT32 cspidCur; // number of SPIDs used so far
} data1;
unsigned i;
operands >> data.spidMax >> data.cidcl >> data.cspSaved >> data.cdgSaved;
kdDebug(s_area) << data.cspSaved << " shapes in " <<
data.cidcl - 1 << " clusters in " <<
data.cdgSaved << " drawings" << endl;
for (i = 0; i < data.cidcl - 1; i++)
{
operands >> data1.dgid >> data1.cspidCur;
}
}
void Msod::opDggcontainer(Header &, TQ_UINT32 bytes, TQDataStream &operands)
{
walk(bytes, operands);
}
void Msod::opOleobject(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opOpt(Header &, TQ_UINT32 bytes, TQDataStream &operands)
{
m_opt->walk(bytes, operands);
}
void Msod::opRegroupitems(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opSelection(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::opSolvercontainer(Header &, TQ_UINT32 bytes, TQDataStream &operands)
{
walk(bytes, operands);
}
void Msod::opSp(Header &op, TQ_UINT32 bytes, TQDataStream &operands)
{
// We want to defer the act of drawing a shape until we have seen any options
// that may affect it. Thus, we merely store the data away, and let opSpContainer
// do all the ahrd work.
m_shape.type = op.opcode.fields.inst;
m_shape.length = bytes;
m_shape.data = new char [bytes];
operands.readRawBytes(m_shape.data, bytes);
}
void Msod::opSpcontainer(Header &, TQ_UINT32 bytes, TQDataStream &operands)
{
walk(bytes, operands);
// Having gathered all the information for this shape, we can now draw it.
TQByteArray a;
a.setRawData(m_shape.data, m_shape.length);
TQDataStream s(a, IO_ReadOnly);
s.setByteOrder(TQDataStream::LittleEndian); // Great, I love TQt !
drawShape(m_shape.type, m_shape.length, s);
a.resetRawData(m_shape.data, m_shape.length);
delete [] m_shape.data;
m_shape.data = 0L;
}
void Msod::opSpgr(Header &, TQ_UINT32, TQDataStream &operands)
{
struct
{
TQ_UINT32 x;
TQ_UINT32 y;
TQ_UINT32 w;
TQ_UINT32 h;
} data;
operands >> data.x >> data.y >> data.w >> data.h;
}
void Msod::opSpgrcontainer(Header &, TQ_UINT32 bytes, TQDataStream &operands)
{
walk(bytes, operands);
}
void Msod::opSplitmenucolors(Header &, TQ_UINT32, TQDataStream &operands)
{
struct
{
TQ_UINT32 fill;
TQ_UINT32 line;
TQ_UINT32 shadow;
TQ_UINT32 threeDee;
} data;
operands >> data.fill >> data.line >> data.shadow >> data.threeDee;
}
void Msod::opTextbox(
Header &,
TQ_UINT32,
TQDataStream &)
{
}
void Msod::skip(TQ_UINT32 bytes, TQDataStream &operands)
{
if ((int)bytes < 0)
{
kdError(s_area) << "skip: " << (int)bytes << endl;
return;
}
if (bytes)
{
TQ_UINT32 i;
TQ_UINT8 discard;
kdDebug(s_area) << "skip: " << bytes << endl;
for (i = 0; i < bytes; i++)
{
operands >> discard;
}
}
}
void Msod::walk(TQ_UINT32 bytes, TQDataStream &operands)
{
Header op;
TQ_UINT32 length = 0;
// Stop parsing when there are no more records. Note that we stop as soon
// as we cannot get a complete header.
while (length + 8 <= bytes)
{
operands >> op.opcode.info >> op.cbLength;
// If we get some duff data, protect ourselves.
if (length + op.cbLength + 8 > bytes)
{
op.cbLength = bytes - length - 8;
}
length += op.cbLength + 8;
if (op.opcode.fields.fbt == 0x200)
{
// This appears to be an EOF marker.
break;
}
// Package the arguments...
invokeHandler(op, op.cbLength, operands);
}
// Eat unexpected data that the caller may expect us to consume.
skip(bytes - length, operands);
}
Msod::Options::Options(
Msod &parent) :
m_parent(parent)
{
m_pVertices = 0L;
initialise();
}
Msod::Options::~Options()
{
delete m_pVertices;
}
double Msod::Options::from1616ToDouble(TQ_UINT32 value)
{
return (value >> 16) + 65535.0 / (double)(value & 0xffff);
}
void Msod::Options::initialise()
{
m_rotation = 0.0;
m_lTxid = 0;
m_pib = 0;
m_pibName = TQString();
m_pibFlags = 0;
m_pictureId = 0;
m_fNoHitTestPicture = false;
m_pictureGray = false;
m_pictureBiLevel = false;
m_pictureActive = false;
m_geoLeft = 0;
m_geoTop = 0;
m_geoRight = 21600;
m_geoBottom = 21600;
m_shapePath = 1;
delete m_pVertices;
m_pVertices = 0L;
m_fShadowOK = true;
m_f3DOK = true;
m_fLineOK = true;
m_fGTextOK = false;
m_fFillShadeShapeOK = false;
m_fFillOK = true;
m_fFilled = true;
m_fHitTestFill = true;
m_fillShape = true;
m_fillUseRect = false;
m_fNoFillHitTest = false;
m_lineColor = 0;
m_lineBackColor = 0xffffff;
m_lineType = 0;
m_lineWidth = 9525;
m_fArrowheadsOK = false;
m_fLine = true;
m_fHitTestLine = true;
m_lineFillShape = true;
m_fNoLineDrawDash = false;
m_bWMode = 1;
m_fOleIcon = false;
m_fPreferRelativeResize = false;
m_fLockShapeType = false;
m_fDeleteAttachedObject = false;
m_fBackground = false;
}
void Msod::Options::walk(TQ_UINT32 bytes, TQDataStream &operands)
{
Header op;
TQ_UINT16 length = 0;
TQ_UINT16 complexLength = 0;
// Reset all options to default values.
initialise();
// First process all simple options, and add all complex options to a list.
TQPtrList<Header> complexOpts;
complexOpts.setAutoDelete(true);
bool unsupported;
while (length + complexLength < (int)bytes)
{
operands >> op.opcode.info >> op.value;
length += 6;
// Defer processing of complex options.
if (op.opcode.fields.fComplex)
{
complexLength += op.value;
complexOpts.append(new Header(op));
continue;
}
// Now squirrel away the option value.
unsupported = false;
switch (op.opcode.fields.pid)
{
case 4:
m_rotation = from1616ToDouble(op.value);
break;
case 128:
m_lTxid = op.value;
kdDebug(s_area) << "textbox: 0x" << TQString::number(op.value,16) << endl;
break;
case 260:
if (op.opcode.fields.fBid)
{
m_pib = op.value;
if (m_parent.m_isRequiredDrawing)
{
Image *image = m_parent.m_images[m_pib - 1];
// If it is an embedded WMF we don't bother with the
// part; we just extract it as more vector graphics.
if (image->extension == "wmf")
{
TQByteArray a;
a.setRawData(image->data, image->length);
TQDataStream s(a, IO_ReadOnly);
m_parent.KWmf::parse(s, image->length);
a.resetRawData(image->data, image->length);
}
else
{
m_parent.gotPicture(
m_pib,
image->extension,
image->length,
image->data);
}
}
}
else
{
kdError(s_area) << "Cannot handle IMsoBlip" << endl;
}
break;
case 262:
m_pibFlags = op.value;
break;
case 267:
m_pictureId = op.value;
break;
case 319:
m_fNoHitTestPicture = (op.value & 0x0008) != 0;
m_pictureGray = (op.value & 0x0004) != 0;
m_pictureBiLevel = (op.value & 0x0002) != 0;
m_pictureActive = (op.value & 0x0001) != 0;
break;
case 320:
m_geoLeft = op.value;
break;
case 321:
m_geoTop = op.value;
break;
case 322:
m_geoRight = op.value;
break;
case 323:
m_geoBottom = op.value;
break;
case 324:
m_shapePath = op.value;
break;
case 383:
m_fShadowOK = (op.value & 0x0020) != 0;
m_f3DOK = (op.value & 0x0010) != 0;
m_fLineOK = (op.value & 0x0008) != 0;
m_fGTextOK = (op.value & 0x0004) != 0;
m_fFillShadeShapeOK = (op.value & 0x0002) != 0;
m_fFillOK = (op.value & 0x0001) != 0;
break;
case 447:
m_fFilled = (op.value & 0x0010) != 0;
m_fHitTestFill = (op.value & 0x0008) != 0;
m_fillShape = (op.value & 0x0004) != 0;
m_fillUseRect = (op.value & 0x0002) != 0;
m_fNoFillHitTest = (op.value & 0x0001) != 0;
break;
case 448:
m_lineColor = op.value;
break;
case 450:
m_lineBackColor = op.value;
break;
case 452:
m_lineType = op.value;
break;
case 459:
m_lineWidth = op.value;
break;
case 511:
m_fArrowheadsOK = (op.value & 0x0010) != 0;
m_fLine = (op.value & 0x0008) != 0;
m_fHitTestLine = (op.value & 0x0004) != 0;
m_lineFillShape = (op.value & 0x0002) != 0;
m_fNoLineDrawDash = (op.value & 0x0001) != 0;
break;
case 772:
m_bWMode = op.value;
break;
case 831:
m_fOleIcon = (op.value & 0x0010) != 0;
m_fPreferRelativeResize = (op.value & 0x0008) != 0;
m_fLockShapeType = (op.value & 0x0004) != 0;
m_fDeleteAttachedObject = (op.value & 0x0002) != 0;
m_fBackground = (op.value & 0x0001) != 0;
break;
default:
unsupported = true;
kdWarning(s_area) << "unsupported simple option: " <<
op.opcode.fields.pid << endl;
break;
}
if (!unsupported)
kdDebug(s_area) << "simple option: " <<
op.opcode.fields.pid << endl;
}
// Now empty the list of complex options.
while (complexOpts.count())
{
TQ_INT16 t16;
unsigned i;
op = *complexOpts.getFirst();
complexOpts.removeFirst();
unsupported = false;
switch (op.opcode.fields.pid)
{
case 261:
while (true)
{
operands >> t16;
if (!t16)
break;
m_pibName += TQChar(t16);
};
break;
case 325:
m_pVertices = new TQPointArray(op.value / 4);
for (i = 0; i < m_pVertices->count(); i++)
{
m_pVertices->setPoint(i, m_parent.normalisePoint(operands));
};
break;
case 326:
operands >> t16;
i = t16;
operands >> t16;
operands >> t16;
m_parent.skip(i * t16, operands);
break;
default:
unsupported = true;
kdWarning(s_area) << "unsupported complex option: " <<
op.opcode.fields.pid << " operands: " << op.value << endl;
m_parent.skip(op.value, operands);
break;
}
if (!unsupported)
kdDebug(s_area) << "complex option: " <<
op.opcode.fields.pid << " operands: " << op.value << endl;
complexLength -= op.value;
}
}