diff options
Diffstat (limited to 'filters/karbon/msod/msod.cpp')
-rw-r--r-- | filters/karbon/msod/msod.cpp | 1340 |
1 files changed, 1340 insertions, 0 deletions
diff --git a/filters/karbon/msod/msod.cpp b/filters/karbon/msod/msod.cpp new file mode 100644 index 000000000..35445e18f --- /dev/null +++ b/filters/karbon/msod/msod.cpp @@ -0,0 +1,1340 @@ +/* + 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", // Verticalscroll + "HORIZONTALSCROLL", // 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; + } +} |