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

1239 lines
39 KiB

/* Windows Meta File Loader/Painter Class Implementation
*
* SPDX-FileCopyrightText: 1998 Stefan Taferner
* SPDX-FileCopyrightText: 2002 thierry lorthiois
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-ktnef.h> // WORDS_BIGENDIAN
#include <math.h>
#include <assert.h>
#include <QPainterPath>
#include <QDataStream>
#include <QBuffer>
#include <QFile>
#include <QPolygon>
#include "ktnef_debug.h"
bool qwmfDebug = false;
#include "qwmf.h"
#include "wmfstruct.h"
#include "metafuncs.h"
#define QWMF_DEBUG 0
class WmfCmd
{
public:
~WmfCmd()
{
delete next;
}
WmfCmd *next;
unsigned short funcIndex;
long numParm;
short *parm;
};
class WinObjHandle
{
public:
virtual ~WinObjHandle()
= default;
virtual void apply(QPainter &p) = 0;
};
class WinObjBrushHandle : public WinObjHandle
{
public:
void apply(QPainter &p) override;
QBrush brush;
~WinObjBrushHandle() override
= default;
};
class WinObjPenHandle : public WinObjHandle
{
public:
void apply(QPainter &p) override;
QPen pen;
~WinObjPenHandle() override
= default;
};
class WinObjPatternBrushHandle : public WinObjHandle
{
public:
void apply(QPainter &p) override;
QBrush brush;
QImage image;
~WinObjPatternBrushHandle() override
= default;
};
class WinObjFontHandle : public WinObjHandle
{
public:
void apply(QPainter &p) override;
QFont font;
int rotation;
~WinObjFontHandle() override
= default;
};
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 = nullptr;
mObjHandleTab = nullptr;
mDpi = 1000;
}
//-----------------------------------------------------------------------------
QWinMetaFile::~QWinMetaFile()
{
delete mFirstCmd;
if (mObjHandleTab) {
delete[] mObjHandleTab;
}
}
//-----------------------------------------------------------------------------
bool QWinMetaFile::load(const QString &filename)
{
QFile file(filename);
if (!file.exists()) {
qCDebug(KTNEFAPPS_LOG) << "File" << QFile::encodeName(filename) << " does not exist";
return false;
}
if (!file.open(QIODevice::ReadOnly)) {
qCDebug(KTNEFAPPS_LOG) << "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;
int filePos, idx, i;
WmfCmd *cmd, *last;
DWORD rdSize;
WORD rdFunc;
mTextAlign = 0;
mRotation = 0;
mTextColor = Qt::black;
if (mFirstCmd) {
delete mFirstCmd;
}
mFirstCmd = nullptr;
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;
const WORD 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) {
qCDebug(KTNEFAPPS_LOG) << "\n-------------------------------------------------";
qCDebug(KTNEFAPPS_LOG) << "WMF Placeable Header (" << static_cast<int>(sizeof(pheader)) << "):";
qCDebug(KTNEFAPPS_LOG) << " bbox=(" << mBBox.left() << ";" << mBBox.top() << ";" << mBBox.width()
<< "; " << mBBox.height() << ")";
qCDebug(KTNEFAPPS_LOG) << " inch=" << pheader.inch;
qCDebug(KTNEFAPPS_LOG) << " checksum=" << pheader.checksum << "("
<< (pheader.checksum == checksum ? "ok" : "wrong") << " )";
}
} 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) {
qCDebug(KTNEFAPPS_LOG) << "\n-------------------------------------------------";
qCDebug(KTNEFAPPS_LOG) << "WMF Extended Header:";
qCDebug(KTNEFAPPS_LOG) << " iType=" << eheader.iType;
qCDebug(KTNEFAPPS_LOG) << " nSize=" << eheader.nSize;
qCDebug(KTNEFAPPS_LOG) << " rclBounds=(" << eheader.rclBounds.left << ";" << eheader.rclBounds.top << ";"
<< eheader.rclBounds.right << "; " << eheader.rclBounds.bottom << ")";
qCDebug(KTNEFAPPS_LOG) << " rclFrame=(" << eheader.rclFrame.left << ";" << eheader.rclFrame.top << ";"
<< eheader.rclFrame.right << "; " << eheader.rclFrame.bottom << ")";
qCDebug(KTNEFAPPS_LOG) << " nBytes=" << eheader.nBytes;
qCDebug(KTNEFAPPS_LOG) << "\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) {
qCDebug(KTNEFAPPS_LOG) << "WMF Header:" << "mtSize=" << header.mtSize;
}
}
//----- Test header validity
mValid = ((header.mtHeaderSize == 9) && (header.mtNoParameters == 0)) || mIsEnhanced || mIsPlaceable;
if (mValid) {
//----- Read Metafile Records
last = nullptr;
rdFunc = -1;
while (!st.atEnd() && (rdFunc != 0)) {
st >> rdSize;
st >> rdFunc;
idx = findFunc(rdFunc);
rdSize -= 3;
cmd = new WmfCmd;
cmd->next = nullptr;
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) {
qCDebug(KTNEFAPPS_LOG) << "WMF : file truncated !";
return false;
}
}
//----- Test records validities
mValid = (rdFunc == 0) && (mBBox.width() != 0) && (mBBox.height() != 0);
if (!mValid) {
qCDebug(KTNEFAPPS_LOG) << "WMF : incorrect file format !";
}
} else {
qCDebug(KTNEFAPPS_LOG) << "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 != nullptr);
if (mPainter.isActive()) {
return false;
}
if (mObjHandleTab) {
delete[] mObjHandleTab;
}
mObjHandleTab = new WinObjHandle * [ MAX_OBJHANDLE ];
for (i = MAX_OBJHANDLE - 1; i >= 0; --i) {
mObjHandleTab[ i ] = nullptr;
}
mPainter.resetTransform();
mWinding = false;
mAbsoluteCoord = absolute;
mPainter.begin(aTarget);
if (QWMF_DEBUG) {
qCDebug(KTNEFAPPS_LOG) << "Bounding box :" << mBBox.left()
<< " " << mBBox.top() << " " << mBBox.right() << " " << mBBox.bottom();
}
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 == nullptr) {
str += QLatin1String("UNKNOWN ");
}
if (metaFuncTab[ idx ].method == &QWinMetaFile::noop) {
str += QLatin1String("UNIMPLEMENTED ");
}
str += QLatin1String(metaFuncTab[ idx ].name);
str += QLatin1String(" : ");
for (i = 0; i < cmd->numParm; ++i) {
param.setNum(cmd->parm[ i ]);
str += param;
str += QLatin1Char(' ');
}
qCDebug(KTNEFAPPS_LOG) << str;
}
}
/*
// TODO: cleanup this code when QPicture::setBoundingBox() is possible in KOClipart (QT31)
// because actually QPicture::boundingBox() != mBBox()
mWindowsCoord += 1;
if ( mWindowsCoord == 2 ) {
qCDebug(KTNEFAPPS_LOG) <<"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.xored(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.drawRoundedRect(parm[ 5 ], parm[ 4 ], parm[ 3 ] - parm[ 5 ], parm[ 2 ] - parm[ 4 ],
xRnd, yRnd, Qt::RelativeSize);
}
//-----------------------------------------------------------------------------
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.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.subtracted(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)
{
auto *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(&copyParm[ 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.boundingRect(QLatin1String(text)).width() + 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, QLatin1String(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, QLatin1String(text.mid(i, 1)));
}
} else {
mPainter.drawText(x, y, width, height, Qt::AlignLeft | Qt::AlignTop, QLatin1String(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
QTransform m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F);
mPainter.setWorldTransform(m, true);
}
if (parm[ 4 ] < 0) { // height < 0 => vertical flip
QTransform m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F);
mPainter.setWorldTransform(m, true);
}
mPainter.drawImage(parm[ 7 ], parm[ 6 ], bmpSrc, parm[ 3 ], parm[ 2 ], parm[ 5 ], parm[ 4 ]);
mPainter.restore();
}
} else {
qCDebug(KTNEFAPPS_LOG) << "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
QTransform m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F);
mPainter.setWorldTransform(m, true);
}
if (parm[ 6 ] < 0) { // height < 0 => vertical flip
QTransform m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F);
mPainter.setWorldTransform(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
QTransform m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F);
mPainter.setWorldTransform(m, true);
}
if (parm[ 7 ] < 0) { // height < 0 => vertical flip
QTransform m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F);
mPainter.setWorldTransform(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)
{
auto *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)
auto *handle = new WinObjPenHandle;
addHandle(handle);
qCDebug(KTNEFAPPS_LOG) << "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;
auto *handle = new WinObjBrushHandle;
addHandle(handle);
arg = parm[ 0 ];
if (arg == 2) {
arg = parm[ 3 ];
if (arg >= 0 && arg < 5) {
style = hatchedStyleTab[ arg ];
} else {
qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile::createBrushIndirect: invalid hatched brush" << arg;
style = Qt::SolidPattern;
}
} else if (arg >= 0 && arg < 9) {
style = styleTab[ arg ];
} else {
qCDebug(KTNEFAPPS_LOG) << "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;
auto *handle = new WinObjPenHandle;
addHandle(handle);
if (parm[ 0 ] >= 0 && parm[ 0 ] < 6) {
style = styleTab[ parm[ 0 ] ];
} else {
qCDebug(KTNEFAPPS_LOG) << "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() ;
qCDebug(KTNEFAPPS_LOG) <<"CreatePenIndirect:";
qCDebug(KTNEFAPPS_LOG) <<" log coord. :" << mBBox.width() <<"" << mBBox.height();
qCDebug(KTNEFAPPS_LOG) <<" log. pen :" << parm[ 1 ] <<"" << parm[ 2 ];
qCDebug(KTNEFAPPS_LOG) <<" dev. pen :" << width;
handle->pen.setWidth( width );
*/
}
//-----------------------------------------------------------------------------
void QWinMetaFile::createFontIndirect(long, short *parm)
{
auto *handle = new WinObjFontHandle;
addHandle(handle);
QString family(QLatin1String((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 :
// qCDebug(KTNEFAPPS_LOG) <<"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 {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 ] == nullptr) {
break;
}
}
if (idx < MAX_OBJHANDLE) {
mObjHandleTab[ idx ] = handle;
} else {
qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile error: handle table full !";
}
}
//-----------------------------------------------------------------------------
void QWinMetaFile::deleteHandle(int idx)
{
if (idx >= 0 && idx < MAX_OBJHANDLE && mObjHandleTab[ idx ]) {
delete mObjHandleTab[ idx ];
mObjHandleTab[ idx ] = nullptr;
}
}
//-----------------------------------------------------------------------------
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 *)(pattern.constData());
bmpHeader->bmType = 0x4D42;
bmpHeader->bmSize = sizeBmp;
if (!bmp.loadFromData((const uchar *)bmpHeader, pattern.size(), "BMP")) {
qCDebug(KTNEFAPPS_LOG) << "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;
}
}