diff --git a/libs/vectorimage/libwmf/WmfParser.cpp b/libs/vectorimage/libwmf/WmfParser.cpp index 8ec57a6a35c..c372d2deb34 100644 --- a/libs/vectorimage/libwmf/WmfParser.cpp +++ b/libs/vectorimage/libwmf/WmfParser.cpp @@ -1,1694 +1,1694 @@ /* This file is part of the KDE libraries * * Copyright (c) 1998 Stefan Taferner * 2001/2003 thierry lorthiois (lorthioist@wanadoo.fr) * 2007-2008 Jan Hambrecht * 2009-2011 Inge Wallin * With the help of WMF documentation by Caolan Mc Namara * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include "WmfParser.h" #include "WmfAbstractBackend.h" #include #include #include #include #include #include #include #include #define DEBUG_BBOX 0 #define DEBUG_RECORDS 0 /** Namespace for Windows Metafile (WMF) classes */ namespace Libwmf { // Used for debugging of records static const struct KoWmfFunc { const char *name; } koWmfFunc[] = { // index metafunc { "end" }, // 0 0x00 { "setBkColor" }, // 1 0x01 { "setBkMode" }, // 2 0x02 { "setMapMode" }, // 3 0x03 { "setRop" }, // 4 0x04 { "setRelAbs" }, // 5 0x05 { "setPolyFillMode" }, // 6 0x06 { "setStretchBltMode" }, // 7 0x07 { "setTextCharExtra" }, // 8 0x08 { "setTextColor" }, // 9 0x09 { "setTextJustification" }, // 10 0x0a { "setWindowOrg" }, // 11 0x0b { "setWindowExt" }, // 12 0x0c { "setViewportOrg" }, // 13 0x0d { "setViewportExt" }, // 14 0x0e { "offsetWindowOrg" }, // 15 0x0f { "scaleWindowExt" }, // 16 0x10 { "offsetViewportOrg" }, // 17 0x11 { "scaleViewportExt" }, // 18 0x12 { "lineTo" }, // 19 0x13 { "moveTo" }, // 20 0x14 { "excludeClipRect" }, // 21 0x15 { "intersectClipRect" }, // 22 0x16 { "arc" }, // 23 0x17 { "ellipse" }, // 24 0x18 { "floodfill" }, // 25 0x19 floodfill { "pie" }, // 26 0x1a { "rectangle" }, // 27 0x1b { "roundRect" }, // 28 0x1c { "patBlt" }, // 29 0x1d { "saveDC" }, // 30 0x1e { "setPixel" }, // 31 0x1f { "offsetClipRegion" }, // 32 0x20 { "textOut" }, // 33 0x21 { "bitBlt" }, // 34 0x22 { "stretchBlt" }, // 35 0x23 { "polygon" }, // 36 0x24 { "polyline" }, // 37 0x25 { "escape" }, // 38 0x26 { "restoreDC" }, // 39 0x27 { "fillRegion" }, // 40 0x28 { "frameRegion" }, // 41 0x29 { "invertRegion" }, // 42 0x2a { "paintRegion" }, // 43 0x2b { "selectClipRegion" }, // 44 0x2c { "selectObject" }, // 45 0x2d { "setTextAlign" }, // 46 0x2e { "noSuchRecord" }, // 47 0x2f { "chord" }, // 48 0x30 { "setMapperFlags" }, // 49 0x31 { "extTextOut" }, // 50 0x32 { "setDibToDev" }, // 51 0x33 { "selectPalette" }, // 52 0x34 { "realizePalette" }, // 53 0x35 { "animatePalette" }, // 54 0x36 { "setPalEntries" }, // 55 0x37 { "polyPolygon" }, // 56 0x38 { "resizePalette" }, // 57 0x39 { "noSuchRecord" }, // 58 0x3a { "noSuchRecord" }, // 59 0x3b { "noSuchRecord" }, // 60 0x3c { "noSuchRecord" }, // 61 0x3d { "noSuchRecord" }, // 62 0x3e { "unimplemented" }, // 63 0x3f { "dibBitBlt" }, // 64 0x40 { "dibStretchBlt" }, // 65 0x41 { "dibCreatePatternBrush" }, // 66 0x42 { "stretchDib" }, // 67 0x43 { "noSuchRecord" }, // 68 0x44 { "noSuchRecord" }, // 69 0x45 { "noSuchRecord" }, // 70 0x46 { "noSuchRecord" }, // 71 0x47 { "extFloodFill" }, // 72 0x48 { "setLayout" }, // 73 0x49 { "unimplemented" }, // 74 0x4a { "unimplemented" }, // 75 0x4b { "resetDC" }, // 76 0x4c { "startDoc" }, // 77 0x4d { "unimplemented" }, // 78 0x4e { "startPage" }, // 79 0x4f { "endPage" }, // 80 0x50 { "unimplemented" }, // 81 0x51 { "unimplemented" }, // 82 0x52 { "unimplemented" }, // 83 0x53 { "unimplemented" }, // 84 0x54 { "unimplemented" }, // 85 0x55 { "unimplemented" }, // 86 0x56 { "unimplemented" }, // 87 0x57 { "unimplemented" }, // 88 0x58 { "unimplemented" }, // 89 0x59 { "unimplemented" }, // 90 0x5a { "unimplemented" }, // 91 0x5b { "unimplemented" }, // 92 0x5c { "unimplemented" }, // 93 0x5d { "endDoc" }, // 94 0x5e { "unimplemented" }, // 95 0x5f { "deleteObject" }, // 96 0xf0 { "noSuchRecord" }, // 97 0xf1 { "noSuchRecord" }, // 98 0xf2 { "noSuchRecord" }, // 99 0xf3 { "noSuchRecord" }, // 100 0xf4 { "noSuchRecord" }, // 101 0xf5 { "noSuchRecord" }, // 102 0xf6 { "createPalette" }, // 103 0xf7 { "createBrush" }, // 104 0xf8 { "createPatternBrush" }, // 105 0xf9 { "createPenIndirect" }, // 106 0xfa { "createFontIndirect" }, // 107 0xfb { "createBrushIndirect" }, //108 0xfc { "createBitmapIndirect" }, //109 0xfd { "createBitmap" }, // 110 0xfe { "createRegion" } // 111 0xff }; WmfParser::WmfParser() { mNbrFunc = 0; mValid = false; mStandard = false; mPlaceable = false; mEnhanced = false; mBuffer = 0; mObjHandleTab = 0; } WmfParser::~WmfParser() { if (mObjHandleTab != 0) { for (int i = 0 ; i < mNbrObject ; i++) { if (mObjHandleTab[i] != 0) delete mObjHandleTab[i]; } delete[] mObjHandleTab; } if (mBuffer != 0) { mBuffer->close(); delete mBuffer; } } bool WmfParser::load(const QByteArray& array) { // delete previous buffer if (mBuffer != 0) { mBuffer->close(); delete mBuffer; mBuffer = 0; } if (array.size() == 0) return false; // load into buffer mBuffer = new QBuffer(); mBuffer->setData(array); mBuffer->open(QIODevice::ReadOnly); // read and check the header WmfMetaHeader header; WmfEnhMetaHeader eheader; WmfPlaceableHeader pheader; // Contains a bounding box unsigned short checksum; int filePos; QDataStream stream(mBuffer); stream.setByteOrder(QDataStream::LittleEndian); mStackOverflow = false; mLayout = LAYOUT_LTR; mTextColor = Qt::black; mMapMode = MM_ANISOTROPIC; mValid = false; mStandard = false; mPlaceable = false; mEnhanced = false; // Initialize the bounding box. //mBBoxTop = 0; // The default origin is (0, 0). //mBBoxLeft = 0; mBBoxTop = 32767; mBBoxLeft = 32767; mBBoxRight = -32768; mBBoxBottom = -32768; mMaxWidth = 0; mMaxHeight = 0; #if DEBUG_RECORDS debugVectorImage << "--------------------------- Starting parsing WMF ---------------------------"; #endif stream >> pheader.key; if (pheader.key == (quint32)APMHEADER_KEY) { //----- Read placeable metafile header mPlaceable = true; #if DEBUG_RECORDS debugVectorImage << "Placeable header! Yessss!"; #endif stream >> pheader.handle; stream >> pheader.left; stream >> pheader.top; stream >> pheader.right; stream >> pheader.bottom; stream >> pheader.inch; stream >> pheader.reserved; stream >> pheader.checksum; checksum = calcCheckSum(&pheader); if (pheader.checksum != checksum) { warnVectorImage << "Checksum for placeable metafile header is incorrect ( actual checksum" << pheader.checksum << ", expected checksum" << checksum << ")"; return false; } stream >> header.fileType; stream >> header.headerSize; stream >> header.version; stream >> header.fileSize; stream >> header.numOfObjects; stream >> header.maxRecordSize; stream >> header.numOfParameters; mNbrObject = header.numOfObjects; // The bounding box of the WMF mBBoxLeft = pheader.left; mBBoxTop = pheader.top; mBBoxRight = pheader.right; mBBoxBottom = pheader.bottom; #if DEBUG_RECORDS debugVectorImage << "bounding box in header: " << mBBoxLeft << mBBoxTop << mBBoxRight << mBBoxBottom << "width, height: " << mBBoxRight - mBBoxLeft << mBBoxBottom - mBBoxTop; #endif mMaxWidth = abs(pheader.right - pheader.left); mMaxHeight = abs(pheader.bottom - pheader.top); mDpi = pheader.inch; } else { mBuffer->reset(); //----- Read as enhanced metafile header filePos = mBuffer->pos(); stream >> eheader.recordType; stream >> eheader.recordSize; stream >> eheader.boundsLeft; stream >> eheader.boundsTop; stream >> eheader.boundsRight; stream >> eheader.boundsBottom; stream >> eheader.frameLeft; stream >> eheader.frameTop; stream >> eheader.frameRight; stream >> eheader.frameBottom; stream >> eheader.signature; if (eheader.signature == ENHMETA_SIGNATURE) { mEnhanced = true; stream >> eheader.version; stream >> eheader.size; stream >> eheader.numOfRecords; stream >> eheader.numHandles; stream >> eheader.reserved; stream >> eheader.sizeOfDescription; stream >> eheader.offsetOfDescription; stream >> eheader.numPaletteEntries; stream >> eheader.widthDevicePixels; stream >> eheader.heightDevicePixels; stream >> eheader.widthDeviceMM; stream >> eheader.heightDeviceMM; } else { //----- Read as standard metafile header mStandard = true; mBuffer->seek(filePos); stream >> header.fileType; stream >> header.headerSize; stream >> header.version; stream >> header.fileSize; stream >> header.numOfObjects; stream >> header.maxRecordSize; stream >> header.numOfParameters; mNbrObject = header.numOfObjects; } } mOffsetFirstRecord = mBuffer->pos(); //----- Test header validity if (((header.headerSize == 9) && (header.numOfParameters == 0)) || (mPlaceable)) { // valid wmf file mValid = true; } else { debugVectorImage << "WmfParser : incorrect file format !"; } // check bounding rectangle for standard meta file if (mStandard && mValid) { // Note that this call can change mValid. createBoundingBox(stream); #if DEBUG_RECORDS debugVectorImage << "bounding box created by going through all records: " << mBBoxLeft << mBBoxTop << mBBoxRight << mBBoxBottom << "width, height: " << mBBoxRight - mBBoxLeft << mBBoxBottom - mBBoxTop; #endif } return mValid; } bool WmfParser::play(WmfAbstractBackend* backend) { if (!(mValid)) { debugVectorImage << "WmfParser::play : invalid WMF file"; return false; } if (mNbrFunc) { #if DEBUG_RECORDS if ((mStandard)) { debugVectorImage << "Standard :" << mBBoxLeft << "" << mBBoxTop << "" << mBBoxRight - mBBoxLeft << "" << mBBoxBottom - mBBoxTop; } else { debugVectorImage << "DPI :" << mDpi; debugVectorImage << "size (inch):" << (mBBoxRight - mBBoxLeft) / mDpi << "" << (mBBoxBottom - mBBoxTop) / mDpi; debugVectorImage << "size (mm):" << (mBBoxRight - mBBoxLeft) * 25.4 / mDpi << "" << (mBBoxBottom - mBBoxTop) * 25.4 / mDpi; } debugVectorImage << mValid << "" << mStandard << "" << mPlaceable; #endif } // Stack of handles mObjHandleTab = new KoWmfHandle* [ mNbrObject ]; for (int i = 0; i < mNbrObject ; i++) { mObjHandleTab[ i ] = 0; } mDeviceContext.reset(); quint16 recordType; quint32 size; int bufferOffset; // Create a stream from which the records will be read. QDataStream stream(mBuffer); stream.setByteOrder(QDataStream::LittleEndian); // Set the output backend. m_backend = backend; // Set some initial values. mDeviceContext.windowOrg = QPoint(0, 0); mDeviceContext.windowExt = QSize(1, 1); QRect bbox(QPoint(mBBoxLeft,mBBoxTop), QSize(mBBoxRight - mBBoxLeft, mBBoxBottom - mBBoxTop)); if (m_backend->begin(bbox)) { // Play WMF functions. mBuffer->seek(mOffsetFirstRecord); recordType = 1; while ((recordType) && (!mStackOverflow)) { int j = 1; bufferOffset = mBuffer->pos(); stream >> size; stream >> recordType; // mapping between n function and index of table 'metaFuncTab' // lower 8 digits of the function => entry in the table quint16 index = recordType & 0xff; if (index > 0x5F) { index -= 0x90; } #if DEBUG_RECORDS debugVectorImage << "Record = " << koWmfFunc[ index ].name << " (" << hex << recordType << ", index" << dec << index << ")"; #endif if (mNbrFunc) { // debug mode if ((j + 12) > mNbrFunc) { // output last 12 functions int offBuff = mBuffer->pos(); quint16 param; debugVectorImage << j << " :" << index << " :"; for (quint16 i = 0 ; i < (size - 3) ; i++) { stream >> param; debugVectorImage << param << ""; } debugVectorImage; mBuffer->seek(offBuff); } if (j >= mNbrFunc) { break; } j++; } // Execute the function and parse the record. switch (recordType & 0xff) { case (META_EOF & 0xff): // Don't need to do anything here. break; case (META_SETBKCOLOR & 0xff): { quint32 color; stream >> color; mDeviceContext.backgroundColor = qtColor(color); mDeviceContext.changedItems |= DCBgTextColor; } break; case (META_SETBKMODE & 0xff): { quint16 bkMode; stream >> bkMode; //debugVectorImage << "New bkMode: " << bkMode; mDeviceContext.bgMixMode = bkMode; mDeviceContext.changedItems |= DCBgMixMode; } break; case (META_SETMAPMODE & 0xff): { stream >> mMapMode; //debugVectorImage << "New mapmode: " << mMapMode; //mDeviceContext.FontMapMode = mMapMode;Not defined yet mDeviceContext.changedItems |= DCFontMapMode; } break; case (META_SETROP2 & 0xff): { quint16 rop; stream >> rop; m_backend->setCompositionMode(winToQtComposition(rop)); mDeviceContext.rop = rop; mDeviceContext.changedItems |= DCFgMixMode; } break; case (META_SETRELABS & 0xff): break; case (META_SETPOLYFILLMODE & 0xff): { stream >> mDeviceContext.polyFillMode; mDeviceContext.changedItems |= DCPolyFillMode; } break; case (META_SETSTRETCHBLTMODE & 0xff): case (META_SETTEXTCHAREXTRA & 0xff): break; case (META_SETTEXTCOLOR & 0xff): { quint32 color; stream >> color; mDeviceContext.foregroundTextColor = qtColor(color); mDeviceContext.changedItems |= DCFgTextColor; } break; case (META_SETTEXTJUSTIFICATION & 0xff): break; case (META_SETWINDOWORG & 0xff): { qint16 top, left; stream >> top >> left; m_backend->setWindowOrg(left, top); mDeviceContext.windowOrg = QPoint(left, top); #if DEBUG_RECORDS debugVectorImage <<"Org: (" << left <<"," << top <<")"; #endif } break; case (META_SETWINDOWEXT & 0xff): { qint16 width, height; // negative value allowed for width and height stream >> height >> width; #if DEBUG_RECORDS debugVectorImage <<"Ext: (" << width <<"," << height <<")"; #endif m_backend->setWindowExt(width, height); mDeviceContext.windowExt = QSize(width, height); } break; case (META_SETVIEWPORTORG & 0xff): { qint16 top, left; stream >> top >> left; m_backend->setViewportOrg(left, top); mDeviceContext.viewportOrg = QPoint(left, top); #if DEBUG_RECORDS debugVectorImage <<"Org: (" << left <<"," << top <<")"; #endif } break; case (META_SETVIEWPORTEXT & 0xff): { qint16 width, height; // Negative value allowed for width and height stream >> height >> width; #if DEBUG_RECORDS debugVectorImage <<"Ext: (" << width <<"," << height <<")"; #endif m_backend->setViewportExt(width, height); mDeviceContext.viewportExt = QSize(width, height); } break; case (META_OFFSETWINDOWORG & 0xff): { qint16 offTop, offLeft; stream >> offTop >> offLeft; m_backend->setWindowOrg(mDeviceContext.windowOrg.x() + offLeft, mDeviceContext.windowOrg.y() + offTop); mDeviceContext.windowOrg += QPoint(offLeft, offTop); } break; case (META_SCALEWINDOWEXT & 0xff): { // Use 32 bits in the calculations to not lose precision. qint32 width, height; qint16 heightDenum, heightNum, widthDenum, widthNum; stream >> heightDenum >> heightNum >> widthDenum >> widthNum; if ((widthDenum != 0) && (heightDenum != 0)) { width = (qint32(mDeviceContext.windowExt.width()) * widthNum) / widthDenum; height = (qint32(mDeviceContext.windowExt.height()) * heightNum) / heightDenum; m_backend->setWindowExt(width, height); mDeviceContext.windowExt = QSize(width, height); } //debugVectorImage <<"WmfParser::ScaleWindowExt :" << widthDenum <<"" << heightDenum; } break; case (META_OFFSETVIEWPORTORG & 0xff): { qint16 offTop, offLeft; stream >> offTop >> offLeft; m_backend->setViewportOrg(mDeviceContext.windowOrg.x() + offLeft, mDeviceContext.windowOrg.y() + offTop); mDeviceContext.viewportOrg += QPoint(offLeft, offTop); } break; case (META_SCALEVIEWPORTEXT & 0xff): { // Use 32 bits in the calculations to not lose precision. qint32 width, height; qint16 heightDenum, heightNum, widthDenum, widthNum; stream >> heightDenum >> heightNum >> widthDenum >> widthNum; if ((widthDenum != 0) && (heightDenum != 0)) { width = (qint32(mDeviceContext.windowExt.width()) * widthNum) / widthDenum; height = (qint32(mDeviceContext.windowExt.height()) * heightNum) / heightDenum; m_backend->setViewportExt(width, height); mDeviceContext.viewportExt = QSize(width, height); } //debugVectorImage <<"WmfParser::ScaleWindowExt :" << widthDenum <<"" << heightDenum; } break; // ---------------------------------------------------------------- // Drawing records case (META_LINETO & 0xff): { qint16 top, left; stream >> top >> left; m_backend->lineTo(mDeviceContext, left, top); } break; case (META_MOVETO & 0xff): { qint16 top, left; stream >> top >> left; mDeviceContext.currentPosition = QPoint(left, top); } break; case (META_EXCLUDECLIPRECT & 0xff): { qint16 top, left, right, bottom; stream >> bottom >> right >> top >> left; QRegion region = mDeviceContext.clipRegion; QRegion newRegion(left, top, right - left, bottom - top); if (region.isEmpty()) { // FIXME: I doubt that if the region is previously empty, // it should be set to the new region. /iw region = newRegion; } else { region = region.subtract(newRegion); } mDeviceContext.clipRegion = region; mDeviceContext.changedItems |= DCClipRegion; } break; case (META_INTERSECTCLIPRECT & 0xff): { qint16 top, left, right, bottom; stream >> bottom >> right >> top >> left; QRegion region = mDeviceContext.clipRegion; QRegion newRegion(left, top, right - left, bottom - top); if (region.isEmpty()) { // FIXME: I doubt that if the region is previously empty, // it should be set to the new region. /iw region = newRegion; } else { - region = region.intersect(newRegion); + region = region.intersected(newRegion); } mDeviceContext.clipRegion = region; mDeviceContext.changedItems |= DCClipRegion; } break; case (META_ARC & 0xff): { int xCenter, yCenter, angleStart, aLength; qint16 topEnd, leftEnd, topStart, leftStart; qint16 top, left, right, bottom; stream >> topEnd >> leftEnd >> topStart >> leftStart; stream >> bottom >> right >> top >> left; xCenter = left + ((right - left) / 2); yCenter = top + ((bottom - top) / 2); xyToAngle(leftStart - xCenter, yCenter - topStart, leftEnd - xCenter, yCenter - topEnd, angleStart, aLength); m_backend->drawArc(mDeviceContext, left, top, right - left, bottom - top, angleStart, aLength); } break; case (META_ELLIPSE & 0xff): { qint16 top, left, right, bottom; stream >> bottom >> right >> top >> left; m_backend->drawEllipse(mDeviceContext, left, top, right - left, bottom - top); } break; case (META_FLOODFILL & 0xff): break; case (META_PIE & 0xff): { int xCenter, yCenter, angleStart, aLength; qint16 topEnd, leftEnd, topStart, leftStart; qint16 top, left, right, bottom; stream >> topEnd >> leftEnd >> topStart >> leftStart; stream >> bottom >> right >> top >> left; xCenter = left + ((right - left) / 2); yCenter = top + ((bottom - top) / 2); xyToAngle(leftStart - xCenter, yCenter - topStart, leftEnd - xCenter, yCenter - topEnd, angleStart, aLength); m_backend->drawPie(mDeviceContext, left, top, right - left, bottom - top, angleStart, aLength); } break; case (META_RECTANGLE & 0xff): { qint16 top, left, right, bottom; stream >> bottom >> right >> top >> left; //debugVectorImage << left << top << right << bottom; m_backend->drawRect(mDeviceContext, left, top, right - left, bottom - top); } break; case (META_ROUNDRECT & 0xff): { int xRnd = 0, yRnd = 0; quint16 widthCorner, heightCorner; qint16 top, left, right, bottom; stream >> heightCorner >> widthCorner; stream >> bottom >> right >> top >> left; // convert (widthCorner, heightCorner) in percentage if ((right - left) != 0) xRnd = (widthCorner * 100) / (right - left); if ((bottom - top) != 0) yRnd = (heightCorner * 100) / (bottom - top); m_backend->drawRoundRect(mDeviceContext, left, top, right - left, bottom - top, xRnd, yRnd); } break; case (META_PATBLT & 0xff): { quint32 rasterOperation; quint16 height, width; qint16 y, x; stream >> rasterOperation; stream >> height >> width; stream >> y >> x; //debugVectorImage << "patBlt record" << hex << rasterOperation << dec // << x << y << width << height; m_backend->patBlt(mDeviceContext, x, y, width, height, rasterOperation); } break; case (META_SAVEDC & 0xff): m_backend->save(); break; case (META_SETPIXEL & 0xff): { qint16 left, top; quint32 color; stream >> color >> top >> left; m_backend->setPixel(mDeviceContext, left, top, qtColor(color)); } break; case (META_OFFSETCLIPRGN & 0xff): break; case (META_TEXTOUT & 0xff): { quint16 textLength; qint16 x, y; stream >> textLength; QByteArray text; text.resize(textLength); stream.readRawData(text.data(), textLength); // The string is always of even length, so if the actual data is // of uneven length, read an extra byte. if (textLength & 0x01) { quint8 dummy; stream >> dummy; } stream >> y; stream >> x; m_backend->drawText(mDeviceContext, x, y, text); } break; case (META_BITBLT & 0xff): case (META_STRETCHBLT & 0xff): break; case (META_POLYGON & 0xff): { quint16 num; stream >> num; QPolygon pa(num); pointArray(stream, pa); m_backend->drawPolygon(mDeviceContext, pa); } break; case (META_POLYLINE & 0xff): { quint16 num; stream >> num; QPolygon pa(num); pointArray(stream, pa); m_backend->drawPolyline(mDeviceContext, pa); } break; case (META_ESCAPE & 0xff): break; case (META_RESTOREDC & 0xff): { qint16 num; stream >> num; for (int i = 0; i > num ; i--) m_backend->restore(); } break; case (META_FILLREGION & 0xff): case (META_FRAMEREGION & 0xff): case (META_INVERTREGION & 0xff): case (META_PAINTREGION & 0xff): case (META_SELECTCLIPREGION & 0xff): break; case (META_SELECTOBJECT & 0xff): { quint16 idx; stream >> idx; if ((idx < mNbrObject) && (mObjHandleTab[ idx ] != 0)) mObjHandleTab[ idx ]->apply(&mDeviceContext); else debugVectorImage << "WmfParser::selectObject : selection of an empty object"; } break; case (META_SETTEXTALIGN & 0xff): stream >> mDeviceContext.textAlign; mDeviceContext.changedItems |= DCTextAlignMode; break; case (META_CHORD & 0xff): { int xCenter, yCenter, angleStart, aLength; qint16 topEnd, leftEnd, topStart, leftStart; qint16 top, left, right, bottom; stream >> topEnd >> leftEnd >> topStart >> leftStart; stream >> bottom >> right >> top >> left; xCenter = left + ((right - left) / 2); yCenter = top + ((bottom - top) / 2); xyToAngle(leftStart - xCenter, yCenter - topStart, leftEnd - xCenter, yCenter - topEnd, angleStart, aLength); m_backend->drawChord(mDeviceContext, left, top, right - left, bottom - top, angleStart, aLength); } break; case (META_SETMAPPERFLAGS & 0xff): break; case (META_EXTTEXTOUT & 0xff): { qint16 y, x; qint16 stringLength; quint16 fwOpts; qint16 top, left, right, bottom; // optional cliprect stream >> y; stream >> x; stream >> stringLength; stream >> fwOpts; // ETO_CLIPPED flag adds 4 parameters if (fwOpts & (ETO_CLIPPED | ETO_OPAQUE)) { // read the optional clip rect stream >> bottom >> right >> top >> left; } // Read the string. Note that it's padded to 16 bits. QByteArray text; text.resize(stringLength); stream.readRawData(text.data(), stringLength); if (stringLength & 0x01) { quint8 padding; stream >> padding; } #if DEBUG_RECORDS debugVectorImage << "text at" << x << y << "length" << stringLength << ':' << text; //debugVectorImage << "flags:" << hex << fwOpts << dec; debugVectorImage << "flags:" << fwOpts; debugVectorImage << "record length:" << size; #endif m_backend->drawText(mDeviceContext, x, y, text); } break; case (META_SETDIBTODEV & 0xff): case (META_SELECTPALETTE & 0xff): case (META_REALIZEPALETTE & 0xff): case (META_ANIMATEPALETTE & 0xff): case (META_SETPALENTRIES & 0xff): break; case (META_POLYPOLYGON & 0xff): { quint16 numberPoly; quint16 sizePoly; QList listPa; stream >> numberPoly; for (int i = 0 ; i < numberPoly ; i++) { stream >> sizePoly; listPa.append(QPolygon(sizePoly)); } // list of point array for (int i = 0; i < numberPoly; i++) { pointArray(stream, listPa[i]); } // draw polygon's m_backend->drawPolyPolygon(mDeviceContext, listPa); listPa.clear(); } break; case (META_RESIZEPALETTE & 0xff): break; case (META_DIBBITBLT & 0xff): { quint32 raster; qint16 topSrc, leftSrc, widthSrc, heightSrc; qint16 topDst, leftDst; stream >> raster; stream >> topSrc >> leftSrc >> heightSrc >> widthSrc; stream >> topDst >> leftDst; if (size > 11) { // DIB image QImage bmpSrc; if (dibToBmp(bmpSrc, stream, (size - 11) * 2)) { m_backend->setCompositionMode(winToQtComposition(raster)); m_backend->save(); if (widthSrc < 0) { // negative width => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } if (heightSrc < 0) { // negative height => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } m_backend->drawImage(mDeviceContext, leftDst, topDst, bmpSrc, leftSrc, topSrc, widthSrc, heightSrc); m_backend->restore(); } } else { debugVectorImage << "WmfParser::dibBitBlt without image not implemented"; } } break; case (META_DIBSTRETCHBLT & 0xff): { quint32 raster; qint16 topSrc, leftSrc, widthSrc, heightSrc; qint16 topDst, leftDst, widthDst, heightDst; QImage bmpSrc; stream >> raster; stream >> heightSrc >> widthSrc >> topSrc >> leftSrc; stream >> heightDst >> widthDst >> topDst >> leftDst; if (dibToBmp(bmpSrc, stream, (size - 13) * 2)) { m_backend->setCompositionMode(winToQtComposition(raster)); m_backend->save(); if (widthDst < 0) { // negative width => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } if (heightDst < 0) { // negative height => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } bmpSrc = bmpSrc.copy(leftSrc, topSrc, widthSrc, heightSrc); // TODO: scale the bitmap : QImage::scale(widthDst, heightDst) // is actually too slow m_backend->drawImage(mDeviceContext, leftDst, topDst, bmpSrc); m_backend->restore(); } } break; case (META_DIBCREATEPATTERNBRUSH & 0xff): { KoWmfPatternBrushHandle* handle = new KoWmfPatternBrushHandle; if (addHandle(handle)) { quint32 arg; QImage bmpSrc; stream >> arg; if (dibToBmp(bmpSrc, stream, (size - 5) * 2)) { handle->image = bmpSrc; handle->brush.setTextureImage(handle->image); } else { debugVectorImage << "WmfParser::dibCreatePatternBrush : incorrect DIB image"; } } } break; case (META_STRETCHDIB & 0xff): { quint32 raster; qint16 arg, topSrc, leftSrc, widthSrc, heightSrc; qint16 topDst, leftDst, widthDst, heightDst; QImage bmpSrc; stream >> raster >> arg; stream >> heightSrc >> widthSrc >> topSrc >> leftSrc; stream >> heightDst >> widthDst >> topDst >> leftDst; if (dibToBmp(bmpSrc, stream, (size - 14) * 2)) { m_backend->setCompositionMode(winToQtComposition(raster)); m_backend->save(); if (widthDst < 0) { // negative width => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } if (heightDst < 0) { // negative height => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } bmpSrc = bmpSrc.copy(leftSrc, topSrc, widthSrc, heightSrc); // TODO: scale the bitmap ( QImage::scale(param[ 8 ], param[ 7 ]) is actually too slow ) m_backend->drawImage(mDeviceContext, leftDst, topDst, bmpSrc); m_backend->restore(); } } break; case (META_EXTFLOODFILL & 0xff): break; case (META_SETLAYOUT & 0xff): { quint16 layout; quint16 reserved; // negative value allowed for width and height stream >> layout >> reserved; #if DEBUG_RECORDS debugVectorImage << "layout=" << layout; #endif mLayout = (WmfLayout)layout; mDeviceContext.layoutMode = mLayout; mDeviceContext.changedItems |= DCLayoutMode; } break; case (META_DELETEOBJECT & 0xff): { quint16 idx; stream >> idx; deleteHandle(idx); } break; case (META_CREATEPALETTE & 0xff): // Unimplemented createEmptyObject(); break; case (META_CREATEBRUSH & 0xff): case (META_CREATEPATTERNBRUSH & 0xff): break; case (META_CREATEPENINDIRECT & 0xff): { // TODO: userStyle and alternateStyle quint32 color; quint16 style, width, arg; KoWmfPenHandle* handle = new KoWmfPenHandle; if (addHandle(handle)) { stream >> style >> width >> arg >> color; // set the style defaults handle->pen.setStyle(Qt::SolidLine); handle->pen.setCapStyle(Qt::RoundCap); handle->pen.setJoinStyle(Qt::RoundJoin); const int PenStyleMask = 0x0000000F; const int PenCapMask = 0x00000F00; const int PenJoinMask = 0x0000F000; quint16 penStyle = style & PenStyleMask; if (penStyle < 7) handle->pen.setStyle(koWmfStylePen[ penStyle ]); else debugVectorImage << "WmfParser::createPenIndirect: invalid pen" << style; quint16 capStyle = (style & PenCapMask) >> 8; if (capStyle < 3) handle->pen.setCapStyle(koWmfCapStylePen[ capStyle ]); else debugVectorImage << "WmfParser::createPenIndirect: invalid pen cap style" << style; quint16 joinStyle = (style & PenJoinMask) >> 12; if (joinStyle < 3) handle->pen.setJoinStyle(koWmfJoinStylePen[ joinStyle ]); else debugVectorImage << "WmfParser::createPenIndirect: invalid pen join style" << style; handle->pen.setColor(qtColor(color)); handle->pen.setWidth(width); debugVectorImage << "Creating pen" << handle->pen; } } break; case (META_CREATEFONTINDIRECT & 0xff): { qint16 height; // Height of the character cell qint16 width; // Average width (not used) qint16 escapement; // The rotation of the text in 1/10th degrees qint16 orientation; // The rotation of each character quint16 weight, property, fixedPitch, arg; KoWmfFontHandle* handle = new KoWmfFontHandle; if (addHandle(handle)) { stream >> height >> width; stream >> escapement >> orientation; stream >> weight >> property >> arg >> arg; stream >> fixedPitch; //debugVectorImage << height << width << weight << property; // text rotation (in 1/10 degree) handle->font.setFixedPitch(((fixedPitch & 0x01) == 0)); handle->escapement = escapement; handle->orientation = orientation; // A negative height means to use device units. //debugVectorImage << "Font height:" << height; handle->height = height; // FIXME: For some reason this value needs to be multiplied by // a factor. 0.6 seems to give a good result, but why?? // ANSWER(?): The doc says the height is the height of the character cell. // But normally the font height is only the height above the // baseline, isn't it? handle->font.setPointSize(qAbs(height) * 6 / 10); if (weight == 0) weight = QFont::Normal; else { // Linear transform between MS weights to Qt weights // MS: 400=normal, 700=bold // Qt: 50=normal, 75=bold // This makes the line cross x=0 at y=50/3. (x=MS weight, y=Qt weight) // // FIXME: Is this a linear relationship? weight = (50 + 3 * ((weight * (75-50))/(700-400))) / 3; } handle->font.setWeight(weight); handle->font.setItalic((property & 0x01)); handle->font.setUnderline((property & 0x100)); // TODO: Strikethrough // font name int maxChar = (size - 12) * 2; char* nameFont = new char[maxChar]; stream.readRawData(nameFont, maxChar); handle->font.setFamily(nameFont); delete[] nameFont; } } break; case (META_CREATEBRUSHINDIRECT & 0xff): { Qt::BrushStyle style; quint16 sty, arg2; quint32 color; KoWmfBrushHandle* handle = new KoWmfBrushHandle; if (addHandle(handle)) { stream >> sty >> color >> arg2; if (sty == 2) { if (arg2 < 6) style = koWmfHatchedStyleBrush[ arg2 ]; else { debugVectorImage << "WmfParser::createBrushIndirect: invalid hatched brush" << arg2; style = Qt::SolidPattern; } } else { if (sty < 9) style = koWmfStyleBrush[ sty ]; else { debugVectorImage << "WmfParser::createBrushIndirect: invalid brush" << sty; style = Qt::SolidPattern; } } handle->brush.setStyle(style); handle->brush.setColor(qtColor(color)); } } break; #if 0 UNSPECIFIED in the Spec: { &WmfParser::createBitmapIndirect, "createBitmapIndirect" }, //109 0xfd { &WmfParser::createBitmap, "createBitmap" }, // 110 0xfe #endif case (META_CREATEREGION & 0xff): // FIXME: Unimplemented createEmptyObject(); break; default: // function outside WMF specification errorVectorImage << "BROKEN WMF file: Record number" << hex << recordType << dec << " index " << index; mValid = false; break; } mBuffer->seek(bufferOffset + (size << 1)); } // Let the backend clean up it's internal state. m_backend->end(); } for (int i = 0 ; i < mNbrObject ; i++) { if (mObjHandleTab[ i ] != 0) delete mObjHandleTab[ i ]; } delete[] mObjHandleTab; mObjHandleTab = 0; return true; } //----------------------------------------------------------------------------- void WmfParser::createBoundingBox(QDataStream &stream) { // Check bounding rectangle for standard meta file. // This calculation is done in device coordinates. if (!mStandard || !mValid) return; bool windowExtIsSet = false; bool viewportExtIsSet = false; quint16 recordType = 1; quint32 size; int filePos; // Search for records setWindowOrg and setWindowExt to // determine what the total bounding box of this WMF is. // This initialization assumes that setWindowOrg comes before setWindowExt. qint16 windowOrgX = 0; qint16 windowOrgY = 0; qint16 windowWidth = 0; qint16 windowHeight = 0; qint16 viewportOrgX = 0; qint16 viewportOrgY = 0; qint16 viewportWidth = 0; qint16 viewportHeight = 0; bool bboxRecalculated = false; while (recordType) { filePos = mBuffer->pos(); stream >> size >> recordType; if (size == 0) { debugVectorImage << "WmfParser: incorrect file!"; mValid = 0; return; } bool doRecalculateBBox = false; qint16 orgX = 0; qint16 orgY = 0; qint16 extX = 0; qint16 extY = 0; switch (recordType &= 0xFF) { case 11: // setWindowOrg { stream >> windowOrgY >> windowOrgX; #if DEBUG_BBOX debugVectorImage << "setWindowOrg" << windowOrgX << windowOrgY; #endif if (!windowExtIsSet) break; // The bounding box doesn't change just because we get // a new window. Remember we are working in device // (viewport) coordinates when deciding the bounding // box. if (viewportExtIsSet) break; // If there is no viewport, then use the window ext as // size, and (0, 0) as origin. // // FIXME: Handle the case where the window is defined // first and then the viewport, without any // drawing in between. If that happens, I // don't think that the window definition // should influence the bounding box. orgX = 0; orgY = 0; extX = windowWidth; extY = windowHeight; } break; case 12: // setWindowExt { stream >> windowHeight >> windowWidth; windowExtIsSet = true; bboxRecalculated = false; #if DEBUG_BBOX debugVectorImage << "setWindowExt" << windowWidth << windowHeight << "(viewportOrg = " << viewportOrgX << viewportOrgY << ")"; #endif // If the viewport is set, then a changed window // changes nothing in the bounding box. if (viewportExtIsSet) break; bboxRecalculated = false; // Collect the maximum width and height. if (abs(windowWidth - windowOrgX) > mMaxWidth) mMaxWidth = abs(windowWidth - windowOrgX); if (abs(windowHeight - windowOrgY) > mMaxHeight) mMaxHeight = abs(windowHeight - windowOrgY); orgX = 0; orgY = 0; extX = windowWidth; extY = windowHeight; } break; case 13: //setViewportOrg { stream >> viewportOrgY >> viewportOrgX; bboxRecalculated = false; #if DEBUG_BBOX debugVectorImage << "setViewportOrg" << viewportOrgX << viewportOrgY; #endif orgX = viewportOrgX; orgY = viewportOrgY; if (viewportExtIsSet) { extX = viewportWidth; extY = viewportHeight; } else { // If the viewportExt is not set, then either a // subsequent setViewportExt will set it, or the // windowExt will be used instead. extX = windowWidth; extY = windowHeight; } break; // FIXME: Handle the case where the org changes but // there is no subsequent Ext change (should be // rather uncommon). } break; case 14: //setViewportExt { stream >> viewportHeight >> viewportWidth; viewportExtIsSet = true; bboxRecalculated = false; #if DEBUG_BBOX debugVectorImage << "setViewportExt" << viewportWidth << viewportHeight; #endif orgX = viewportOrgX; orgY = viewportOrgY; extX = viewportWidth; extY = viewportHeight; } break; // FIXME: Also support: // ScaleWindowExt, ScaleViewportExt, // OffsetWindowOrg, OffsetViewportOrg // The following are drawing commands. It is only when // there is an actual drawing command that we should check // the bounding box. It seems that some WMF files have // lots of changes of the window or viewports but without // any drawing commands in between. These changes should // not affect the bounding box. case 19: // lineTo //case 20: // moveTo case 23: // arc case 24: // ellipse case 26: // pie case 27: // rectangle case 28: // roundRect case 29: // patBlt case 31: // setPixel case 33: // textOut case 34: // bitBlt case 36: // polygon case 37: // polyline //case 38: // escape FIXME: is this a drawing command? case 40: // fillRegion case 41: case 42: case 43: case 44: case 48: // chord case 50: // extTextOut case 56: // polyPolygon case 64: // dibBitBlt case 65: // dibStretchBlt case 67: // stretchDib case 72: // extFloodFill #if DEBUG_BBOX debugVectorImage << "drawing record: " << (recordType & 0xff); #endif doRecalculateBBox = true; break; default: ; } // Recalculate the BBox if it was indicated above that it should be. if (doRecalculateBBox && !bboxRecalculated) { #if DEBUG_BBOX debugVectorImage << "Recalculating BBox"; #endif // If we have a viewport, always use that one. if (viewportExtIsSet) { orgX = viewportOrgX; orgY = viewportOrgY; extX = viewportWidth; extY = viewportHeight; } else { // If there is no defined viewport, then use the // window as the fallback viewport. But only the size, // the origin is always (0, 0). orgX = 0; orgY = 0; extX = qAbs(windowWidth); extY = qAbs(windowHeight); } // If ext < 0, switch the org and org+ext if (extX < 0) { orgX += extX; extX = -extX; } if (extY < 0) { orgY += extY; extY = -extY; } // At this point, the ext is always >= 0, i.e. org <= org+ext #if DEBUG_BBOX debugVectorImage << orgX << orgY << extX << extY; #endif if (orgX < mBBoxLeft) mBBoxLeft = orgX; if (orgY < mBBoxTop) mBBoxTop = orgY; if (orgX + extX > mBBoxRight) mBBoxRight = orgX + extX; if (orgY + extY > mBBoxBottom) mBBoxBottom = orgY + extY; bboxRecalculated = true; } #if DEBUG_BBOX if (isOrgOrExt) { debugVectorImage << " mBBoxTop = " << mBBoxTop; debugVectorImage << "mBBoxLeft = " << mBBoxLeft << " mBBoxRight = " << mBBoxRight; debugVectorImage << " MBBoxBotton = " << mBBoxBottom; debugVectorImage << "Max width,height = " << mMaxWidth << mMaxHeight; } #endif mBuffer->seek(filePos + (size << 1)); } } //----------------------------------------------------------------------------- // Object handle void WmfParser::createEmptyObject() { // allocation of an empty object (to keep object counting in sync) KoWmfPenHandle* handle = new KoWmfPenHandle; addHandle(handle); } //----------------------------------------------------------------------------- // Misc functions quint16 WmfParser::calcCheckSum(WmfPlaceableHeader* apmfh) { quint16* lpWord; quint16 wResult, i; // Start with the first word wResult = *(lpWord = (quint16*)(apmfh)); // XOR in each of the other 9 words for (i = 1; i <= 9; i++) { wResult ^= lpWord[ i ]; } return wResult; } //----------------------------------------------------------------------------- // Utilities and conversion Wmf -> Qt bool WmfParser::addHandle(KoWmfHandle* handle) { int idx; for (idx = 0; idx < mNbrObject ; idx++) { if (mObjHandleTab[ idx ] == 0) break; } if (idx < mNbrObject) { mObjHandleTab[ idx ] = handle; return true; } else { delete handle; mStackOverflow = true; debugVectorImage << "WmfParser::addHandle : stack overflow = broken file !"; return false; } } void WmfParser::deleteHandle(int idx) { if ((idx < mNbrObject) && (mObjHandleTab[idx] != 0)) { delete mObjHandleTab[ idx ]; mObjHandleTab[ idx ] = 0; } else { debugVectorImage << "WmfParser::deletehandle() : bad index number"; } } void WmfParser::pointArray(QDataStream& stream, QPolygon& pa) { qint16 left, top; int i, max; for (i = 0, max = pa.size() ; i < max ; i++) { stream >> left >> top; pa.setPoint(i, left, top); } } void WmfParser::xyToAngle(int xStart, int yStart, int xEnd, int yEnd, int& angleStart, int& angleLength) { double aStart, aLength; aStart = atan2((double)yStart, (double)xStart); aLength = atan2((double)yEnd, (double)xEnd) - aStart; angleStart = (int)((aStart * 2880) / 3.14166); angleLength = (int)((aLength * 2880) / 3.14166); if (angleLength < 0) angleLength = 5760 + angleLength; } QPainter::CompositionMode WmfParser::winToQtComposition(quint16 param) const { if (param < 17) return koWmfOpTab16[ param ]; else return QPainter::CompositionMode_Source; } QPainter::CompositionMode WmfParser::winToQtComposition(quint32 param) const { /* TODO: Ternary raster operations 0x00C000CA dest = (source AND pattern) 0x00F00021 dest = pattern 0x00FB0A09 dest = DPSnoo 0x005A0049 dest = pattern XOR dest */ int i; for (i = 0 ; i < 15 ; i++) { if (koWmfOpTab32[ i ].winRasterOp == param) break; } if (i < 15) return koWmfOpTab32[ i ].qtRasterOp; else return QPainter::CompositionMode_SourceOver; } bool WmfParser::dibToBmp(QImage& bmp, QDataStream& stream, quint32 size) { typedef struct _BMPFILEHEADER { quint16 bmType; quint32 bmSize; quint16 bmReserved1; quint16 bmReserved2; quint32 bmOffBits; } BMPFILEHEADER; int sizeBmp = size + 14; QByteArray pattern; // BMP header and DIB data pattern.resize(sizeBmp); pattern.fill(0); stream.readRawData(pattern.data() + 14, size); // add BMP header BMPFILEHEADER* bmpHeader; bmpHeader = (BMPFILEHEADER*)(pattern.data()); bmpHeader->bmType = 0x4D42; bmpHeader->bmSize = sizeBmp; // if ( !bmp.loadFromData( (const uchar*)bmpHeader, pattern.size(), "BMP" ) ) { if (!bmp.loadFromData(pattern, "BMP")) { debugVectorImage << "WmfParser::dibToBmp: invalid bitmap"; return false; } else { return true; } } } diff --git a/libs/vectorimage/libwmf/qwmf.cc b/libs/vectorimage/libwmf/qwmf.cc index dffe0e95e92..910401b5e75 100644 --- a/libs/vectorimage/libwmf/qwmf.cc +++ b/libs/vectorimage/libwmf/qwmf.cc @@ -1,1250 +1,1250 @@ /* Windows Meta File Loader/Painter Class Implementation * * Copyright ( C ) 1998 Stefan Taferner * Modified 2002 thierry lorthiois * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or ( at your * option ) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. You should have received a copy * of the GNU General Public License along with this program; if not, write * to the Free Software Foundation, Inc, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include // WORDS_BIGENDIAN #include #include #include #include #include #include #include #include #include #include #include bool qwmfDebug = false; #include "qwmf.h" #include "wmfstruct.h" #include "metafuncs.h" #define QWMF_DEBUG 0 class WmfCmd { public: ~WmfCmd() { if (next) delete next; } WmfCmd* next; unsigned short funcIndex; long numParm; short* parm; }; class WinObjHandle { public: virtual ~WinObjHandle() {} virtual void apply(QPainter& p) = 0; }; class WinObjBrushHandle: public WinObjHandle { public: virtual void apply(QPainter& p); QBrush brush; virtual ~WinObjBrushHandle() {} }; class WinObjPenHandle: public WinObjHandle { public: virtual void apply(QPainter& p); QPen pen; virtual ~WinObjPenHandle() {} }; class WinObjPatternBrushHandle: public WinObjHandle { public: virtual void apply(QPainter& p); QBrush brush; QImage image; virtual ~WinObjPatternBrushHandle() {} }; class WinObjFontHandle: public WinObjHandle { public: virtual void apply(QPainter& p); QFont font; int rotation; virtual ~WinObjFontHandle() {} }; void WinObjBrushHandle::apply(QPainter& p) { p.setBrush(brush); } void WinObjPenHandle::apply(QPainter& p) { p.setPen(pen); } void WinObjPatternBrushHandle::apply(QPainter& p) { p.setBrush(brush); } void WinObjFontHandle::apply(QPainter& p) { p.setFont(font); } #define MAX_OBJHANDLE 64 //----------------------------------------------------------------------------- QWinMetaFile::QWinMetaFile() { mValid = false; mFirstCmd = NULL; mObjHandleTab = NULL; mDpi = 1000; } //----------------------------------------------------------------------------- QWinMetaFile::~QWinMetaFile() { if (mFirstCmd) delete mFirstCmd; if (mObjHandleTab) delete[] mObjHandleTab; } //----------------------------------------------------------------------------- bool QWinMetaFile::load(const QString &filename) { QFile file(filename); if (!file.exists()) { debugVectorImage() << "File" << QFile::encodeName(filename) << " does not exist"; return false; } if (!file.open(QIODevice::ReadOnly)) { debugVectorImage() << "Cannot open file" << QFile::encodeName(filename); return false; } QByteArray ba = file.readAll(); file.close(); QBuffer buffer(&ba); buffer.open(QIODevice::ReadOnly); return load(buffer); } //----------------------------------------------------------------------------- bool QWinMetaFile::load(QBuffer &buffer) { QDataStream st; WmfEnhMetaHeader eheader; WmfMetaHeader header; WmfPlaceableHeader pheader; WORD checksum; int filePos, idx, i; WmfCmd *cmd, *last; DWORD rdSize; WORD rdFunc; mTextAlign = 0; mRotation = 0; mTextColor = Qt::black; if (mFirstCmd) delete mFirstCmd; mFirstCmd = NULL; st.setDevice(&buffer); st.setByteOrder(QDataStream::LittleEndian); // Great, I love Qt ! //----- Read placeable metafile header st >> pheader.key; mIsPlaceable = (pheader.key == (DWORD)APMHEADER_KEY); if (mIsPlaceable) { st >> pheader.hmf; st >> pheader.bbox.left; st >> pheader.bbox.top; st >> pheader.bbox.right; st >> pheader.bbox.bottom; st >> pheader.inch; st >> pheader.reserved; st >> pheader.checksum; checksum = calcCheckSum(&pheader); if (pheader.checksum != checksum) mIsPlaceable = false; mDpi = pheader.inch; mBBox.setLeft(pheader.bbox.left); mBBox.setTop(pheader.bbox.top); mBBox.setRight(pheader.bbox.right); mBBox.setBottom(pheader.bbox.bottom); mHeaderBoundingBox = mBBox; if (QWMF_DEBUG) { debugVectorImage() << endl << "-------------------------------------------------"; debugVectorImage() << "WMF Placeable Header (" << static_cast(sizeof(pheader)) << "):"; debugVectorImage() << " bbox=(" << mBBox.left() << ";" << mBBox.top() << ";" << mBBox.width() << "; " << mBBox.height() << ")" << endl; debugVectorImage() << " inch=" << pheader.inch; debugVectorImage() << " checksum=" << pheader.checksum << "(" << (pheader.checksum == checksum ? "ok" : "wrong") << " )" << endl; } } else buffer.reset(); //----- Read as enhanced metafile header filePos = buffer.pos(); st >> eheader.iType; st >> eheader.nSize; st >> eheader.rclBounds.left; st >> eheader.rclBounds.top; st >> eheader.rclBounds.right; st >> eheader.rclBounds.bottom; st >> eheader.rclFrame.left; st >> eheader.rclFrame.top; st >> eheader.rclFrame.right; st >> eheader.rclFrame.bottom; st >> eheader.dSignature; mIsEnhanced = (eheader.dSignature == ENHMETA_SIGNATURE); if (mIsEnhanced) { // is it really enhanced ? st >> eheader.nVersion; st >> eheader.nBytes; st >> eheader.nRecords; st >> eheader.nHandles; st >> eheader.sReserved; st >> eheader.nDescription; st >> eheader.offDescription; st >> eheader.nPalEntries; st >> eheader.szlDevice.width; st >> eheader.szlDevice.height; st >> eheader.szlMillimeters.width; st >> eheader.szlMillimeters.height; if (QWMF_DEBUG) { debugVectorImage() << endl << "-------------------------------------------------"; debugVectorImage() << "WMF Extended Header:"; debugVectorImage() << " iType=" << eheader.iType; debugVectorImage() << " nSize=" << eheader.nSize; debugVectorImage() << " rclBounds=(" << eheader.rclBounds.left << ";" << eheader.rclBounds.top << ";" << eheader.rclBounds.right << "; " << eheader.rclBounds.bottom << ")" << endl; debugVectorImage() << " rclFrame=(" << eheader.rclFrame.left << ";" << eheader.rclFrame.top << ";" << eheader.rclFrame.right << "; " << eheader.rclFrame.bottom << ")" << endl; debugVectorImage() << " nBytes=" << eheader.nBytes; debugVectorImage() << "\nNOT YET IMPLEMENTED, SORRY."; } } else { // no, not enhanced //----- Read as standard metafile header buffer.seek(filePos); st >> header.mtType; st >> header.mtHeaderSize; st >> header.mtVersion; st >> header.mtSize; st >> header.mtNoObjects; st >> header.mtMaxRecord; st >> header.mtNoParameters; if (QWMF_DEBUG) { debugVectorImage() << "WMF Header:" << "mtSize=" << header.mtSize; } } //----- Test header validity mValid = ((header.mtHeaderSize == 9) && (header.mtNoParameters == 0)) || mIsEnhanced || mIsPlaceable; if (mValid) { //----- Read Metafile Records last = NULL; rdFunc = -1; while (!st.atEnd() && (rdFunc != 0)) { st >> rdSize; st >> rdFunc; idx = findFunc(rdFunc); rdSize -= 3; cmd = new WmfCmd; cmd->next = NULL; if (last) last->next = cmd; else mFirstCmd = cmd; cmd->funcIndex = idx; cmd->numParm = rdSize; cmd->parm = new WORD[ rdSize ]; last = cmd; for (i = 0; i < rdSize && !st.atEnd(); i++) st >> cmd->parm[ i ]; if (rdFunc == 0x020B) { // SETWINDOWORG: dimensions mBBox.setLeft(cmd->parm[ 1 ]); mBBox.setTop(cmd->parm[ 0 ]); } if (rdFunc == 0x020C) { // SETWINDOWEXT: dimensions mBBox.setWidth(cmd->parm[ 1 ]); mBBox.setHeight(cmd->parm[ 0 ]); } if (i < rdSize) { debugVectorImage() << "WMF : file truncated !"; return false; } } //----- Test records validities mValid = (rdFunc == 0) && (mBBox.width() != 0) && (mBBox.height() != 0); if (!mValid) { debugVectorImage() << "WMF : incorrect file format !"; } } else { debugVectorImage() << "WMF Header : incorrect header !"; } buffer.close(); return mValid; } //----------------------------------------------------------------------------- bool QWinMetaFile::paint(QPaintDevice* aTarget, bool absolute) { int idx, i; WmfCmd* cmd; if (!mValid) return false; assert(aTarget != NULL); if (mPainter.isActive()) return false; if (mObjHandleTab) delete[] mObjHandleTab; mObjHandleTab = new WinObjHandle* [ MAX_OBJHANDLE ]; for (i = MAX_OBJHANDLE - 1; i >= 0; i--) mObjHandleTab[ i ] = NULL; mPainter.resetMatrix(); mWinding = false; mAbsoluteCoord = absolute; mPainter.begin(aTarget); if (QWMF_DEBUG) { debugVectorImage() << "Bounding box :" << mBBox.left() << " " << mBBox.top() << " " << mBBox.right() << " " << mBBox.bottom() << endl; } if (mAbsoluteCoord) { mPainter.setWindow(mBBox.top(), mBBox.left(), mBBox.width(), mBBox.height()); } mInternalWorldMatrix.reset(); for (cmd = mFirstCmd; cmd; cmd = cmd->next) { idx = cmd->funcIndex; (this->*metaFuncTab[ idx ].method)(cmd->numParm, cmd->parm); if (QWMF_DEBUG) { QString str = "", param; if (metaFuncTab[ idx ].name == NULL) { str += "UNKNOWN "; } if (metaFuncTab[ idx ].method == &QWinMetaFile::noop) { str += "UNIMPLEMENTED "; } str += metaFuncTab[ idx ].name + " : "; for (i = 0 ; i < cmd->numParm ; i++) { param.setNum(cmd->parm[ i ]); str += param + ' '; } debugVectorImage() << str; } } /* // TODO: cleanup this code when QPicture::setBoundingBox() is possible in KOClipart (QT31) // because actually QPicture::boundingBox() != mBBox() mWindowsCoord += 1; if ( mWindowsCoord == 2 ) { debugVectorImage() <<"DRAW ANGLES"; mPainter.setPen( Qt::white ); mPainter.drawPoint( mBBox.left(), mBBox.top() ); mPainter.drawPoint( mBBox.right(), mBBox.bottom() ); } */ mPainter.end(); return true; } //----------------s------------------------------------------------------------- // Metafile painter methods //----------------------------------------------------------------------------- void QWinMetaFile::setWindowOrg(long, short* parm) { if (mAbsoluteCoord) { QRect r = mPainter.window(); mPainter.setWindow(parm[ 1 ], parm[ 0 ], r.width(), r.height()); } else { double dx = mInternalWorldMatrix.dx(); double dy = mInternalWorldMatrix.dy(); mInternalWorldMatrix.translate(-dx, -dy); mInternalWorldMatrix.translate(-parm[ 1 ], -parm[ 0 ]); mPainter.translate(-dx, -dy); mPainter.translate(-parm[ 1 ], -parm[ 0 ]); } } //----------------------------------------------------------------------------- void QWinMetaFile::setWindowExt(long, short* parm) { // negative value allowed for width and height : QABS() forbidden if (mAbsoluteCoord) { QRect r = mPainter.window(); mPainter.setWindow(r.left(), r.top(), parm[ 1 ], parm[ 0 ]); } else { if ((parm[ 0 ] != 0) && (parm[ 1 ] != 0)) { QRect r = mPainter.window(); double dx = mInternalWorldMatrix.dx(); double dy = mInternalWorldMatrix.dy(); double sx = mInternalWorldMatrix.m11(); double sy = mInternalWorldMatrix.m22(); mInternalWorldMatrix.translate(-dx, -dy); mInternalWorldMatrix.scale(1 / sx, 1 / sy); mPainter.translate(-dx, -dy); mPainter.scale(1 / sx, 1 / sy); sx = (double)r.width() / (double)parm[ 1 ]; sy = (double)r.height() / (double)parm[ 0 ]; mInternalWorldMatrix.scale(sx, sy); mInternalWorldMatrix.translate(dx, dy); mPainter.scale(sx, sy); mPainter.translate(dx, dy); } } } //----------------------------------------------------------------------------- // Drawing //----------------------------------------------------------------------------- void QWinMetaFile::lineTo(long, short* parm) { mPainter.drawLine(mLastPos, QPoint(parm[1], parm[0])); } //----------------------------------------------------------------------------- void QWinMetaFile::moveTo(long, short* parm) { mLastPos = QPoint(parm[ 1 ], parm[ 0 ]); } //----------------------------------------------------------------------------- void QWinMetaFile::ellipse(long, short* parm) { mPainter.drawEllipse(parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ]); } //----------------------------------------------------------------------------- void QWinMetaFile::polygon(long, short* parm) { QPolygon* pa; // causing a memleck ??? pa = pointArray(parm[ 0 ], &parm[ 1 ]); if (mWinding) mPainter.drawPolygon(*pa, Qt::WindingFill); else mPainter.drawPolygon(*pa, Qt::OddEvenFill); delete pa; } //----------------------------------------------------------------------------- void QWinMetaFile::polyPolygon(long, short* parm) { QRegion region; int i, j, startPolygon; mPainter.save(); // define clipping region QRect win = bbox(); startPolygon = 1 + parm[ 0 ]; for (i = 0 ; i < parm[ 0 ] ; i++) { QPolygon pa1(parm[ 1+i ]); for (j = 0 ; j < parm[ 1+i ] ; j++) { pa1.setPoint(j, parm[ startPolygon ], parm[ startPolygon+1 ]); startPolygon += 2; } QRegion r(pa1); region = region.eor(r); } mPainter.setClipRegion(region); // fill polygons mPainter.fillRect(win.left(), win.top(), win.width(), win.height(), mPainter.brush()); // draw polygon's border if necessary if (mPainter.pen().style() != Qt::NoPen) { mPainter.setClipping(false); mPainter.setBrush(Qt::NoBrush); QPolygon* pa; int idxPolygon = 1 + parm[ 0 ]; for (i = 0 ; i < parm[ 0 ] ; i++) { pa = pointArray(parm[ 1+i ], &parm[ idxPolygon ]); mPainter.drawPolygon(*pa); idxPolygon += parm[ 1+i ] * 2; } } mPainter.restore(); } //----------------------------------------------------------------------------- void QWinMetaFile::polyline(long, short* parm) { QPolygon* pa; pa = pointArray(parm[ 0 ], &parm[ 1 ]); mPainter.drawPolyline(*pa); } //----------------------------------------------------------------------------- void QWinMetaFile::rectangle(long, short* parm) { mPainter.drawRect(parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ]); } //----------------------------------------------------------------------------- void QWinMetaFile::roundRect(long, short* parm) { int xRnd = 0, yRnd = 0; // convert (xRound, yRound) in percentage if ((parm[ 3 ] - parm[ 5 ]) != 0) xRnd = (parm[ 1 ] * 100) / (parm[ 3 ] - parm[ 5 ]) ; if ((parm[ 2 ] - parm[ 4 ]) != 0) yRnd = (parm[ 0 ] * 100) / (parm[ 2 ] - parm[ 4 ]) ; mPainter.drawRoundRect(parm[ 5 ], parm[ 4 ], parm[ 3 ] - parm[ 5 ], parm[ 2 ] - parm[ 4 ], xRnd, yRnd); } //----------------------------------------------------------------------------- void QWinMetaFile::arc(long, short* parm) { int xCenter, yCenter, angleStart, aLength; xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); xyToAngle(parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength); mPainter.drawArc(parm[ 7 ], parm[ 6 ], parm[ 5 ] - parm[ 7 ], parm[ 4 ] - parm[ 6 ], angleStart, aLength); } //----------------------------------------------------------------------------- void QWinMetaFile::chord(long, short* parm) { int xCenter, yCenter, angleStart, aLength; xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); xyToAngle(parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength); mPainter.drawChord(parm[ 7 ], parm[ 6 ], parm[ 5 ] - parm[ 7 ], parm[ 4 ] - parm[ 6 ], angleStart, aLength); } //----------------------------------------------------------------------------- void QWinMetaFile::pie(long, short* parm) { int xCenter, yCenter, angleStart, aLength; xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); xyToAngle(parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength); mPainter.drawPie(parm[ 7 ], parm[ 6 ], parm[ 5 ] - parm[ 7 ], parm[ 4 ] - parm[ 6 ], angleStart, aLength); } //----------------------------------------------------------------------------- void QWinMetaFile::setPolyFillMode(long, short* parm) { mWinding = parm[ 0 ]; } //----------------------------------------------------------------------------- void QWinMetaFile::setBkColor(long, short* parm) { mPainter.setBackground(QBrush(color(parm))); } //----------------------------------------------------------------------------- void QWinMetaFile::setBkMode(long, short* parm) { if (parm[ 0 ] == 1) mPainter.setBackgroundMode(Qt::TransparentMode); else mPainter.setBackgroundMode(Qt::OpaqueMode); } //----------------------------------------------------------------------------- void QWinMetaFile::setPixel(long, short* parm) { QPen pen = mPainter.pen(); mPainter.setPen(color(parm)); mPainter.drawPoint(parm[ 3 ], parm[ 2 ]); mPainter.setPen(pen); } //----------------------------------------------------------------------------- void QWinMetaFile::setRop(long, short* parm) { mPainter.setCompositionMode(winToQtComposition(parm[ 0 ])); } //----------------------------------------------------------------------------- void QWinMetaFile::saveDC(long, short*) { mPainter.save(); } //----------------------------------------------------------------------------- void QWinMetaFile::restoreDC(long, short *parm) { for (int i = 0; i > parm[ 0 ] ; i--) mPainter.restore(); } //----------------------------------------------------------------------------- void QWinMetaFile::intersectClipRect(long, short* parm) { /* TODO: better implementation : need QT 3.0.2 QRegion region = mPainter.clipRegion(); if ( region.isEmpty() ) region = bbox(); */ QRegion region(bbox()); QRegion newRegion(parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ]); - region = region.intersect(newRegion); + region = region.intersected(newRegion); mPainter.setClipRegion(region); } //----------------------------------------------------------------------------- void QWinMetaFile::excludeClipRect(long, short* parm) { /* TODO: better implementation : need QT 3.0.2 QRegion region = mPainter.clipRegion(); if ( region.isEmpty() ) region = bbox(); */ QRegion region(bbox()); QRegion newRegion(parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ]); region = region.subtract(newRegion); mPainter.setClipRegion(region); } //----------------------------------------------------------------------------- // Text //----------------------------------------------------------------------------- void QWinMetaFile::setTextColor(long, short* parm) { mTextColor = color(parm); } //----------------------------------------------------------------------------- void QWinMetaFile::setTextAlign(long, short* parm) { mTextAlign = parm[ 0 ]; } //----------------------------------------------------------------------------- void QWinMetaFile::textOut(long num, short* parm) { short *copyParm = new short[ num + 1 ]; // re-order parameters int idxOffset = (parm[ 0 ] / 2) + 1 + (parm[ 0 ] & 1); copyParm[ 0 ] = parm[ idxOffset ]; copyParm[ 1 ] = parm[ idxOffset + 1 ]; copyParm[ 2 ] = parm[ 0 ]; copyParm[ 3 ] = 0; memcpy(©Parm[ 4 ], &parm[ 1 ], parm[ 0 ]); extTextOut(num + 1, copyParm); delete [] copyParm; } //----------------------------------------------------------------------------- void QWinMetaFile::extTextOut(long num, short* parm) { char* ptStr; int x, y, width, height; int idxOffset; if (parm[ 3 ] != 0) // ETO_CLIPPED flag add 4 parameters ptStr = (char*) & parm[ 8 ]; else ptStr = (char*) & parm[ 4 ]; QByteArray text(ptStr, parm[ 2 ] + 1); QFontMetrics fm(mPainter.font()); width = fm.width(text) + fm.descent(); // because fm.width(text) isn't rigth with Italic text height = fm.height(); mPainter.save(); if (mTextAlign & 0x01) { // (left, top) position = current logical position x = mLastPos.x(); y = mLastPos.y(); } else { // (left, top) position = parameters x = parm[ 1 ]; y = parm[ 0 ]; } if (mRotation) { mPainter.translate(parm[ 1 ], parm[ 0 ]); mPainter.rotate(mRotation); mPainter.translate(-parm[ 1 ], -parm[ 0 ]); } // alignment if (mTextAlign & 0x06) x -= (width / 2); if (mTextAlign & 0x08) y -= (height - fm.descent()); mPainter.setPen(mTextColor); idxOffset = (parm[ 2 ] / 2) + 4 + (parm[ 2 ] & 1); if ((parm[ 2 ] > 1) && (num >= (idxOffset + parm[ 2 ])) && (parm[ 3 ] == 0)) { // offset for each char int left = x; mPainter.drawText(left, y, width, height, Qt::AlignLeft | Qt::AlignTop, text.mid(0, 1)); for (int i = 1; i < parm[ 2 ] ; i++) { left += parm[ idxOffset + i - 1 ]; mPainter.drawText(left, y, width, height, Qt::AlignLeft | Qt::AlignTop, text.mid(i, 1)); } } else { mPainter.drawText(x, y, width, height, Qt::AlignLeft | Qt::AlignTop, text); } mPainter.restore(); } //----------------------------------------------------------------------------- // Bitmap //----------------------------------------------------------------------------- void QWinMetaFile::dibBitBlt(long num, short* parm) { if (num > 9) { // DIB image QImage bmpSrc; if (dibToBmp(bmpSrc, (char*)&parm[ 8 ], (num - 8) * 2)) { long raster = toDWord(parm); mPainter.setCompositionMode(winToQtComposition(raster)); // wmf file allow negative width or height mPainter.save(); if (parm[ 5 ] < 0) { // width < 0 => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } if (parm[ 4 ] < 0) { // height < 0 => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } mPainter.drawImage(parm[ 7 ], parm[ 6 ], bmpSrc, parm[ 3 ], parm[ 2 ], parm[ 5 ], parm[ 4 ]); mPainter.restore(); } } else { debugVectorImage() << "QWinMetaFile::dibBitBlt without image: not implemented"; } } //----------------------------------------------------------------------------- void QWinMetaFile::dibStretchBlt(long num, short* parm) { QImage bmpSrc; if (dibToBmp(bmpSrc, (char*)&parm[ 10 ], (num - 10) * 2)) { long raster = toDWord(parm); mPainter.setCompositionMode(winToQtComposition(raster)); // wmf file allow negative width or height mPainter.save(); if (parm[ 7 ] < 0) { // width < 0 => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } if (parm[ 6 ] < 0) { // height < 0 => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } bmpSrc = bmpSrc.copy(parm[ 5 ], parm[ 4 ], parm[ 3 ], parm[ 2 ]); // TODO: scale the bitmap ( QImage::scale(parm[ 7 ], parm[ 6 ]) is actually too slow ) mPainter.drawImage(parm[ 9 ], parm[ 8 ], bmpSrc); mPainter.restore(); } } //----------------------------------------------------------------------------- void QWinMetaFile::stretchDib(long num, short* parm) { QImage bmpSrc; if (dibToBmp(bmpSrc, (char*)&parm[ 11 ], (num - 11) * 2)) { long raster = toDWord(parm); mPainter.setCompositionMode(winToQtComposition(raster)); // wmf file allow negative width or height mPainter.save(); if (parm[ 8 ] < 0) { // width < 0 => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } if (parm[ 7 ] < 0) { // height < 0 => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } bmpSrc = bmpSrc.copy(parm[ 6 ], parm[ 5 ], parm[ 4 ], parm[ 3 ]); // TODO: scale the bitmap ( QImage::scale(parm[ 8 ], parm[ 7 ]) is actually too slow ) mPainter.drawImage(parm[ 10 ], parm[ 9 ], bmpSrc); mPainter.restore(); } } //----------------------------------------------------------------------------- void QWinMetaFile::dibCreatePatternBrush(long num, short* parm) { WinObjPatternBrushHandle* handle = new WinObjPatternBrushHandle; addHandle(handle); QImage bmpSrc; if (dibToBmp(bmpSrc, (char*)&parm[ 2 ], (num - 2) * 2)) { handle->image = bmpSrc; handle->brush.setTextureImage(handle->image); } } //----------------------------------------------------------------------------- // Object handle //----------------------------------------------------------------------------- void QWinMetaFile::selectObject(long, short* parm) { int idx = parm[ 0 ]; if (idx >= 0 && idx < MAX_OBJHANDLE && mObjHandleTab[ idx ]) mObjHandleTab[ idx ]->apply(mPainter); } //----------------------------------------------------------------------------- void QWinMetaFile::deleteObject(long, short* parm) { deleteHandle(parm[ 0 ]); } //----------------------------------------------------------------------------- void QWinMetaFile::createEmptyObject(long, short*) { // allocation of an empty object (to keep object counting in sync) WinObjPenHandle* handle = new WinObjPenHandle; addHandle(handle); debugVectorImage() << "QWinMetaFile: unimplemented createObject"; } //----------------------------------------------------------------------------- void QWinMetaFile::createBrushIndirect(long, short* parm) { static Qt::BrushStyle hatchedStyleTab[] = { Qt::HorPattern, Qt::FDiagPattern, Qt::BDiagPattern, Qt::CrossPattern, Qt::DiagCrossPattern }; static Qt::BrushStyle styleTab[] = { Qt::SolidPattern, Qt::NoBrush, Qt::FDiagPattern, /* hatched */ Qt::Dense4Pattern, /* should be custom bitmap pattern */ Qt::HorPattern, /* should be BS_INDEXED (?) */ Qt::VerPattern, /* should be device-independent bitmap */ Qt::Dense6Pattern, /* should be device-independent packed-bitmap */ Qt::Dense2Pattern, /* should be BS_PATTERN8x8 */ Qt::Dense3Pattern /* should be device-independent BS_DIBPATTERN8x8 */ }; Qt::BrushStyle style; short arg; WinObjBrushHandle* handle = new WinObjBrushHandle; addHandle(handle); arg = parm[ 0 ]; if (arg == 2) { arg = parm[ 3 ]; if (arg >= 0 && arg < 5) style = hatchedStyleTab[ arg ]; else { debugVectorImage() << "QWinMetaFile::createBrushIndirect: invalid hatched brush" << arg; style = Qt::SolidPattern; } } else if (arg >= 0 && arg < 9) style = styleTab[ arg ]; else { debugVectorImage() << "QWinMetaFile::createBrushIndirect: invalid brush" << arg; style = Qt::SolidPattern; } handle->brush.setStyle(style); handle->brush.setColor(color(parm + 1)); } //----------------------------------------------------------------------------- void QWinMetaFile::createPenIndirect(long, short* parm) { static Qt::PenStyle styleTab[] = { Qt::SolidLine, Qt::DashLine, Qt::DotLine, Qt::DashDotLine, Qt::DashDotDotLine, Qt::NoPen, Qt::SolidLine }; Qt::PenStyle style; WinObjPenHandle* handle = new WinObjPenHandle; addHandle(handle); if (parm[ 0 ] >= 0 && parm[ 0 ] < 6) style = styleTab[ parm[ 0 ] ]; else { debugVectorImage() << "QWinMetaFile::createPenIndirect: invalid pen" << parm[ 0 ]; style = Qt::SolidLine; } handle->pen.setStyle(style); handle->pen.setColor(color(parm + 3)); handle->pen.setCapStyle(Qt::RoundCap); //int width = 0; // TODO : width of pen proportional to device context width // DOESN'T WORK /* QRect devRec; devRec = mPainter.transformed( mBBox ); width = ( parm[ 0 ] * devRec.width() ) / mBBox.width() ; debugVectorImage() <<"CreatePenIndirect:"; debugVectorImage() <<" log coord. :" << mBBox.width() <<"" << mBBox.height(); debugVectorImage() <<" log. pen :" << parm[ 1 ] <<"" << parm[ 2 ]; debugVectorImage() <<" dev. pen :" << width; handle->pen.setWidth( width ); */ } //----------------------------------------------------------------------------- void QWinMetaFile::createFontIndirect(long , short* parm) { WinObjFontHandle* handle = new WinObjFontHandle; addHandle(handle); QString family((const char*)&parm[ 9 ]); mRotation = -parm[ 2 ] / 10; // text rotation (in 1/10 degree) // TODO: memorisation of rotation in object Font handle->font.setFamily(family); handle->font.setFixedPitch(((parm[ 8 ] & 0x01) == 0)); // TODO: investigation why some test case need -2. (size of font in logical point) handle->font.setPointSize(qAbs(parm[ 0 ]) - 2); handle->font.setWeight((parm[ 4 ] >> 3)); handle->font.setItalic((parm[ 5 ] & 0x01)); handle->font.setUnderline((parm[ 5 ] & 0x100)); } //----------------------------------------------------------------------------- // Misc //----------------------------------------------------------------------------- void QWinMetaFile::noop(long, short*) { } void QWinMetaFile::end(long, short*) { // end of file : // debugVectorImage() <<"END bbox=(" << mBBox.left() <<";" << mBBox.top() <<";" << mBBox.width() <<";" << mBBox.height() <<")"; } //----------------------------------------------------------------------------- unsigned short QWinMetaFile::calcCheckSum(WmfPlaceableHeader* apmfh) { WORD* lpWord; WORD wResult, i; // Start with the first word wResult = *(lpWord = (WORD*)(apmfh)); // XOR in each of the other 9 words for (i = 1; i <= 9; i++) { wResult ^= lpWord[ i ]; } return wResult; } //----------------------------------------------------------------------------- int QWinMetaFile::findFunc(unsigned short aFunc) const { int i; for (i = 0; metaFuncTab[ i ].name; i++) if (metaFuncTab[ i ].func == aFunc) return i; // here : unknown function return i; } //----------------------------------------------------------------------------- QPolygon* QWinMetaFile::pointArray(short num, short* parm) { int i; mPoints.resize(num); for (i = 0; i < num; i++, parm += 2) mPoints.setPoint(i, parm[ 0 ], parm[ 1 ]); return &mPoints; } //----------------------------------------------------------------------------- unsigned int QWinMetaFile::toDWord(short* parm) { unsigned int l; #if !defined( WORDS_BIGENDIAN ) l = *(unsigned int*)(parm); #else char *bytes; char swap[ 4 ]; bytes = (char*)parm; swap[ 0 ] = bytes[ 2 ]; swap[ 1 ] = bytes[ 3 ]; swap[ 2 ] = bytes[ 0 ]; swap[ 3 ] = bytes[ 1 ]; l = *(unsigned int*)(swap); #endif return l; } //----------------------------------------------------------------------------- QColor QWinMetaFile::color(short* parm) { unsigned int colorRef; int red, green, blue; colorRef = toDWord(parm) & 0xffffff; red = colorRef & 255; green = (colorRef >> 8) & 255; blue = (colorRef >> 16) & 255; return QColor(red, green, blue); } //----------------------------------------------------------------------------- void QWinMetaFile::xyToAngle(int xStart, int yStart, int xEnd, int yEnd, int& angleStart, int& angleLength) { float aStart, aLength; aStart = atan2((double)yStart, (double)xStart); aLength = atan2((double)yEnd, (double)xEnd) - aStart; angleStart = (int)(aStart * 2880 / 3.14166); angleLength = (int)(aLength * 2880 / 3.14166); if (angleLength < 0) angleLength = 5760 + angleLength; } //----------------------------------------------------------------------------- void QWinMetaFile::addHandle(WinObjHandle* handle) { int idx; for (idx = 0; idx < MAX_OBJHANDLE ; idx++) if (mObjHandleTab[ idx ] == NULL) break; if (idx < MAX_OBJHANDLE) mObjHandleTab[ idx ] = handle; else debugVectorImage() << "QWinMetaFile error: handle table full !"; } //----------------------------------------------------------------------------- void QWinMetaFile::deleteHandle(int idx) { if (idx >= 0 && idx < MAX_OBJHANDLE && mObjHandleTab[ idx ]) { delete mObjHandleTab[ idx ]; mObjHandleTab[ idx ] = NULL; } } //----------------------------------------------------------------------------- QPainter::CompositionMode QWinMetaFile::winToQtComposition(short parm) const { static const QPainter::CompositionMode opTab[] = { // ### untested (conversion from Qt::RasterOp) QPainter::CompositionMode_Source, // Qt::CopyROP QPainter::CompositionMode_Clear, // Qt::ClearROP QPainter::CompositionMode_SourceOut, // Qt::NandROP QPainter::CompositionMode_SourceOut, // Qt::NotAndROP QPainter::CompositionMode_DestinationOut, // Qt::NotCopyROP QPainter::CompositionMode_DestinationOut, // Qt::AndNotROP QPainter::CompositionMode_DestinationOut, // Qt::NotROP QPainter::CompositionMode_Xor, // Qt::XorROP QPainter::CompositionMode_Source, // Qt::NorROP QPainter::CompositionMode_SourceIn, // Qt::AndROP QPainter::CompositionMode_SourceIn, // Qt::NotXorROP QPainter::CompositionMode_Destination, // Qt::NopROP QPainter::CompositionMode_Destination, // Qt::NotOrROP QPainter::CompositionMode_Source, // Qt::CopyROP QPainter::CompositionMode_Source, // Qt::OrNotROP QPainter::CompositionMode_SourceOver, // Qt::OrROP QPainter::CompositionMode_Source // Qt::SetROP }; if (parm > 0 && parm <= 16) return opTab[ parm ]; else return QPainter::CompositionMode_Source; } //----------------------------------------------------------------------------- QPainter::CompositionMode QWinMetaFile::winToQtComposition(long parm) const { /* TODO: Ternary raster operations 0x00C000CA dest = (source AND pattern) 0x00F00021 dest = pattern 0x00FB0A09 dest = DPSnoo 0x005A0049 dest = pattern XOR dest */ static const struct OpTab { long winRasterOp; QPainter::CompositionMode qtRasterOp; } opTab[] = { // ### untested (conversion from Qt::RasterOp) { 0x00CC0020, QPainter::CompositionMode_Source }, // CopyROP { 0x00EE0086, QPainter::CompositionMode_SourceOver }, // OrROP { 0x008800C6, QPainter::CompositionMode_SourceIn }, // AndROP { 0x00660046, QPainter::CompositionMode_Xor }, // XorROP { 0x00440328, QPainter::CompositionMode_DestinationOut }, // AndNotROP { 0x00330008, QPainter::CompositionMode_DestinationOut }, // NotCopyROP { 0x001100A6, QPainter::CompositionMode_SourceOut }, // NandROP { 0x00C000CA, QPainter::CompositionMode_Source }, // CopyROP { 0x00BB0226, QPainter::CompositionMode_Destination }, // NotOrROP { 0x00F00021, QPainter::CompositionMode_Source }, // CopyROP { 0x00FB0A09, QPainter::CompositionMode_Source }, // CopyROP { 0x005A0049, QPainter::CompositionMode_Source }, // CopyROP { 0x00550009, QPainter::CompositionMode_DestinationOut }, // NotROP { 0x00000042, QPainter::CompositionMode_Clear }, // ClearROP { 0x00FF0062, QPainter::CompositionMode_Source } // SetROP }; int i; for (i = 0 ; i < 15 ; i++) if (opTab[ i ].winRasterOp == parm) break; if (i < 15) return opTab[ i ].qtRasterOp; else return QPainter::CompositionMode_Source; } //----------------------------------------------------------------------------- bool QWinMetaFile::dibToBmp(QImage& bmp, const char* dib, long size) { typedef struct _BMPFILEHEADER { WORD bmType; DWORD bmSize; WORD bmReserved1; WORD bmReserved2; DWORD bmOffBits; } BMPFILEHEADER; int sizeBmp = size + 14; QByteArray pattern; // BMP header and DIB data pattern.fill(0, sizeBmp); //resize and fill pattern.insert(14, QByteArray::fromRawData(dib, size)); // add BMP header BMPFILEHEADER* bmpHeader; bmpHeader = (BMPFILEHEADER*)((const char*)pattern); bmpHeader->bmType = 0x4D42; bmpHeader->bmSize = sizeBmp; if (!bmp.loadFromData((const uchar*)bmpHeader, pattern.size(), "BMP")) { debugVectorImage() << "QWinMetaFile::dibToBmp: invalid bitmap"; return false; } else { // if ( bmp.save("/home/software/kde-cvs/qt/examples/wmf/test.bmp", "BMP") ) // if ( bmp.load( "/home/software/kde-cvs/qt/examples/wmf/test.bmp", "BMP" ) ) // fprintf(stderr, "Bitmap ok \n"); return true; } } diff --git a/plugins/chartshape/Legend.cpp b/plugins/chartshape/Legend.cpp index ed7e7611a6a..465f45e031c 100644 --- a/plugins/chartshape/Legend.cpp +++ b/plugins/chartshape/Legend.cpp @@ -1,577 +1,577 @@ /* This file is part of the KDE project Copyright 2007 Johannes Simon Copyright 2010 Inge Wallin 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. */ // Own #include "Legend.h" // Qt #include #include #include #include #include #include #include // Calligra #include #include #include #include #include #include #include #include #include #include #include // KChart #include #include #include #include #include #include #include "KChartConvertions.h" // KoChart #include "PlotArea.h" #include "ScreenConversions.h" #include "ChartLayout.h" #include "OdfLoadingHelper.h" #include "OdfHelper.h" using namespace KoChart; class Legend::Private { public: Private(); ~Private(); ChartShape *shape; // Properties of the Legend QString title; LegendExpansion expansion; Position position; QFont font; QFont titleFont; QColor fontColor; Qt::Alignment alignment; KoShapeStroke *lineBorder; // The connection to KChart KChart::Legend *kdLegend; QImage image; mutable bool pixmapRepaintRequested; QSizeF lastSize; QPointF lastZoomLevel; }; Legend::Private::Private() { lineBorder = new KoShapeStroke(0.5, Qt::black); expansion = HighLegendExpansion; alignment = Qt::AlignCenter; pixmapRepaintRequested = true; position = EndPosition; } Legend::Private::~Private() { delete lineBorder; } Legend::Legend(ChartShape *parent) : QObject(parent) , d(new Private()) { Q_ASSERT(parent); setShapeId("ChartShapeLegend"); d->shape = parent; d->kdLegend = new KChart::Legend(); d->kdLegend->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); // we use the shape to display frame and background KChart::FrameAttributes frameAttr = d->kdLegend->frameAttributes(); frameAttr.setVisible(false); d->kdLegend->setFrameAttributes(frameAttr); setTitleFontSize(10); setTitle(QString()); setFontSize(8); update(); parent->addShape(this); setAllowedInteraction(KoShape::ResizeAllowed, false); setAllowedInteraction(KoShape::RotationAllowed, false); connect (d->kdLegend, SIGNAL(propertiesChanged()), this, SLOT(slotKdLegendChanged())); connect (parent, SIGNAL(chartTypeChanged(ChartType, ChartType)), this, SLOT(slotChartTypeChanged(ChartType))); } Legend::~Legend() { delete d->kdLegend; delete d; } QString Legend::title() const { return d->title; } void Legend::setTitle(const QString &title) { d->title = title; d->kdLegend->setTitleText(title); d->pixmapRepaintRequested = true; emit updateConfigWidget(); } QFont Legend::font() const { return d->font; } void Legend::setFont(const QFont &font) { d->font = font; // KChart KChart::TextAttributes attributes = d->kdLegend->textAttributes(); attributes.setFont(font); d->kdLegend->setTextAttributes(attributes); d->pixmapRepaintRequested = true; emit updateConfigWidget(); } qreal Legend::fontSize() const { return d->font.pointSizeF(); } void Legend::setFontSize(qreal size) { d->font.setPointSizeF(size); // KChart KChart::TextAttributes attributes = d->kdLegend->textAttributes(); KChart::Measure m = attributes.fontSize(); m.setValue(size); attributes.setFontSize(m); d->kdLegend->setTextAttributes(attributes); d->pixmapRepaintRequested = true; emit updateConfigWidget(); } void Legend::setFontColor(const QColor &color) { KChart::TextAttributes attributes = d->kdLegend->textAttributes(); QPen pen = attributes.pen(); pen.setColor(color); attributes.setPen(pen); d->kdLegend->setTextAttributes(attributes); d->pixmapRepaintRequested = true; } QColor Legend::fontColor() const { KChart::TextAttributes attributes = d->kdLegend->textAttributes(); QPen pen = attributes.pen(); return pen.color(); } QFont Legend::titleFont() const { return d->titleFont; } void Legend::setTitleFont(const QFont &font) { d->titleFont = font; // KChart KChart::TextAttributes attributes = d->kdLegend->titleTextAttributes(); attributes.setFont(font); d->kdLegend->setTitleTextAttributes(attributes); d->pixmapRepaintRequested = true; } qreal Legend::titleFontSize() const { return d->titleFont.pointSizeF(); } void Legend::setTitleFontSize(qreal size) { d->titleFont.setPointSizeF(size); // KChart KChart::TextAttributes attributes = d->kdLegend->titleTextAttributes(); attributes.setFontSize(KChart::Measure(size, KChartEnums::MeasureCalculationModeAbsolute)); d->kdLegend->setTitleTextAttributes(attributes); d->pixmapRepaintRequested = true; } LegendExpansion Legend::expansion() const { return d->expansion; } void Legend::setExpansion(LegendExpansion expansion) { d->expansion = expansion; d->kdLegend->setOrientation(LegendExpansionToQtOrientation(expansion)); d->pixmapRepaintRequested = true; emit updateConfigWidget(); } Qt::Alignment Legend::alignment() const { return d->alignment; } void Legend::setAlignment(Qt::Alignment alignment) { d->alignment = alignment; } Position Legend::legendPosition() const { return d->position; } void Legend::setLegendPosition(Position position) { d->position = position; d->pixmapRepaintRequested = true; } void Legend::setSize(const QSizeF &newSize) { QSize newSizePx = ScreenConversions::scaleFromPtToPx(newSize); d->kdLegend->resize(newSizePx); d->kdLegend->resizeLayout(newSizePx); KoShape::setSize(newSize); } void Legend::paintPixmap(QPainter &painter, const KoViewConverter &converter) { // Adjust the size of the painting area to the current zoom level const QSize paintRectSize = converter.documentToView(d->lastSize).toSize(); d->image = QImage(paintRectSize, QImage::Format_ARGB32); QPainter pixmapPainter(&d->image); pixmapPainter.setRenderHints(painter.renderHints()); pixmapPainter.setRenderHint(QPainter::Antialiasing, false); // Scale the painter's coordinate system to fit the current zoom level. applyConversion(pixmapPainter, converter); d->kdLegend->paint(&pixmapPainter); } void Legend::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) { //painter.save(); // First of all, scale the painter's coordinate system to fit the current zoom level applyConversion(painter, converter); // Calculate the clipping rect QRectF paintRect = QRectF(QPointF(0, 0), size()); - //clipRect.intersect(paintRect); + //clipRect.intersected(paintRect); painter.setClipRect(paintRect, Qt::IntersectClip); // Get the current zoom level QPointF zoomLevel; converter.zoom(&zoomLevel.rx(), &zoomLevel.ry()); // Only repaint the pixmap if it is scheduled, the zoom level changed or the shape was resized /*if ( d->pixmapRepaintRequested || d->lastZoomLevel != zoomLevel || d->lastSize != size()) { // TODO: What if two zoom levels are constantly being requested? // At the moment, this *is* the case, due to the fact // that the shape is also rendered in the page overview // in Stage // Every time the window is hidden and shown again, a repaint is // requested --> laggy performance, especially when quickly // switching through windows d->pixmapRepaintRequested = false; d->lastZoomLevel = zoomLevel; d->lastSize = size(); paintPixmap(painter, converter); }*/ // Paint the background if (background()) { QPainterPath p; p.addRect(paintRect); background()->paint(painter, converter, paintContext, p); } // KChart thinks in pixels, Calligra in pt // KChart also for non-QWidget painting devices cares for the logicalDpi // Other than PlotArea we do not control the output size via the paint method, // so here have to resize the legend temporarily. // Printing should only result in 1 paint call, so this should not happen too often. // TODO: something in KChart seems broken in general on printer output, also seen in kchart examples // so legend in print is still broken :/ const QSize sizePx = d->kdLegend->size(); const QSize newSizePx = ScreenConversions::scaleFromPtToPx(size(), painter); const bool isPainterDifferentDpi = (sizePx != newSizePx); if (isPainterDifferentDpi) { // temporarily set a size matching the painterdevice d->kdLegend->resize(newSizePx); d->kdLegend->resizeLayout(newSizePx); } ScreenConversions::scaleFromPtToPx(painter); d->kdLegend->paint(&painter); if (isPainterDifferentDpi) { // restore screen-dpi size d->kdLegend->resize(sizePx); d->kdLegend->resizeLayout(sizePx); } //painter.restore(); // Paint the cached pixmap //painter.drawImage(0, 0, d->image); } // ---------------------------------------------------------------- // loading and saving bool Legend::loadOdf(const KoXmlElement &legendElement, KoShapeLoadingContext &context) { KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); styleStack.clear(); // FIXME: If the style isn't present we shouldn't care about it at all // and move everything related to the legend style in this if clause if (legendElement.hasAttributeNS(KoXmlNS::chart, "style-name")) { context.odfLoadingContext().fillStyleStack(legendElement, KoXmlNS::chart, "style-name", "chart"); styleStack.setTypeProperties("graphic"); } if (!legendElement.isNull()) { int attributesToLoad = OdfAllAttributes; QString lp = legendElement.attributeNS(KoXmlNS::chart, "legend-position", QString()); // Note: load position even if it might not be used loadOdfAttributes(legendElement, context, attributesToLoad); QString lalign = legendElement.attributeNS(KoXmlNS::chart, "legend-align", QString()); if (legendElement.hasAttributeNS(KoXmlNS::style, "legend-expansion")) { QString lexpansion = legendElement.attributeNS(KoXmlNS::style, "legend-expansion", QString()); if (lexpansion == "wide") setExpansion(WideLegendExpansion); else if (lexpansion == "high") setExpansion(HighLegendExpansion); else setExpansion(BalancedLegendExpansion); } if (lalign == "start") { setAlignment(Qt::AlignLeft); } else if (lalign == "end") { setAlignment(Qt::AlignRight); } else { setAlignment(Qt::AlignCenter); // default } if (lp == "start") { setLegendPosition(StartPosition); } else if (lp == "top") { setLegendPosition(TopPosition); } else if (lp == "bottom") { setLegendPosition(BottomPosition); } else if (lp == "end") { setLegendPosition(EndPosition); } else if (lp == "top-start") { setLegendPosition(TopStartPosition); } else if (lp == "bottom-start") { setLegendPosition(BottomStartPosition); } else if (lp == "top-end") { setLegendPosition(TopEndPosition); } else if (lp == "bottom-end") { setLegendPosition(BottomEndPosition); } else { setLegendPosition(FloatingPosition); } if (legendElement.hasAttributeNS(KoXmlNS::office, "title")) { setTitle(legendElement.attributeNS(KoXmlNS::office, "title", QString())); } styleStack.setTypeProperties("text"); if (styleStack.hasProperty(KoXmlNS::fo, "font-family")) { QString fontFamily = styleStack.property(KoXmlNS::fo, "font-family"); QFont font = d->font; font.setFamily(fontFamily); setFont(font); } if (styleStack.hasProperty(KoXmlNS::fo, "font-size")) { qreal fontSize = KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "font-size")); setFontSize(fontSize); } if (styleStack.hasProperty(KoXmlNS::fo, "font-color")) { QColor color = styleStack.property(KoXmlNS::fo, "font-color"); if (color.isValid()) { setFontColor(color); } } } else { // No legend element, use default legend. setLegendPosition(EndPosition); setAlignment(Qt::AlignCenter); } d->pixmapRepaintRequested = true; return true; } void Legend::saveOdf(KoShapeSavingContext &context) const { KoXmlWriter &bodyWriter = context.xmlWriter(); bodyWriter.startElement("chart:legend"); saveOdfAttributes(context, OdfPosition); // Legend specific attributes QString lp = PositionToString(d->position); if (!lp.isEmpty()) { bodyWriter.addAttribute("chart:legend-position", lp); } QString lalign; switch (d->alignment) { case Qt::AlignLeft: lalign = "start"; break; case Qt::AlignRight: lalign = "end"; break; case Qt::AlignCenter: lalign = "center"; break; default: break; } if (!lalign.isEmpty()) { bodyWriter.addAttribute("chart:legend-align", lalign); } // Legend style FIXME: Check if more styling then just the font goes here. KoGenStyle style(KoGenStyle::ChartAutoStyle, "chart", 0); OdfHelper::saveOdfFont(style, d->font, d->fontColor); bodyWriter.addAttribute("chart:style-name", saveStyle(style, context)); QString lexpansion; switch (expansion()) { case WideLegendExpansion: lexpansion = "wide"; break; case HighLegendExpansion: lexpansion = "high"; break; case BalancedLegendExpansion: lexpansion = "balanced"; break; }; bodyWriter.addAttribute("style:legend-expansion", lexpansion); if (!title().isEmpty()) bodyWriter.addAttribute("office:title", title()); bodyWriter.endElement(); // chart:legend } KChart::Legend *Legend::kdLegend() const { // There has to be a valid KChart instance of this legend Q_ASSERT(d->kdLegend); return d->kdLegend; } void Legend::rebuild() { d->kdLegend->forceRebuild(); update(); } void Legend::update() const { d->pixmapRepaintRequested = true; KoShape::update(); } void Legend::slotKdLegendChanged() { // FIXME: Update legend properly by implementing all *DataChanged() slots // in KChartModel. Right now, only yDataChanged() is implemented. //d->kdLegend->forceRebuild(); QSize size = d->kdLegend->sizeHint(); setSize(ScreenConversions::scaleFromPxToPt(size)); update(); } void Legend::slotChartTypeChanged(ChartType chartType) { // TODO: Once we support markers, this switch will have to be // more clever. switch (chartType) { case LineChartType: case ScatterChartType: d->kdLegend->setLegendStyle(KChart::Legend::MarkersAndLines); break; default: d->kdLegend->setLegendStyle(KChart::Legend::MarkersOnly); break; } } diff --git a/words/part/KWViewModeNormal.cpp b/words/part/KWViewModeNormal.cpp index 0f0a3dfec46..1fb21f02302 100644 --- a/words/part/KWViewModeNormal.cpp +++ b/words/part/KWViewModeNormal.cpp @@ -1,299 +1,299 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2009 Thomas Zander * * 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. */ #include "KWViewModeNormal.h" #include "KWPageManager.h" #include "KWPage.h" #include "KWView.h" #include #include #define GAP 20 KWViewModeNormal::KWViewModeNormal() : m_pageSpreadMode(false) { } QVector KWViewModeNormal::mapExposedRects(const QRectF &viewRect, KoViewConverter *viewConverter) const { QVector answer; if (!viewConverter) return answer; #if 1 if (m_pageTops.isEmpty()) return answer; KWPage page = m_pageManager->begin(); const int pageOffset = page.pageNumber(); // Perform a binary search for page-index using our m_pageTops cache. int begin = 0; int end = m_pageTops.count() - 1; int index = 0; const qreal value = viewConverter->viewToDocument(viewRect.topLeft()).y(); if (m_pageTops.value(end) <= value) { // check extremes. Only end is needed since begin is zero. begin = end; index = end; } while (end - begin > 1) { index = begin + (end - begin) / 2; qreal diff = m_pageTops.value(index) - value; if (diff < 0) begin = index; else if (diff > 0) end = index; else break; } // index is now the number of the first possible page that can // contain the viewRect. The next step is to find the // corresponding Page. Since we have no way to get to the Nth // page directly we have to enumerate them from the beginning. // // We use 1 since we might hit a pagespread in the binary search, // so start one page early. while (index > 1) { page = page.next(); --index; } // From here we loop through some more pages after the found one // and see if they intersect with the page in question. When we // have two pages in row that don't intersect we break the loop. qreal offsetX = 0.0; int emptyPages = 0; for(; page.isValid(); page = page.next()) { Q_ASSERT_X(page.pageNumber()-pageOffset < m_pageTops.count(), __FUNCTION__, QString("Pagemanager has more pages than viewmode (%1>%2 with pageOffset=%3 and pageNumber=%4 and pageCount=%5). Make sure you add pages via the document!") .arg(page.pageNumber()-pageOffset).arg(m_pageTops.count()) .arg(pageOffset).arg(page.pageNumber()).arg(m_pageManager->pageCount()).toLocal8Bit()); // Some invariants const QRectF pageRect = page.rect(); const qreal offsetY = m_pageTops[page.pageNumber() - pageOffset] - pageRect.top(); bool pageIntersects = false; // 1. First handle the page itself. const QRectF zoomedPage = viewConverter->documentToView(pageRect); ViewMap vm; vm.page = page; //kDebug(32003) <<"page" << page.pageNumber(); vm.distance = viewConverter->documentToView(QPointF(offsetX, offsetY)); const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(), zoomedPage.width(), zoomedPage.height()); - QRectF intersection = targetPage.intersect(viewRect); + QRectF intersection = targetPage.intersected(viewRect); if (! intersection.isEmpty()) { intersection.moveTopLeft(intersection.topLeft() - vm.distance); vm.clipRect = intersection.toRect(); answer.append(vm); pageIntersects = true; } // 2. Then handle the annotation area if annotations are active. // // The reason we don't do them together with the page // itself is because the pages have a gap between them, but // the annotation area should be unbroken. // // NOTE: 'annotation' below means the annotation area. // // FIXME: We should only do this if the annotation area is // actually shown. How can we inside the KWViewMode // know if annotations are active? if (1 /* annotations are shown */) { const QRectF annotationRect = pageRect.adjusted(page.width(), 0, KWCanvasBase::AnnotationAreaWidth, GAP); const QRectF zoomedAnnotation = viewConverter->documentToView(annotationRect); ViewMap vm2; vm2.page = page; vm2.distance = viewConverter->documentToView(QPointF(offsetX, offsetY)); const QRectF targetAnnotation(zoomedAnnotation.x() + vm2.distance.x(), zoomedAnnotation.y() + vm2.distance.y(), zoomedAnnotation.width(), zoomedAnnotation.height()); - intersection = targetAnnotation.intersect(viewRect); + intersection = targetAnnotation.intersected(viewRect); if (! intersection.isEmpty()) { intersection.moveTopLeft(intersection.topLeft() - vm2.distance); vm2.clipRect = intersection.toRect(); answer.append(vm2); pageIntersects = true; } } // Record whether this page had an intersection with the view rect. if (pageIntersects) { emptyPages = 0; } else { ++emptyPages; } if (emptyPages > 2) // Since we show at max 2 pages side by side this is an easy rule break; if (m_pageSpreadMode) { if (page.pageSide() == KWPage::Left) offsetX = page.width() + GAP; else offsetX = 0.0; } } #else KWPage page = m_pageManager->begin(); Q_ASSERT(page.isValid()); qreal offsetX = 0.0; const int pageOffset = page.pageNumber(); for(; page.isValid(); page = page.next()) { const QRectF pageRect = page.rect(); const QRectF zoomedPage = viewConverter->documentToView(pageRect); ViewMap vm; vm.page = page; const qreal offsetY = m_pageTops[page.pageNumber() - pageOffset] - pageRect.top(); vm.distance = viewConverter->documentToView(QPointF(offsetX, offsetY)); #if 0 const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(), zoomedPage.width() , zoomedPage.height()); - QRectF intersection = targetPage.intersect(viewRect); + QRectF intersection = targetPage.intersected(viewRect); if (! intersection.isEmpty()) { intersection.moveTopLeft(intersection.topLeft() - vm.distance); vm.clipRect = intersection.toRect(); answer.append(vm); } #else const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(), zoomedPage.width() , zoomedPage.height()); vm.clipRect = targetPage.toRect(); answer.append(vm); #endif } #endif return answer; } void KWViewModeNormal::updatePageCache() { if (!m_pageManager) { warnWords << "Error detected while running KWViewModeNormal::updatePageCache: PageManager not set"; return; } m_pageSpreadMode = false; foreach (const KWPage &page, m_pageManager->pages()) { Q_UNUSED(page); } m_pageTops.clear(); qreal width = 0.0, bottom = 0.0; if (m_pageSpreadMode) { // two pages next to each other per row qreal top = 0.0, last = 0.0, halfWidth = 0.0; foreach (const KWPage &page, m_pageManager->pages()) { switch (page.pageSide()) { case KWPage::Left: m_pageTops.append(top); last = page.height(); halfWidth = page.width() + GAP; width = qMax(width, halfWidth); bottom = top + last; break; case KWPage::Right: m_pageTops.append(top); top += qMax(page.height(), last); last = 0.0; width = qMax(width, halfWidth + page.width()); halfWidth = 0.0; bottom = top; top += GAP; break; default: Q_ASSERT(false); break; } } } else { // each page on a row qreal top = 0.0; foreach (const KWPage &page, m_pageManager->pages()) { m_pageTops.append(top); top += page.height() + GAP; width = qMax(width, page.width()); } bottom = top; } if (bottom > GAP) bottom -= GAP; // remove one too many added m_contents = QSizeF(width, bottom); } QPointF KWViewModeNormal::documentToView(const QPointF & point, KoViewConverter *viewConverter) const { Q_ASSERT(viewConverter); KWPage page = m_pageManager->page(point); if (! page.isValid()) page = m_pageManager->last(); if (! page.isValid()) return QPointF(); int pageIndex = page.pageNumber() - m_pageManager->begin().pageNumber(); qreal x = 0; if (m_pageSpreadMode && page.pageSide() == KWPage::Right) { KWPage prevPage = m_pageManager->page(page.pageNumber() - 1); if (prevPage.isValid()) x = prevPage.width(); } QPointF offsetInPage(point.x(), + point.y() - page.offsetInDocument()); Q_ASSERT(pageIndex >= 0); Q_ASSERT(pageIndex < m_pageTops.count()); QPointF translated(x, m_pageTops[pageIndex]); return viewConverter->documentToView(translated + offsetInPage); } QPointF KWViewModeNormal::viewToDocument(const QPointF & point, KoViewConverter *viewConverter) const { Q_ASSERT(viewConverter); QPointF clippedPoint(qMax(qreal(0.0), point.x()), qMax(qreal(0.0), point.y())); QPointF translated = viewConverter->viewToDocument(clippedPoint); int pageNumber = 0; foreach (qreal top, m_pageTops) { if (translated.y() < top) break; pageNumber++; } translated = viewConverter->viewToDocument(point); KWPage page = m_pageManager->page(pageNumber - 1 + m_pageManager->begin().pageNumber()); qreal xOffset = translated.x(); if (page.isValid() && m_pageSpreadMode && page.pageSide() == KWPage::Right && page != m_pageManager->begin()) { // there is a page displayed left of this one. KWPage prevPage = page.previous(); if (xOffset <= prevPage.width()) // was left page instead of right :) page = prevPage; else xOffset -= prevPage.width(); } if (! page.isValid()) // below doc or right of last page return QPointF(m_contents.width(), m_contents.height()); qreal yOffset = translated.y(); if (pageNumber >= 0) yOffset -= m_pageTops[pageNumber -1]; return QPointF(xOffset, page.offsetInDocument() + yOffset); }